首页 > 解决方案 > Django:用“_code”或其他后缀替换外键属性名(以及列名)“_id”后缀

问题描述

我的问题与这个问题密切相关,但又略有不同。

给定以下 Django 模型:

class Country(models.Model):
    code = models.CharField(primary_key=True)


class Person(models.Model):
    country = models.ForeignKey(Country, on_delete=models.CASCADE)

请注意,我在Countrycalled上指定了一个自定义主键字段code。该类Country不应有id字段或属性。这工作正常。

当然,我希望Person班级的外键Country能够使用该code字段。这也很好。到目前为止,一切都很好。

但是,我真的希望表中的基础列名Person完全是country_code. 不是 country_id。所以我添加了db_column字段参数:

class Person(models.Model):
    country = models.ForeignKey(
        Country, 
        on_delete=models.CASCADE,
        db_column='country_code',
    )

现在列名已正确命名country_code,而不是country_id. 到目前为止,一切都很好。


现在。在我的 python 代码中,我希望能够通过一个人以正常方式访问一个国家:

person = Person.objects.get(...)
country = person.country

但是,我希望能够通过使用country_code, not country_id的人访问国家代码 pk ,如下所示:

person = Person.objects.get(...)
country_code = person.country_code

而且我还希望能够使用参数创建Person 对象country_code,而不是country_id

person = Person(country_code='foo')

而且我还希望能够使用参数查询Person 对象country_code,而不是country_id

people = Person.objects.filter(country_code='foo')

不应有属性 person.country_id、字段或列。只有person.countryperson.country_code。是的,我希望准确调用该字段/属性/列person.country_code即使 person.country_id它具有相同的值也不行。这就是我在 Python 和 RDBMS 中想要的确切名称。


到目前为止,我还没有能够做到这一点。我尝试过将to_field参数添加到Person.country字段声明中,如下所示:

class Person(models.Model):
    country = models.ForeignKey(
        Country, 
        on_delete=models.CASCADE,
        to_field='code',
        db_column='country_code',
    )

这似乎没有达到预期的效果。

是否可以在 Django 中更改模型的外键字段属性名称(以及基础列名称)?

在我看来,这似乎不是一件奇怪的事情。也许我错了。这可能吗?这是否太难/不标准而不值得?我是在自找麻烦吗?

标签: pythondjango

解决方案


自定义外键子类

class NamedForeignKey(models.ForeignKey):
    suffix_idname = 'id'
    def __init__(self, *args, **kwargs):
        suffix_idname = kwargs.pop('suffix_idname', None)
        if suffix_idname:
            self.suffix_idname = suffix_idname
        super().__init__(*args, **kwargs)

    def get_attname(self):
        return '%s_%s' % (self.name, self.suffix_idname)

class Person(models.Model):
    country = NamedForeignKey(
        Country, 
        on_delete=models.CASCADE,
        suffix_idname='code',
    )

推荐阅读