2

我的愿望是有一个通用的位置模型,然后让需要位置的各种更高级别的模型引用它。

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

显然,我做错了什么,因为这对我来说似乎是一个非常标准的问题。我的架构设计很无聊吗?

我在愚蠢地使用内联系统吗?我不想为每个上层对象做 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 非常擅长),或者是使看起来向后的东西有意义的技巧。

4

2 回答 2

1

首先,我认为您想要 ForeignKey,而不是 OneToOneField。否则,您不妨将您的位置字段添加到 Publisher 和 Building 模型中。然后,您只需在建筑和发布者管理中获得一个下拉菜单来选择位置和一个添加新位置的链接(如果需要)。

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

于 2012-06-30T06:31:07.837 回答
1

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

wrt/你的问题:

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

ForeignKey 解决方案仍将您的 Publisher 和 Building 模型限制在单个位置(可能 - 或不是 - 您想要的),但给定的位置可能会在不同的 Publisher 和/或 Building 实例之间共享。这意味着编辑一个给定的位置将反映所有相关的实例(注意这里不需要的副作用)。

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

无论如何,围绕 Django 管理应用程序的默认行为进行设计可能不是最明智的做法。您必须首先决定什么对这个应用程序有意义(并且您可能对 Publishers 和 Buildings 有不同的需求),然后可能扩展管理员以满足您的需求。

于 2012-06-30T10:47:57.630 回答