Создание пользовательских полевых поисков в Django - PullRequest
11 голосов
/ 04 февраля 2010

Как создать пользовательский поиск полей в Django?

При фильтрации наборов запросов django предоставляет набор запросов, которые вы можете использовать: __contains, __iexact, __in и т. Д. Я хочу, чтобы у моего менеджера был новый поиск, поэтому, например, кто-то может сказать:

twentysomethings = Person.objects.filter(age__within5=25)

и получить обратно все Person объекты в возрасте от 20 до 30 лет. Нужно ли для этого делать подкласс класса QuerySet или Manager? Как это будет реализовано?

Ответы [ 4 ]

12 голосов
/ 06 февраля 2010

Более гибкий способ сделать это - написать собственный QuerySet, а также собственный менеджер. Работа с кодом Озана:

class PersonQuerySet(models.query.QuerySet):
    def in_age_range(self, min, max):
        return self.filter(age__gte=min, age__lt=max)

class PersonManager(models.Manager):
    def get_query_set(self):
         return PersonQuerySet(self.model)

    def __getattr__(self, name):
        return getattr(self.get_query_set(), name)

class Person(models.Model):
    age = #...

    objects = PersonManager()

Это позволяет вам связать ваш собственный запрос. Таким образом, оба эти запроса будут действительны:

Person.objects.in_age_range(20,30)

Person.objects.exclude(somefield = some_value).in_age_range(20, 30)
6 голосов
/ 27 ноября 2011

Во-первых, позвольте мне сказать, что не существует механизма Django, который должен был бы публично содействовать тому, что вы хотите.

(Изменить - на самом деле, поскольку Django 1.7 существует: https://docs.djangoproject.com/en/1.7/howto/custom-lookups/)

Тем не менее, если вы действительно хотите выполнить это, подкласс QuerySet и переопределите метод _filter_or_exclude(). Затем создайте собственный менеджер, который будет возвращать только ваш пользовательский QuerySet (или обезьяна-патч Django QuerySet, чёрт). Мы делаем это в neo4django , чтобы повторно использовать как можно большую часть кода набора запросов Django ORM при создании объектов Query, специфичных для Neo4j.

Попробуйте что-то (примерно) подобное, адаптированное из ответа Зака. Я оставил фактическую обработку ошибок для парсинга поиска полей как упражнение для читателя:)

class PersonQuerySet(models.query.QuerySet):
    def _filter_or_exclude(self, negate, *args, **kwargs):
        cust_lookups = filter(lambda s: s[0].endswith('__within5'), kwargs.items())
        for lookup in cust_lookups:
            kwargs.pop(lookup[0])
            lookup_prefix = lookup[0].rsplit('__',1)[0]
            kwargs.update({lookup_prefix + '__gte':lookup[1]-5,
                           lookup_prefix + '__lt':lookup[1]+5})
        return super(PersonQuerySet, self)._filter_or_exclude(negate, *args, **kwargs)

class PersonManager(models.Manager):
    def get_query_set(self):
         return PersonQuerySet(self.model)

class Person(models.Model):
    age = #...

    objects = PersonManager()

Заключительные замечания - ясно, что если вы хотите связать поиск пользовательских полей, это будет довольно сложно. Кроме того, я обычно писал бы это немного более функционально и использовал бы itertools для производительности, но думал, что было бы более ясно оставить это. Веселись!

6 голосов
/ 05 февраля 2010

Вместо создания поиска по полю лучше всего создать метод менеджера, который может выглядеть примерно так:

class PersonManger(models.Manager):
    def in_age_range(self, min, max):
        return self.filter(age__gte=min, age__lt=max)

class Person(models.Model):
    age = #...

    objects = PersonManager()

тогда использование будет таким:

twentysomethings = Person.objects.in_age_range(20, 30)
1 голос
/ 16 апреля 2015

Начиная с Django 1.7, есть простой способ реализовать это. Ваш пример на самом деле очень похож на пример из документации :

from django.db.models import Lookup

class AbsoluteValueLessThan(Lookup):
    lookup_name = 'lt'

    def as_sql(self, qn, connection):
        lhs, lhs_params = qn.compile(self.lhs.lhs)
        rhs, rhs_params = self.process_rhs(qn, connection)
        params = lhs_params + rhs_params + lhs_params + rhs_params
        return '%s < %s AND %s > -%s' % (lhs, rhs, lhs, rhs), params

AbsoluteValue.register_lookup(AbsoluteValueLessThan)

При регистрации вы можете просто использовать Field.register_lookup(AbsoluteValueLessThan).

...