django:一对多的关系范围

时间:2016-03-30 12:38:26

标签: django

我有以下两个模型实现一对多关系。

class Collection(models.Model):
    pass
class Item(models.Model):
    collection = models.ForeignKey('Collection', on_delete=models.CASCADE, db_index=True)
    date = models.DateTimeField(auto_now_add=True, db_index=True)
    class Meta:
        ordering = ['-date']

对于任何Collection实例,我想知道最新的和第一个相应的项目。

我想使用 单个SQL请求 models.Collection.objects.get(id=1)获取此项目。我相信该请求可以使用JOIN以及后续的最小和最大聚合。

要使用这个第一个/最新的对象操作,我想使用如下界面:

c = models.Collection.objects.get(id=1)
c.get_latest() # Item instance obtained in the previous line and stored in object c
c.get_first() # Item instance also

问题是如何将以下三个表达式合并为单个SQL请求:

Collection.objects.get(id=1)
Item.objects.filter(collection_id=1).latest('date')
Item.objects.filter(collection_id=1).earliest('date')

我怎么能让Django为我做这件事?

2 个答案:

答案 0 :(得分:0)

我能想到将所有三个查询合并为一个的唯一方法是创建一个大的原始查询,首先获取您的Collection,然后是最新的Item。然后,您将没有一个Collection和两个Item对象,而是一个Collection对象,其中包含您可以读出的其他属性。这不是我想用的方式。

但是,您可以通过仅针对项目使用原始查询将其减少为两个查询。保持你的

c = models.Collection.objects.get(id=1)

用于检索集合对象。然后对项目使用以下原始查询:

i = Item.objects.raw('''
    SELECT * FROM <package_name>_item WHERE collection_id = %(collection_id)s ORDER BY date ASC LIMIT 1
    UNION
    SELECT * FROM <package_name>_item WHERE collection_id = %(collection_id)s ORDER BY date DESC LIMIT 1
''', {
    'collection_id': c.id,
})

(将<package_name>替换为带模型的django应用程序的名称)

我认为这并不是很有意义,因为查询基本上对表进行了两次操作。因此,您可以将查询拆分为两个,而不会造成很多性能损失。

有一种方法可以在单个SQL查询中解决这个问题(再次包装在raw()语句中):

SELECT * FROM <package_name>_item
WHERE collection_id = %(collection_id)s
AND (
    date = (
        SELECT max(date) FROM <package_name>_item
        WHERE collection_id = %(collection_id)s
    ) OR 
    date = (
        SELECT min(date) FROM <package_name>_item
        WHERE collection_id = %(collection_id)s
    )
)

这有一个问题:如果存在多个Item,其中包含最小或最大日期,则查询将全部返回。

答案 1 :(得分:0)

我认为你不能合并这三个查询,但你可以试试这个。

items = Item.objects.filter(collection__id=1) # you have all items of collection whit ID = 1

记住django查询是懒惰的,所以在第一行你不要点击DataBase。

获得最后一次使用:

first_item = items.first()
last_item = items.last()

在这里你没有点击DataBase,你有first_item,last_item和集合对象。

# firt_item
# last_item
# first_item.collection