如何在Django中创建共享模型?

时间:2019-06-05 11:45:49

标签: django

我有几个需要一组通用字段的模型。它基本上是一组各种不同类型的联系人:

# 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)

我想与PhoneAddressContact模型共享OrganizationUserProfile模型。我的第一个尝试是在ForeignKeyContactOrganization的每一个上添加UserProfile,但经过更多研究,我认为这是倒退的,所以我将ForeignKey移开了。到AddressPhone中去,但随后发现ForeignKey可以属于一个并且只能属于一个模型。除了在多种不同的联系人类型之间共享此数据结构外,我还希望能够向联系人添加多个地址或电话号码。例如,联系人可以具有家庭住址,工作地址,手机号码和工作号码。所以我基本上有两个问题:

1)以这种方式共享模型是否合理?
2)我将如何建立模型?

2 个答案:

答案 0 :(得分:2)

如果我理解正确;您希望您的ContactOrganizationUserProfile模型都具有AddressPhone的字段。而且,它们每个可以具有一个以上的地址/电话。

这合理吗?对我来说听起来如此。

您怎么去建立模型? Generic Relations浮现在我的脑海。

现在只考虑ContactAddress。您的第二次尝试是正确的:我们有一个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字段) 到ContactOrganizationUserProfile

但这意味着不同的联系人/组织/用户可以共享地址。 尽管这看起来很诱人,但这种解决方案的问题是,如果有人 编辑联系人的地址,它们还会更改系统中所有其余对象的地址!


避免上述问题的另一种可能的解决方案 并且不需要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 从ContactOrganizationEntity的点。)