2009-08-18 3 views
2

У меня есть Django установки модели следующим образом:Джанго запрос для большого количества отношений

модель А имеет отношение один-ко-многим для модели B

каждая запись в имеет от 3000 до 15000 записи в B

Каков наилучший способ построения запроса, который будет извлекать самую новую запись (наибольшую pk) в B, которая соответствует записи в A для каждой записи в A? Является ли это чем-то, что я должен использовать SQL вместо Django ORM?

ответ

2

Создание вспомогательной функции для безопасного извлечения «главного» элемент из любого набора запросов. Я использую все это в своих приложениях Django.

def top_or_none(queryset): 
    """Safely pulls off the top element in a queryset""" 
    # Extracts a single element collection w/ top item 
    result = queryset[0:1] 

    # Return that element or None if there weren't any matches 
    return result[0] if result else None 

Это использует немного трюк ж/slice operator to add a limit clause onto your SQL.

Теперь эта функция используется везде, где вам нужно получить «верхний» элемент набора запросов. В этом случае, вы хотите, чтобы получить верхний элемент B для данного А где Б сортируются по убыванию рк, как таковой:

latest = top_or_none(B.objects.filter(a=my_a).order_by('-pk')) 

Там также недавно добавлена ​​функция «Max» в Django Aggregation, которые могли бы помочь вам получить max pk, но в этом случае мне не нравится это решение, так как оно добавляет сложности.

P.S. Мне не очень нравится полагаться на поле «pk» для этого типа запросов, поскольку некоторые РСУБД не гарантируют, что последовательные pks совпадают с порядком логического создания. Если у меня есть таблица, которую я знаю, мне нужно будет запросить таким образом, у меня обычно есть свой собственный столбец datetime для создания, который я могу использовать для заказа вместо pk.

Редактировать на основе комментариев:

Если вы не хотите использовать QuerySet [0], вы можете изменить функцию 'top_or_none' Thusly:

def top_or_none(queryset): 
    """Safely pulls off the top element in a queryset""" 
    try: 
     return queryset[0] 
    except IndexError: 
     return None 

Я не предлагал этого изначально потому что у меня создалось впечатление, что queryset [0] отбросит весь набор результатов, а затем возьмет 0-й элемент. По-видимому, Django добавляет «LIMIT 1» в этом сценарии, поэтому это безопасная альтернатива моей нарезной версии.

Edit 2

Конечно, вы также можете воспользоваться соответствующим менеджером Джанго построить здесь и построить QuerySet через «А» объект, в зависимости от ваших предпочтений:

latest = top_or_none(my_a.b_set.order_by('-pk')) 
+0

Что такого разница между result = queryset [0: 1] и result = queryset [0]? – hekevintran

+0

queryset [0: 1] будет возвращать пустой список, если нет соответствующих элементов, тогда как queryset [0] будет вызывать IndexError. –

+0

Спасибо за ответ! – hekevintran

0

Я не думаю, что Django ORM может это сделать (но я был приятно удивлен раньше ...). Если есть достаточное количество записей A (или если вы подкачки), я бы просто добавил метод к модели A, которая вернет эту «новейшую» запись B. Если вы хотите получить много записей A, каждый из которых имеет собственный новый B, я бы перешел на SQL.

Remeber, что независимо от того, какой маршрут вы принимаете, вы будете нуждаться в подходящий композитный индекс по таблице B, возможно добавление order_by=('a_fk','-id') к Meta подкласса

Смежные вопросы