2010-02-04 19 views
11

Jak utworzyć niestandardowe field lookups w Django?Tworzenie niestandardowych Wyszukiwanie pól w Django

Podczas filtrowania zapytań, django udostępnia zestaw wyszukiwań, których można używać: __contains, __iexact, __in i tak dalej. Chcę być w stanie dostarczyć nowe wyszukiwanie dla mojego menedżera, więc na przykład, ktoś mógłby powiedzieć:

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

i odzyskać wszystkie Person obiektów z wieku pomiędzy 20 a 30. Czy muszę podklasy QuerySet lub Manager klasy, aby to zrobić? W jaki sposób zostanie on wdrożony?

+3

Te odpowiedzi są użyteczne dla jego przykładu (który może, ale nie musi, był czymś, co po prostu wyrzucił, by wyrazić swoją opinię). Ale chciałbym, żeby ktoś odpowiedział na pytanie, które faktycznie zostało zadane. – royal

+1

@royal Wydaje mi się, że moja odpowiedź obejmuje to - wymyśliłem to w bibliotece, nad którą pracuję. –

Odpowiedz

1

Od Django 1.7 istnieje prosty sposób na jego wdrożenie. Twój przykład jest rzeczywiście bardzo podobny do jednego z the documentation:

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) 

Podczas rejestracji, można po prostu użyć Field.register_lookup(AbsoluteValueLessThan) zamiast.

+0

w jakim pliku powinienem to zrobić? – user1735921

+0

@ user1735921 już nie pamięta. A co powiesz na models.py? – d33tah

+0

Hej, proszę, sprawdź moje pytanie pod adresem: http://stackoverflow.com/questions/41186414/how-to-put-in-string-in-django, nikt nie jest w stanie go rozwiązać – user1735921

6

Zamiast tworzenia odnośnika pola, najlepsze praktyki byłoby utworzyć metodę menedżera, który może wyglądać trochę tak:

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() 

następnie wykorzystanie byłoby tak:

twentysomethings = Person.objects.in_age_range(20, 30) 
12

Bardziej elastycznym sposobem na to jest napisanie niestandardowego zestawu kwerendy, a także niestandardowego menedżera. Praca z kodu ozonu:

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() 

Umożliwia to powiązanie niestandardowego zapytania. Tak więc oba te pytania byłyby ważne:

Person.objects.in_age_range(20,30) 

Person.objects.exclude(somefield = some_value).in_age_range(20, 30) 
+0

To świetnie! Wciąż mam nadzieję, że istnieje API do przechwytywania składni pola wyszukiwania, aby skorzystać z logiki przemian rlationship. Będę dalej kopać. W międzyczasie myślę, że dzięki temu jestem o krok bliżej do zrozumienia, w jaki sposób elementy wiążą się ze sobą. Dzięki! – jcdyer

+0

Możesz to zrobić trochę łatwiej/czysto za pomocą [django-qmethod] (https://github.com/zacharyvoase/django-qmethod). Wygląda na to, że nie było od jakiegoś czasu aktualizowane, ale używam go od lat i nigdy nie miałem żadnych problemów. – Dave

6

Po pierwsze, chciałbym powiedzieć, że nie ma maszyny Django w miejscu, które jest przeznaczone do ułatwienia publicznie, co chcesz.

(Edit - faktycznie od Django 1.7 znajduje się: https://docs.djangoproject.com/en/1.7/howto/custom-lookups/)

Powiedział, że jeśli naprawdę chcą tego dokonać, podklasa QuerySet i zastąpić metodę _filter_or_exclude(). Następnie utwórz niestandardowego menedżera, który zwraca tylko niestandardową QuerySet (lub łatkę z łatą małpy Django, QuerySet, fuj). Robimy to w neo4django, aby ponownie wykorzystać jak najwięcej kodu zapytania Django ORM podczas budowania obiektów specyficznych dla Neo4j Query.

Spróbuj czegoś (w przybliżeniu) w ten sposób, zaadaptowanego z odpowiedzi Zacha. Zostawiłam rzeczywisty błąd obsługi dla parsowania przeglądowej pole jako ćwiczenie dla czytelnika :)

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() 

Uwagi końcowe - oczywiście, jeśli chcesz łańcuch niestandardowych wyszukiwań polowych, to będzie uzyskać bardzo owłosione. Poza tym normalnie pisałbym to nieco bardziej funkcjonalnie i używał itertools do wydajności, ale myślałem, że lepiej jest go pominąć. Baw się dobrze!

+1

Dzięki, stary, to było pomocne. – nisc

+0

Cieszę się, że ktoś go używa! –

+1

Po prostu znalazłem odpowiedź! Dziękuję za to. Tego właśnie szukałem. – jcdyer

Powiązane problemy