2010-02-04 16 views
11

Come si crea personalizzato field lookups in Django?Creazione di ricerche campo personalizzate in Django

Quando il filtraggio querysets, Django fornisce una serie di ricerche che è possibile utilizzare: __contains, __iexact, __in, e così via. Voglio essere in grado di fornire una nuova ricerca per il mio manager, così per esempio, qualcuno potrebbe dire:

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

e tornare tutti i Person oggetti con un'età tra 20 e 30. Ho bisogno di sottoclasse il QuerySet o Manager classe per fare questo? Come sarebbe implementato?

+3

Queste risposte sono utili per il suo esempio (che può o non può essere stato qualcosa che ha appena sballottato per esprimere il suo punto) .. ma mi piacerebbe che qualcuno rispondesse alla domanda che è stata effettivamente posta. – royal

+1

@royal Penso che la mia risposta lo copra- Ho avuto questo in una biblioteca su cui sto lavorando. –

risposta

1

A partire da Django 1.7, esiste un modo semplice per implementarlo. Il vostro esempio è in realtà molto simile a quella da 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) 

Durante la registrazione, si può semplicemente utilizzare Field.register_lookup(AbsoluteValueLessThan) invece.

+0

in quale file dovrei farlo? – user1735921

+0

@ user1735921 non ricordo più. Che ne dici di models.py? – d33tah

+0

hey man puoi per favore controllare la mia domanda a: http://stackoverflow.com/questions/41186414/how-to-put-in-string-in-django, nessuno è in grado di risolverlo – user1735921

6

Piuttosto che creare una ricerca di campo, le migliori prassi sarebbe quella di creare un metodo di manager, che potrebbe sembrare un po 'come questo:

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

quindi l'utilizzo sarebbe in questo modo:

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

Un modo più flessibile per eseguire questa operazione è scrivere un QuerySet personalizzato e un gestore personalizzato. Lavorando dal codice di ozan:

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

Ciò consente di concatenare la query personalizzata. Così entrambe queste domande sarebbero validi:

Person.objects.in_age_range(20,30) 

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

È eccellente! Sto ancora sperando che ci sia un po 'di API per aggancio alla sintassi del campo di ricerca, per trarre vantaggio dalla logica del trasloco. Continuerò a scavare. Nel frattempo, penso che questo mi faccia un passo avanti nel capire come i pezzi si legano. Grazie! – jcdyer

+0

Puoi farlo un po 'più facilmente/in modo pulito con [django-qmethod] (https://github.com/zacharyvoase/django-qmethod). Sembra che non sia stato aggiornato da un po ', ma lo uso da anni e non ho mai avuto problemi. – Dave

6

In primo luogo, lasciatemi dire che non v'è alcun macchinario Django in luogo che è destinata a facilitare pubblicamente ciò che vuoi.

(Edit - in realtà dal Django 1.7 c'è: https://docs.djangoproject.com/en/1.7/howto/custom-lookups/)

Detto questo, se si davvero vuole per raggiungere questo obiettivo, sottoclasse QuerySet e l'override del metodo _filter_or_exclude(). Quindi crea un gestore personalizzato che restituisce solo la tua QuerySet personalizzata (o la patch di scimmia di Django QuerySet, bleah). Lo facciamo in neo4django per riutilizzare il più possibile il codice queryset di Django ORM mentre si costruiscono oggetti specifici di Neo4j.

Provate qualcosa (approssimativamente) come questo, adattato dalla risposta di Zach. Ho lasciato effettivo trattamento per il parsing campo di ricerca errore come un esercizio per il lettore :)

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

Considerazioni finali - in modo chiaro, se si vuole le ricerche sul campo catena di misura, questo sta per diventare piuttosto peloso. Inoltre, normalmente lo scriverei un po 'più funzionalmente e userei itertools per le prestazioni, ma ho pensato che fosse più chiaro lasciarlo fuori. Divertiti!

+1

Grazie amico, questo è stato utile. – nisc

+0

Contento che qualcuno lo stia usando! –

+1

Hai appena trovato la tua risposta! Grazie mille per questo. Questo è quello che stavo cercando. – jcdyer

Problemi correlati