Django Admin内联和架构设计

时间:2012-06-30 04:53:57

标签: django django-models django-admin

我的愿望是拥有一个共同的位置模型,然后让需要位置的各种更高级别的模型参考它。

我想在管理员中显示我的用户,其中包含多部分表单(内联),允许他们输入发布者和构建的更高级别信息,以及每个部分的位置信息。内联系统似乎不想以这种方式工作。

显然,我做的事情非常错误,因为这对我来说似乎是一个非常标准的问题。我的架构设计是否被设置了?

我是否愚蠢地使用内联系统?我不想为每个上层对象做Location的子类,因为我想以不同的方式操作位置,而不管高级对象拥有它们(邮件列表或地理查找)

models.py:
...
class Location(models.Model):
    """
    A geographical address
    """
# Standard Location stuff   
    address_line1 = models.CharField("Address line 1", max_length = 45, null=True, blank=True)
    ...

class Publisher(models.Model):
    """
    Contains Publisher information for publishers of yearbooks.  Replaces Institution from 1.x
    """
    name =  models.CharField(max_length=100, null=False, help_text="Name of publisher, e.g. University of Kansas")
    groups = models.ManyToManyField(Group, help_text="Select groups that this publisher owns.  Usually just one, but multiple groups are possible.")
    is_active = models.BooleanField(help_text="Check this box to enable this publisher.")
    location = models.OneToOneField(Location)
    ...

class Building(models.Model):
    """
    Contains Building Information
    """
    name =  models.CharField(max_length=100, null=False, help_text="Name of building, e.g. Physical Sciences")
    is_active = models.BooleanField(help_text="Check this box to enable this building.")
    location = models.OneToOneField(Location)
    ...

admin.py:
...
class LocationInline(generic.GenericStackedInline):
    model = Location
    max_num = 1
    extra = 1

class PublisherAdmin(admin.ModelAdmin):
    model = Publisher
    inlines = [ LocationInline,
    ]

class BuildingAdmin(admin.ModelAdmin):
    model = Building
    inlines = [ LocationInline,
    ]

admin.site.register(Publisher, PublisherAdmin)
admin.site.register(Building, BuildingAdmin)

我可以通过将其添加到Location模型来强制内联加载和显示:

# Support reverse lookup for admin  
    object_id    = models.PositiveIntegerField()
    content_type = models.ForeignKey(ContentType)
    of           = generic.GenericForeignKey('content_type', 'object_id' )

但是当我这样做时,即使我确实得到了一个内联对象,并且可以对其进行编辑,这种关系似乎也向后倾斜,其中Location将id存储到创建它的对象中。

欢迎提供任何帮助,无论是推荐的架构更改,以使一切都运行得非常好(因为Django非常擅长),或者使得向后看似的东西变得有意义。

2 个答案:

答案 0 :(得分:1)

首先,我认为你想要ForeignKey,而不是OneToOneField。否则,您可以将您的位置字段添加到发布服务器和构建模型。然后,您只需在下拉列表中选择位置,然后在建筑物和发布商管理员中根据需要添加新链接。

如果您确实希望每个建筑物/发布者拥有一个位置实例,则无法将其编辑为内联,因为内联模型需要将ForeignKey指向父模型,除非您添加通用外部键。这不是'向后' - 当你希望一个对象能够将自己附加到任何其他对象时,它是一个有效的选项,无论其类型如何。

答案 1 :(得分:1)

对于域模型而言,没有“一种正确的方法”可以做到这一点,这取决于您的特定应用程序的要求。

wrt /你的问题:

OneToOne字段将您的模型限制为每个模型实例的一个位置,(正如Greg所提到的)概念上与仅直接在模型中粘贴Location的字段非常不同。 wrt / DRY / factorisation / reuse等,你也可以使用模型继承来完成这个工作,有一个抽象(或者最终具体,如果你的应用程序有意义)位置模型。

ForeignKey解决方案仍然将您的发布商和构建模型限制为单个位置(可能 - 或不是 - 您想要的),但可以在不同的发布服务器和/或构建实例之间共享给定位置。这意味着编辑一个给定位置将反映所有相关实例(请注意此处不必要的副作用)。

在Location模型中使用GenericForeignKey意味着给定的位置实例属于一个且只有一个相关对象。与上述解决方案一样没有令人担忧的副作用,但您可能具有相同值的重复位置(即一个用于建筑物,一个用于发布者),并且您将无法查找特定位置的所有相关对象(或至少不那么容易)。此外,这不会阻止发布服务器或构建实例具有多个位置,这可能再次可能没有问题。 wrt /位置实例“存储它们所属对象的id”,嗯,这正是这个设计选择的含义:一个位置“属于”某个其他对象,句点。

在任何情况下,围绕Django管理应用程序的默认行为进行设计可能不是最明智的做法。您必须先确定应用程序的含义(并且您可能对发布商和建筑物有不同的需求),然后可能会扩展管理员以满足您的需求。