2013-05-25 4 views
1

Я пытаюсь оптимизировать некоторый код Django, и у меня есть два похожих подхода, которые выполняют разные. Вот некоторые примеры моделей:django filtering vs python filtering with prefetched objects

class A(models.Model): 
    name = models.CharField(max_length=100) 

class B(models.Model): 
    name = models.CharField(max_length=100) 
    a = models.ForeignKey(A) 
    c = models.ForeignKey(C) 

class C(models.Model): 
    name = models.CharField(max_length=100) 

Для каждого A объекта, я хотел бы, чтобы перебрать подмножество его входящих B «с, отфильтрованный по их стоимости c. Простой:

for a in A.objects.all() : 
    for b in a.B_set.filter(c__name='some_val') : 
     print a.name, b.name, c.name 

Проблема с этим состоит в том, что существует новый поиск в базе данных для каждого a значения итерации.

Похоже, что решение состоит в предварительной выборке значений c, которые будут подаваться в фильтр.

qs_A = A.objects.all().prefetch_related('B_set__c') 

Теперь рассмотрим следующие два фильтра подходов:

# Django filter 
for a in qs_A : 
    for b in a.B_set.filter(c__name='some_val') : 
     print a.name, b.name, n.name 

# Python filter 
for a in qs_A : 
    for b in filter(lambda b: b.c.name == 'some_val', a.B_set.all()): 
     print a.name, b.name, c.name 

С данными я использую, то Джанго фильтр составляет 48 больше запросов SQL, чем питон фильтра (на 12-элементной qs_A результата задавать). Это заставляет меня поверить, что фильтр django не использует предварительно загруженные таблицы.

Может ли кто-нибудь объяснить, что произошло?

Возможно, можно применить фильтр во время предварительной выборки?

ответ

1

Предварительная выборка и фильтрация не имеют прямого соединения ... Фильтрация всегда происходит внутри вашей базы данных, тогда как основная цель - prefetch_related - получить данные для связанных объектов при их выводе или что-то подобное.

Меньшие SQL-запросы в основном лучше, но если вы хотите оптимизировать свой вариант использования, вы должны выполнить некоторый бенчмаркинг и профилирование и не полагаться на некоторые общие утверждения!

Вы могли бы сделать свой пример более эффективным, если вы не будете работать с A в первую очередь, но с B вместо:

qs = B.objects.select_related('a', 'c').filter(c__name='some val') 
# maybe you need some filtering for a as well: 
# qs = qs.filter(a__x=....) 
for b in qs: 
    print b.a.name, b.name, b.c.name 

Может быть, вам нужно сделать некоторые перегруппировки/упорядочивание после фильтрации (в python), но если вы уже можете выполнить все действия фильтрации за один шаг, это будет более эффективно ... В противном случае, возможно, посмотрите на необработанные запросы sql ...