Django 空字段回退

11 投票
2 回答
3689 浏览
提问于 2025-04-15 13:55

我有一个模型用来保存用户地址。这个模型必须包含 first_name(名字)和 last_name(姓氏)这两个字段,因为我们需要把地址设置给收件人(比如他的公司等等)。我想实现的目标是:

  • 如果地址中的 first_namelast_name 字段有值,就直接返回这个字段的值。
  • 如果地址中的 first_namelast_name 字段是空的,就从一个外键中获取相应的字段数据,这个外键指向一个合适的 django.auth.models.User
  • 我希望这个字段能像普通的 Django 字段一样,可以在字段查找中使用。
  • 我不想创建一个方法,因为这是重构,而 Address.first_namelast_name 在应用的多个地方都有用(比如在模型表单中等等),所以我希望这个过程尽可能顺利,否则我就得在很多地方进行修改。

2 个回答

2

虽然这并不能完全满足提问者的所有需求,但在某些情况下,如果需要在数据库查询中使用备用值,可以考虑使用Django的 Coalesce() 类(文档)。

在这种情况下,最简单的解决方案可能是使用查询集注释(参考@daniel-roseman的回答中的例子):

queryset = MyModel.objects.annotate(first_name=Coalesce('_first_name', 'user__first_name'))

这样,从查询集中得到的每个项目都会有一个 first_name 属性,它的值是 _first_name,如果这个值是 null,就会使用 user.first_name 作为备用。

也就是说,假设你的模型中有一个 user 关系。

这可以在自定义管理器中实现,也可以与 property 方法结合使用。

关于类似情况的详细示例(覆盖而不是备用)可以参考这个回答

13

这里有两个选择。第一个是创建一个方法来动态查找,但使用 property 装饰器,这样其他代码仍然可以直接访问属性。

class MyModel(models.Model):
    _first_name = models.CharField(max_length=100, db_column='first_name')

    @property
    def first_name(self):
        return self._first_name or self.user.first_name

    @first_name.setter
    def first_name(self, value):
       self._first_name = value

这样做会始终引用最新的 first_name 值,即使相关的用户发生了变化。你可以像访问属性一样获取或设置这个属性:myinstance.first_name = 'daniel'

另一个选择是重写模型的 save() 方法,这样在你保存的时候就会进行查找:

def save(self, *args, **kwargs):
    if not self.first_name:
        self.first_name = self.user.first_name
    # now call the default save() method
    super(MyModel, self).save(*args, **kwargs)

这样你就不需要更改数据库,但它只在保存时刷新——所以如果相关的用户对象发生了变化,而这个对象没有变化,它将引用旧的用户值。

撰写回答