Django 管理后台:将一对一关系作为内联?

82 投票
5 回答
50084 浏览
提问于 2025-04-15 16:04

我正在为一个叫做Satchmo的应用程序搭建管理后台。Satchmo使用一对一的关系来扩展基本的Product模型,我想在同一页面上编辑所有内容。

请问可以把一对一的关系放在内联编辑中吗?如果不行,最好的办法是什么,能在我的管理页面上添加几个字段,最后把它们保存到一对一的关系中?

举个例子:

class Product(models.Model):
    name = models.CharField(max_length=100)
    ...

class MyProduct(models.Model):
    product = models.OneToOne(Product)
    ...

我尝试在我的管理后台这样做,但不成功,似乎它期待的是外键:

class ProductInline(admin.StackedInline):
    model = Product
    fields = ('name',)

class MyProductAdmin(admin.ModelAdmin):
    inlines = (AlbumProductInline,)

admin.site.register(MyProduct, MyProductAdmin)

这导致了一个错误:<class 'satchmo.product.models.Product'>没有指向<class 'my_app.models.MyProduct'>的外键

难道唯一的解决办法就是使用自定义表单吗?

编辑:我刚刚尝试了以下代码直接添加字段……同样不成功:

class AlbumAdmin(admin.ModelAdmin):
    fields = ('product__name',)

5 个回答

5

关于上一个问题,针对多个子类型,最好的解决方案是什么呢?比如说,有一个类叫做产品(Product),它有两个子类型,一个是书(Book),另一个是光盘(CD)。按照这里的方式,如果你想编辑一个产品,你就得同时修改通用的产品信息,还要修改书的子类型信息和光盘的子类型信息。所以即使你只想添加一本书,你也得处理光盘的字段。如果你再添加一个子类型,比如DVD,那你就会有三个子类型的字段组,但实际上你只想要一个子类型的字段组,在这个例子中就是书籍。

8

也许可以考虑使用继承,而不是一对一的关系。

class Product(models.Model):
    name = models.CharField(max_length=100)
    ...

class MyProduct(Product):
    .....

或者使用代理类。

class ProductProxy(Product)
    class Meta:
        proxy = True

在admin.py文件中。

class MyProductInlines(admin.StackedInline):
    model = MyProduct

class MyProductAdmin(admin.ModelAdmin):
    inlines = [MyProductInlines]

    def queryset(self, request):
        qs = super(MyProductAdmin, self).queryset(request)
        qs = qs.exclude(relatedNameForYourProduct__isnone=True)
        return qs

admin.site.register(ProductProxy, MyProductAdmin)

在这种情况下,你的产品会以嵌套的方式显示。

89

在一对一关系中使用内联是完全可以的。不过,定义这个关系的字段必须放在内联模型里,而不是父模型里——这和外键的情况是一样的。把它换过来就能正常工作了。

评论后补充: 你说父模型已经在管理员那里注册了,那就先取消注册,然后再重新注册。

from original.satchmo.admin import ProductAdmin

class MyProductInline(admin.StackedInline):
    model = MyProduct

class ExtendedProductAdmin(ProductAdmin):
    inlines = ProductAdmin.inlines + (MyProductInline,)

admin.site.unregister(Product)
admin.site.register(Product, ExtendedProductAdmin)

更新 2020(Django 3.1.1)

这个方法仍然有效,但在新的Django版本中,有些类型发生了变化,因为在ExtendedProductAdmin中的inlines现在应该作为列表添加,而不是元组,像这样:

class ExtendedProductAdmin(ProductAdmin):
    inlines = ProductAdmin.inlines + [MyProductInline]

否则你会遇到这个错误:

    inlines = ProductAdmin.inlines + (MyProductInline,)
TypeError: can only concatenate list (not "tuple") to list

撰写回答