【问题标题】:Django Model relationship refactor help?Django 模型关系重构有帮助吗?
【发布时间】:2010-08-18 20:18:37
【问题描述】:

我有点 django / db brainfart。

换句话说,我试图想出一种更好的方法来表示我的项目中的 Address 对象。我有 5 或 6 个不同的模型,它们与地址(账单/运输/等)有一个或多个关联,我需要允许创建/修改/删除这些地址。我的第一个想法是为此使用管理员,因为它看起来很自然。

但是,我似乎不知道如何告诉管理员将可见的地址集限制为相关的特定模型(帐户/合作伙伴/发票)。我发现了一种非常讨厌的、不可行的、令人难以置信的维护方式,我将在下面展示。

我怎样才能有效地做到这一点(最好在管理员中)?我愿意建立对我来说更自然的 m2m 关系,并使用自定义视图/表单,但我想看看我在走那条路线之前是否错过了一些管理技巧。如果我走那条路,我想我需要使用 GenericRelation,这样我就不会得到大量的查找表(每个不同的实体一个)。

编辑:相同的地址可能用于不同的模型,并且对于特定模型的不同实例,但是如果一个地址被重复使用,我们必须跟踪谁在使用什么保持模型和/或实例之间的独立性。或者换句话说,在 m2m 关系中,我们可以通过中间表跟踪谁在使用什么。如果我们不使用查找表,那么我们需要始终复制 Address 实例,以便两个对象都有自己的副本。 (如果有编辑,我们需要确保我们不会更改其他任何人的预先存在的关系。换句话说,编辑实际上是在 m2m 案例中创建和重新分配。)

这是一个几乎可以作为一个空项目工作的示例,它显示了我希望在添加/编辑/删除期间如何隔离地址,但它也显示了解决方案是多么可怕。

models.py

​​>
from django.db import models

class Account(models.Model):
    name = models.CharField(max_length=200,blank=True)
    #...
    def __unicode__(self):
        return u"%s - (%s)" %(self.name, self.address_set.all())

class Partner(models.Model):
    name = models.CharField(max_length=200,blank=True) 
    discount = models.DecimalField(max_digits= 3, decimal_places=1, default=0)
    #...
    def __unicode__(self):
        return u"%s - (%s)" %(self.name, self.address_set.all())

class Invoice(models.Model):
    invoice_number = models.IntegerField(default=1)
    #...
    def __unicode__(self):
        return u"%s - (%s)" %(self.invoice_number, self.address_set.all())

class Address(models.Model):
    street = models.CharField(max_length=200,blank=True)
    zip = models.CharField(max_length=10, verbose_name='Zip Code')
    account = models.ForeignKey(Account, blank=True, null=True)
    partner = models.ForeignKey(Partner, blank=True, null=True)
    invoice = models.ForeignKey(Invoice, blank=True, null=True)
    type = models.CharField(max_length=25, choices=(('B','Billing'),('S','Shipping')))

    class Meta:
        unique_together = (('type', 'account' ),
                           ('type', 'partner' ),
                           ('type', 'invoice' ),)

    def __unicode__(self):
        return "(%s) - %s %s" %(self.get_type_display(), self.street, self.zip)

admin.py

​​>
from django.contrib import admin
from relationships.rels.models import Partner, Account, Address, Invoice

class AcctAddrInline(admin.TabularInline):
    model = Address
    extra = 1
    max_num =3
    exclude = ('partner', 'invoice')

class PartAddrInline(admin.TabularInline):
    model = Address
    extra = 1
    max_num =3
    exclude = ('account', 'invoice')

class InvAddrInline(admin.TabularInline):
    model = Address
    extra = 1
    max_num =2
    exclude = ('account', 'partner')        

class AccountAdmin(admin.ModelAdmin):
    inlines = [AcctAddrInline,]

class PartnerAdmin(admin.ModelAdmin):
    inlines = [PartAddrInline,]

class InvoiceAdmin(admin.ModelAdmin):
    inlines = [InvAddrInline,]

admin.site.register(Invoice, InvoiceAdmin)         
admin.site.register(Partner, PartnerAdmin)
admin.site.register(Account, AccountAdmin)
admin.site.register(Address)

【问题讨论】:

  • 您是否要为不同类型的模型重用相同的地址实例(即合作伙伴和帐户的相同地址)?相同模型的不同实例(即两个不同发票的相同地址)呢?
  • 更新了主要问题陈述。

标签: django django-models django-admin


【解决方案1】:

编辑:相同的地址可能用于不同的模型,对于特定模型的不同实例,但是如果一个地址被重复使用,我们必须跟踪谁在使用如何保持模型和/或实例之间的独立性

您似乎想对地址使用COW 模式,但我认为它与数据库完整性的整体理念不符。

如果您只想将帐户地址与发票地址分开,我建议您使用Multi-table model inheritance。这样,您将拥有多组地址,同时能够一次浏览所有地址。

这是一个例子。

models.py

​​>
from django.db import models

class Account(models.Model):
    name = models.CharField(max_length=200, blank=True)

    def __unicode__(self):
        return u"%s - (%s)" % (self.name, self.address_set.all())

class Partner(models.Model):
    name = models.CharField(max_length=200, blank=True) 
    discount = models.DecimalField(max_digits= 3, decimal_places=1, default=0)

    def __unicode__(self):
        return u"%s - (%s)" % (self.name, self.address_set.all())

class Invoice(models.Model):
    invoice_number = models.IntegerField(default=1)

    def __unicode__(self):
        return u"%s - (%s)" % (self.invoice_number, self.address_set.all())

class Address(models.Model):
    street = models.CharField(max_length=200, blank=True)
    zip = models.CharField(max_length=10, verbose_name='Zip Code')

    def __unicode__(self):
        return "%s %s" % (self.street, self.zip)

class AccountAddress(Address):
    account = models.ForeignKey(Account, related_name='address_set')

class InvoiceAddress(Address):
    invoice = models.ForeignKey(Invoice, related_name='address_set')

class PartnerAddress(Address):
    partner = models.ForeignKey(Partner, related_name='address_set')

admin.py

​​>
from django.contrib import admin
# Wildcard import used for brevity
from relationships.rels.models *

class AccountAddressInline(admin.TabularInline):
    model = AccountAddress
    extra = 1
    max_num = 3

class PartnerAddressInline(admin.TabularInline):
    model = PartnerAddress
    extra = 1
    max_num = 3

class InvoiceAddressInline(admin.TabularInline):
    model = InvoiceAddress
    extra = 1
    max_num = 3

class AccountAdmin(admin.ModelAdmin):
    inlines = [AccountAddressInline,]

class PartnerAdmin(admin.ModelAdmin):
    inlines = [PartnerAddressInline,]

class InvoiceAdmin(admin.ModelAdmin):
    inlines = [InvoiceAddressInline,]

admin.site.register(Account, AccountAdmin)
admin.site.register(Partner, PartnerAdmin)
admin.site.register(Invoice, InvoiceAdmin)

admin.site.register(AccountAddress)
admin.site.register(InvoiceAddress)
admin.site.register(PartnerAddress)

# Uncomment if you want to browse all addresses available at once
# admin.site.register(PartnerAddress)

注意related_name='address_set' hack。我不知道为什么,但当使用继承模型的外键时,这是内联编辑工作的唯一方法。似乎这是 Django 中的一个错误,类似于(但相反的用例)#11120#11121

【讨论】:

  • 我昨晚朝这个方向去了,但无法让它工作。我错过了related_name hack。现在它可以按预期工作了,谢谢!
【解决方案2】:

我个人会将外键放入帐户、合作伙伴和发票上的地址模型中,而不是让地址知道地址是什么。那,MAY,可以解决你的问题。

【讨论】:

  • 那是他们应该去的地方,我同意。但如果我这样做,那么所有地址都可用于所有实体,这正是我想要避免的。
  • @JT 你看过在你的 ForeignKey 字段上使用 limit_choices_to 吗?
  • 我根据您的建议查看了 limit_choices_to,但我想不出在类定义中使用它来引用特定实例的方法。这似乎适用于日期或其他不依赖模型的事情。 stackoverflow.com/questions/160009/… 是另一个效果不佳的用例 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-13
  • 1970-01-01
  • 2011-04-26
  • 1970-01-01
  • 1970-01-01
  • 2012-08-27
相关资源
最近更新 更多