Django QuerySet 顺序排列字符串评估

1 投票
1 回答
3665 浏览
提问于 2025-04-16 16:42

我想根据查询结果中的对象被当作字符串时的表现来排序我的查询集。

我的模型大概是这样的:

class System(models.Model):
  operating_system = models.CharField(...)
  language = models.CharField(...)
  locale = models.CharField(...)

  def __unicode__(self):
    def __clean(orig, new):
      if orig is None or orig == "":
        if new is None or new == "":
          return ""
        else:
          return str(new)
      else:
        if new is None or new == "":
          return str(orig)
        else:
          return str(orig) + " " + str(new)
    name = None
    for attr in System._meta.fields:
      if attr.name != "id":
        name = __clean(name, getattr(self, attr.name))
    for m2mfield in System._meta.many_to_many:
      for object in getattr(self, m2mfield.name).all():
        name = __clean(name, object)  

    if name == "":
      return "Undefined"
    return name

然后,我想能够进行类似这样的查询:

System.objects.filter(...).order_by('__unicode__')

我在想有没有办法做到这一点,而不需要自定义管理器。

谢谢!

1 个回答

1

__unicode__这个方法里,最后你会得到一个代表系统对象的字符串。与其每次需要的时候都去计算,不如一次性计算好,然后把结果保存到模型里。

class System(models.Model):
    operating_system = models.CharField(...)
    language = models.CharField(...)
    locale= models.CharField(...)
    name = models.CharField(editable=False, ...)

    def save(self, *args, **kwargs):
        self.name = self._calculate_name()
        super(System, self).save(*args, **kwargs)

    def __unicode__(self):
        return self.name

    def _calculate_name(self):
        # all that string manipulation and relationship stuff

这样你就可以很方便地按这个名字排序了。

System.objects.filter(...).order_by('name')

不过这种方法有一些注意事项,具体还得看系统的使用情况。而且,不用担心占用空间,这只是我的个人看法!


关于注意事项的进一步说明

因为这个字段是“非规范化”的,所以它会遇到其他非规范化关系数据所面临的相同问题。非规范化可能会引入更新异常(也就是说,如果某个字段或关系是name依赖的,而这个字段通过其他方式改变了,但没有通过System模型的save()方法改变name,那么name就不会更新)。这也可能会稍微减慢写入速度(在这种情况下可能影响很小),还可能增加空间需求(我认为这也不是问题),还有很多其他的事情,谷歌肯定能告诉你更多。

你需要注意的是,每当.name应该更新的时候,就要去更新它。因此,要仔细考虑在什么情况下你的“清理”代码会产生不同的结果。例如,如果你有一个操作系统表,可以在不触碰系统表的情况下改变操作系统的描述,那么你就要意识到,保存到操作系统表不会更新.name,这时需要重新计算。可以通过一些机制来帮助解决这个问题,比如信号和重写更多的save()方法。你也可以根据需要批量更新它们。

这真的很依赖于你的具体使用场景,而这里并没有完全展示出来。如果你能更完整地描述你的使用场景,StackOverflow上有很多人可以帮助你找到最佳解决方案。

撰写回答