我有几个需要一组通用字段的模型。它基本上是一组各种不同类型的联系人:
# models I would like to share
class Address(models.Model):
label = models.CharField(_('label'), max_length=50, blank=True)
street1 = models.CharField(_('street1'), max_length=125, blank=True)
street2 = models.CharField(_('street2'), max_length=125, blank=True)
city = models.CharField(_('city'), max_length=50, blank=True)
state = models.CharField(_('state'), max_length=2, blank=True)
zip_code = models.CharField(_('zip_code'), max_length=10, blank=True)
class Phone(models.Model):
label = models.CharField(_('label'), max_length=50, blank=True)
phone = models.CharField(_('phone'), max_length=50, blank=True)
# these are the models that I would like to have addresses and phone numbers
class Contact(models.Model):
name = models.CharField(_('name'), max_length=50, blank=False)
class Organization(models.Model):
name = models.CharField(_('name'), max_length=255, blank=False)
email = models.CharField(_('email'), max_length=100, blank=True)
website = models.CharField(_('website'), max_length=255, blank=True)
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
photo = models.CharField(_('photo'), max_length=255, blank=True)
我想与Phone
,Address
和Contact
模型共享Organization
和UserProfile
模型。我的第一个尝试是在ForeignKey
,Contact
和Organization
的每一个上添加UserProfile
,但经过更多研究,我认为这是倒退的,所以我将ForeignKey
移开了。到Address
和Phone
中去,但随后发现ForeignKey
可以属于一个并且只能属于一个模型。除了在多种不同的联系人类型之间共享此数据结构外,我还希望能够向联系人添加多个地址或电话号码。例如,联系人可以具有家庭住址,工作地址,手机号码和工作号码。所以我基本上有两个问题:
1)以这种方式共享模型是否合理?
2)我将如何建立模型?
答案 0 :(得分:2)
如果我理解正确;您希望您的Contact
,Organization
和UserProfile
模型都具有Address
和Phone
的字段。而且,它们每个可以具有一个以上的地址/电话。
这合理吗?对我来说听起来如此。
您怎么去建立模型? Generic Relations浮现在我的脑海。
现在只考虑Contact
和Address
。您的第二次尝试是正确的:我们有一个Many-to-one关系(一个联系人,很多地址),因此我们需要在Contact
模型中将Address
作为ForeignKey字段,如下所示:>
class Address(models.Model):
<other_fields>
contact = models.ForeignKey('Contact', on_delete=models.CASCADE)
这使您可以为每个联系人分配多个地址。
继续前进,您基本上希望Address
模型具有多个外键:联系人,组织和UserProfile。实现此目的的一种方法是使用通用关系。这利用了Django内置的“ contenttypes”框架,并允许您创建指向多个模型的GenericForeignKey
字段。我鼓励您阅读链接的文档,因为通用关系并不是那么简单。最后,您将得到类似的内容:
class Address(models.Model):
label = models.CharField(max_length=50, blank=True)
street_1 = models.CharField(max_length=125, blank=True)
street_2 = models.CharField(max_length=125, blank=True)
etc...
models_with_address = models.Q(app_label='app_name', model='contact') | \
models.Q(app_label='app_name', model='organization') | \
models.Q(app_label='app_name', model='userprofile')
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, limit_choices_to=models_with_address)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey()
这样,您可以为models_with_address
查询中指定的每个模型创建多个地址。为了能够查询给定联系人/组织等的地址,您需要一个reverse generic relation。进行此设置需要将行address = GenericRelation(Address)
添加到各个模型中。
为进一步概括,您可以创建一个ContactableModel
(或其他)类:
class ContactableModel(models.Model):
address = GenericRelation('Address')
phone = GenericRelation('Phone')
任何具有地址和电话号码(联系人,组织等)的模型都可以继承此模型,因此您不必重复包括这两个字段。您还可以提高models_with_address
的限制,以使我们得到类似limit_choices_to=<subclasses_of_ContactableModel>
的内容。
希望这会有所帮助!
答案 1 :(得分:1)
一个简单的解决方案是添加ManyToManyField(Address|Phone)
(即两个M2M字段)
到Contact
,Organization
和UserProfile
。
但这意味着不同的联系人/组织/用户可以共享地址。 尽管这看起来很诱人,但这种解决方案的问题是,如果有人 编辑联系人的地址,它们还会更改系统中所有其余对象的地址!
避免上述问题的另一种可能的解决方案 并且不需要M2M字段或将使用通用关系 multi-table inheritance:
class Address(models.Model):
entity = models.ForeignKey(Entity)
...
class Entity(models.Model):
pass
class Contact(Entity):
...
class Organization(Entity):
...
(在内部,Django创建了隐式OneToOneField
-s
从Contact
和Organization
到Entity
的点。)