在两个第三方模型之间添加Django M2M关系

时间:2018-05-07 21:03:59

标签: django django-models many-to-many

我需要在两个模型之间创建多对多关系。在Django中,您通常会在两个模型之一中添加ManyToManyField。但就我而言:

  • 这两个模型来自第三方图书馆,非常简单UserCar
  • 两者都是非抽象的,我想避免,例如,多表继承的复杂性只是为了添加一个M2M字段(实际上不需要在任何一个表中添加额外的列)。
  • 我正在假设其他人会使用它(如果不是字面意思)第三方库,所以我真的想要提供标准接口,尽管存在限制。

显然,我可以创建一个“手动”M2M表:

class UserCar(models.Model):
    user = models.ForeignKey(User)
    car = models.ForeignKey(Car)

    class Meta:
        unique_together = ('user', 'car')

...但是Django会将ForeignKey个字段视为多对一字段,因此标准的“魔术”访问器将无法使用:

user.cars.add(car)
car in user.cars
User.objects.filter(cars=car)

是否有一种简单的方法可以在关系的两边注入通常的M2M魔法? UserCar基本上是through=表。我可以创建一个虚拟字段并调用一两个方法(例如contribute_to_classcontribute_to_related_class)来完成此操作吗?我试过ForeignKeyManyToMany,但我对正常的场级处理不够熟悉甚至猜测关键方法(更不用说排序和延迟处理了)。

1 个答案:

答案 0 :(得分:0)

在评论中,@ AlexandTatarinov建议在创建add_to_class表后立即使用ManyToManyFieldCar添加through。我仍然愿意接受更好的答案,但它肯定是一个答案(目前是最好的答案)。假设我们使用如下代码:

# guard against repeated import
if not hasattr(Car, 'users'):
    field = models.ManyToManyField(User, through=UserCar, related_name='cars')
    field.contribute_to_class(Car, 'users')

这种方法的效果(包括缺点)是:

  • 魔法访问者被添加到关系的两侧
  • 迁移(添加M2M字段)放置在Car的应用中。此迁移将不会提交到我的软件包的存储库,并可能导致一些奇怪的问题。例如,
    • 如果开发人员使用我的包作为依赖项,则在开发人员调用makemigrations时将(重新)创建迁移。如果稍后升级Car包(并包括新的迁移),则可能会导致类似"两个叶节点"在开发机器上。
  • 当我删除Car迁移以模拟生产场景案例时,一个简单的测试 工作(例如,在创建关系中的两个对象user.places和{{1 }}正常工作)
  • 由于M2M表被定义为place.users表,因此through等方法无需开箱即用(即使M2M表与自动创建的表完全相同)通过该领域)。

由于place.users.add()在手动创建的place.users.add()模型上,因此引发了auto_created=False的问题。如果我在through=中设置了auto_created=Car,那么魔术方法可以正常运行,但不会创建迁移。要利用默认迁移通过行为还原,我可以使用以下内容:

UserCar.Meta

编辑:在之前的迭代中,我使用的是@receiver(request_started) def set_auto_created(**kwargs): UserCar._meta.auto_created = Car 。这导致auto_created=True中的FLUSH出现问题。在深入研究M2MField代码之后,需要将TransactionTestCase指向持有auto_created的模型。我认为它与如何在ManyToManyField中管理自动创建的表格有关。

EDIT2:我还尝试将信号附加到TransactionTestCase。这适用于测试(因为它们总是迁移),但在生产中不起作用。 post_migrate似乎适用于两者。

通过此增强功能,在应用中为request_started创建的额外迁移是此策略的唯一主要问题。