DRY в запросах django, обратных запросах и предикатах - PullRequest
0 голосов
/ 07 апреля 2011

Я разочарован тем, что в Django мне часто приходится писать методы на собственном менеджере:

class EntryManager(Manager):
    def filter_beatle(self, beatle):
        return self.filter(headline__contains=beatle)

... и повторите почти тот же метод в другом Менеджере для обратного запроса:

class BlogManager(Manager):
    def filter_beatle(self, beatle):
        return self.filter(entry__headline__contains=beatle)

... и предикат на входе:

def headline_contains(self, beatle):
    return self.headline.find(beatle) != -1

[Обратите внимание, что предикат Entry будет работать с объектами Entry, которые еще даже не были сохранены.]

Это похоже на нарушение СУХОГО. Есть ли способ выразить это один раз и использовать его во всех трех местах?

Я хотел бы написать что-то вроде:

q = Q(headline__contains="Lennon")
lennon_entries = Entry.objects.filter(q)
lennon_blogs = Blog.objects.filter(q.reverse(Entry))
is_lennon = entry.would_filter(q)

... где 'headline__contains = "Lennon"' выражает ровно один раз, что значит быть "записью о" Lennon "", и это можно использовать для построения обратных запросов и предиката.

Ответы [ 4 ]

1 голос
/ 07 апреля 2011

Лучшее место для этого - пользовательский менеджер. Согласно рекомендациям django, класс менеджера является лучшим местом для кода, который затрагивает более одного объекта класса.

class EntryManager(models.Manager):
    def filter_lennons(self):
        return self.get_query_set().filter(headline__contains='Lennon')

class Entry(models.Model):
    headline = models.CharField(max_length=100)

    objects = EntryManager()

lennons = Entry.objects.filter_lennons()
0 голосов
/ 02 сентября 2011

Мое наиболее распространенное использование предиката, кажется, в утверждениях.Часто что-то вроде:

class Thing(Model):
    class QuerySet(query.QuerySet):
        def need_to_be_whacked():
            # ... code ...

    def needs_to_be_whacked(self):
        return Thing.objects.need_to_be_whacked().filter(id=self.id).exists()

    def whack(self):
        assert self.needs_to_be_whacked()

for thing in Thing.objects.need_to_be_whacked():
    thing.whack()

Я хочу убедиться, что никакой другой код не вызывает whack () в состоянии, когда его не нужно взламывать.Это стоит попадания в базу данных, но работает.

0 голосов
/ 13 апреля 2011

Для случая "обратного фильтра" вы можете использовать подзапрос:

Blog.objects.filter(entries__in=Entry.objects.filter_beatle("Lennon"))

Повторное использование или создание предикатов невозможно (в общем случае), поскольку существуют предикаты, которые не могут быть выражены в виде запросов, и запросы, которые не могут быть выражены в виде предикатов без доступа к базе данных.

0 голосов
/ 07 апреля 2011

Вы должны никогда редко должны делать следующее:

if entry.headline.find('Lennon') >= 0:

, потому что фильтр должен позаботиться об ограничении набора результатов интересующими вас экземплярами.

Если вы собираетесь использовать один и тот же фильтр несколько раз, вы можете создать пользовательский менеджер или простой метод класса.

class Entry(models.Model):
    ...
    # this really should be on a custom manager, but this was quicker to demonstrate
    @classmethod
    def find_headlines(cls, text):
        return cls.objects.filter(headline__contains=text)

entries = Entry.find_headlines('Lennon')

Но на самом деле СУХОСТЬуже содержится в API Queryset.Как часто вы действительно будете усердно кодировать строку «Lennon» в запросе?Обычно параметр поиска передается в представление из GET или POST.Совершенно СУХОЙ.

Итак, в чем же проблема?Помимо изучения API набора запросов, вам когда-нибудь приходилось жестко кодировать значения поиска в нескольких запросах, например, в вашем вопросе?

...