如何测试Django QuerySets是否按PK升序排列

10 投票
2 回答
2060 浏览
提问于 2025-04-18 04:48
class Foo(models.Model):
    name = models.CharField(max_length=10)

    class Meta(object):
        ordering = ('pk', )

我想测试一下这个排序是否按照我预期的方式工作。

def test_respect_ordering(self):
    Foo.objects.create(name="bar", pk=2)
    Foo.objects.create(name="baz", pk=1)
    results = Foo.objects.all()
    self.assertEqual("baz", results[0].name)
    self.assertEqual("bar", results[1].name)

虽然这个排序确实如我所愿,但我的测试无论Meta类或其中定义的排序属性是什么,结果都是通过的。有没有什么方法可以测试这段代码是否真的重要呢?

我为什么想测试这个?我的测试是在SQLite上运行的,但生产环境是用mysql。希望有一天,我们能使用更好的关系数据库管理系统(RDBMS),也许在这些数据库中,结果不会总是按主键返回。

Django文档指出,排序并不是自动进行的。

2 个回答

1

这是一个很老的问题。不过对于未来看到这个问题的人,我建议的方法虽然简单,但实际上可以测试Django模型中查询集的排序。

models.py

class Foo(models.Model):
    name = models.CharField(max_length=10)

    class Meta(object):
        ordering = ('name')

test_models.py

  • 这里的测试是用pytest写的,但这个逻辑也可以用unittest模块来实现。
import pyest
from myapp.models import Foo

@pytest.fixture
def foo_1():
    return Foo.objects.create(name="ZZZZ")

@pytest.fixture
def foo_2():
    return Foo.objects.create(name="AAAA")

@pytest.mark.django_db
def test_foo_ordering(foo_1, foo_2):
    foos = Foo.objects.all()
    # this checks that the name attribute second item in the queryset
    # should be greater than first item name attribute in the queryset.
    assert foos[0].name < foos[1].name
  

这个思路可以扩展到测试Django模型中用于排序的其他属性。

1

如果你在你的 Meta 类里不写 ordering 属性,生成的 SQL 查询就不会有 ORDER BY 这个部分(在 1.4 版本中是这样的)。这就意味着你不能指望结果的行会按照某种特定的顺序排列。

没有特定顺序的 SQL 查询结果通常看起来会有点道理。这是因为查询计划很可能会使用索引来加快查询速度,而索引在没有特定顺序的查询中会对行的顺序产生很大影响。通过 Django 模型生成的表,默认情况下只会在主键上有索引,除非你另有说明。所以一般来说,'无序' 的结果顺序会和按主键排序的顺序非常相似。

不过,这里完全没有保证,这种顺序是不能被依赖的。查询计划在很大程度上取决于数据库引擎,甚至对于同一个引擎上非常相似的查询,结果的顺序也可能会有很大变化。

如果你想要特定的顺序,最好明确指定你想要的顺序。这是确保结果顺序的唯一可靠方法。

撰写回答