首页 > 解决方案 > Django - 使用属性作为外键

问题描述

我的应用程序的数据库已填充并与外部数据源保持同步。我有一个抽象模型,我的 Django 2.2 应用程序的所有模型都从该模型派生,定义如下:

class CommonModel(models.Model):
    # Auto-generated by Django, but included in this example for clarity.
  # id = models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')
    ORIGIN_SOURCEA = '1'
    ORIGIN_SOURCEB = '2'
    ORIGIN_CHOICES = [
        (ORIGIN_SOURCEA, 'Source A'),
        (ORIGIN_SOURCEB, 'Source B'),
    ]
    object_origin = models.IntegerField(choices=ORIGIN_CHOICES)
    object_id = models.IntegerField()

class A(CommonModel):
    some_stuff = models.CharField()

class B(CommonModel):
    other_stuff = models.IntegerField()
    to_a_fk = models.ForeignKey("myapp.A", on_delete=models.CASCADE)

class C(CommonModel):
    more_stuff = models.CharField()
    b_m2m = models.ManyToManyField("myapp.B")

object_id字段无法设置为唯一,因为我在应用程序中使用的每个数据源都可能有一个带有object_id = 1. 因此需要通过字段追踪对象的来源object_origin

不幸的是,Django 的 ORM 不支持多列外键。

问题

在将自动生成的主键保留在数据库 ( id) 中的同时,我想让我的外键和多对多关系发生在object_idobject_origin字段而不是主键id上。

我试过的

我想过做这样的事情:

class CommonModel(models.Model):
    # Auto-generated by Django, but included in this example for clarity.
  # id = models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')
    ORIGIN_SOURCEA = '1'
    ORIGIN_SOURCEB = '2'
    ORIGIN_CHOICES = [
        (ORIGIN_SOURCEA, 'Source A'),
        (ORIGIN_SOURCEB, 'Source B'),
    ]
    object_origin = models.IntegerField(choices=ORIGIN_CHOICES)
    object_id = models.IntegerField()

    def _get_composed_object_origin_id(self):
        return f"{self.object_origin}:{self.object_id}"
    composed_object_origin_id = property(_get_composed_object_origin_id)

class A(CommonModel):
    some_stuff = models.CharField()

class B(CommonModel):
    other_stuff = models.IntegerField()
    to_a_fk = models.ForeignKey("myapp.A", to_field="composed_object_origin_id", on_delete=models.CASCADE)

但是 Django 抱怨它:

myapp.B.to_a_fk: (fields.E312) The to_field 'composed_object_origin_id' doesn't exist on the related model 'myapp.A'.

听起来很合法,Django 将给定的字段除外to_field作为数据库字段。但是没有必要向我添加一个新字段,CommonModel因为composed_object_type_id它是使用两个不可为空的字段构建的......

标签: pythondjangodatabase-design

解决方案


您在另一个答案的评论中提到 object_id 不是唯一的,但它与 object_type 结合是唯一的,那么您可以unique_together在元类中使用 a 吗?IE

class CommonModel(models.Model):
    object_type = models.IntegerField()
    object_id = models.IntegerField()

    class Meta:
        unique_together = (
            ("object_type", "object_id"),
        )

推荐阅读