【发布时间】:2014-07-15 09:15:30
【问题描述】:
我有 2 个表,关系是一对多的,“Book”(id,name,author)->“BookStatus”(id,status,date)。我想在查询集中获取所有书籍的最后状态(根据字段“日期”)。我怎样才能做到这一点?
在那之后,我如何通过我的模型“Book”显示该字段(状态),例如 Book.last_status。
提前致谢
【问题讨论】:
标签: django django-models
我有 2 个表,关系是一对多的,“Book”(id,name,author)->“BookStatus”(id,status,date)。我想在查询集中获取所有书籍的最后状态(根据字段“日期”)。我怎样才能做到这一点?
在那之后,我如何通过我的模型“Book”显示该字段(状态),例如 Book.last_status。
提前致谢
【问题讨论】:
标签: django django-models
不幸的是,通过 ORM 实现这一点并不容易。原因是为此所需的 SQL 异常复杂。首先您需要获取最新状态日期,然后加入与该状态匹配的状态。
如果您只需要访问 Python 端的最新状态,那么您可以使用 book.statuses.latest()(为每个模型生成一个查询),或者可能是模型上的一个属性:
@property
def latest_status(self):
latest_status = None
for status in self.statuses.all():
if latest_status is None or latest_status.date < status.date:
latest_status = status
return latest_Status
现在您可以在 Python 端使用 latest_status 来获取图书的最新状态。如果在取书的时候加上prefetch_related,访问latest_status不会产生查询。
如果您在查询时需要访问最新状态,事情变得更加复杂。我经常使用一种解决方案,我在数据库中创建最新项目的视图,然后通过 ORM 使用这些视图。所以,在数据库中是这样的:
CREATE VIEW latest_book_status_view AS (
SELECT null AS id,
book_status.book_id,
book_status.id AS status_id
FROM book_status
WHERE book_status.date = (SELECT max(inner_status.date)
FROM book_status inner_status
WHERE inner_status.book_id = book_status.book_id)
);
在models.py中:
class LatestBookStatus(models.Model):
# Note the null id column above. It will be used as fake primary key
# for this model.
book = models.OneToOneField(Book, null=True, on_delete=models.DO_NOTHING, related_name='latest_status')
status = models.OneToOneField(BookStatus, null=True, on_delete=models.DO_NOTHING)
class Meta:
db_table = "latest_book_status_view"
managed = False
现在您应该能够在过滤状态时发出查询:
Book.objects.filter(latest_status__status__date__gte=today())...
您可以通过以下方式获取最新状态:
qs = Book.objects.select_related('latest_status__status')
# access it
qs[0].latest_status.status.date
不幸的是,这种设置很复杂,并且存在一些问题。例如,在测试时,您将需要某种方式来生成测试数据库中的视图。 Django 1.7 迁移和 RunSQL 操作是执行此操作的一种方法。
简而言之:如果您需要访问 Python 端的最新状态,请使用属性方法。如果您需要访问 SQL 端的最新状态,请为更复杂的解决方案做好准备。上面给出了一种可能的解决方案。
【讨论】:
请务必设置好related_name 并为BookStatus 设置正确的get_latest_by。
books = Book.objects.prefetch_related('statuses')
books[0].statuses.latest()
【讨论】: