2010-02-04 21 views
11

¿Cómo se puede crear field lookups personalizado en Django?Crear búsquedas de campo personalizadas en Django

Al filtrar querysets, django proporciona un conjunto de búsquedas que puede usar: __contains, __iexact, __in, y así sucesivamente. Quiero ser capaz de proporcionar una nueva búsqueda de mi manager, así que por ejemplo, alguien podría decir:

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

y recuperar todos los objetos Person con una edad entre 20 y 30. ¿Es necesario que la subclase QuerySet o Manager clase para hacer esto? ¿Cómo se implementaría?

+3

Estas respuestas son útiles para su ejemplo (que puede o no haber sido algo que simplemente arrojó para exponer su punto) ... pero me encantaría que alguien respondiera la pregunta que realmente se hizo. – royal

+1

@royal Creo que mi respuesta lo cubre: he tenido esto en una biblioteca en la que estoy trabajando. –

Respuesta

1

A partir de Django 1.7, hay una manera simple de implementarlo. Su ejemplo es en realidad muy similar a la de 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) 

Mientras se registra, sólo puede utilizar Field.register_lookup(AbsoluteValueLessThan) lugar.

+0

en qué archivo debo hacerlo? – user1735921

+0

@ user1735921 no recuerdo más. ¿Qué tal en el models.py? – d33tah

+0

hola hombre puede por favor ver mi pregunta en: http://stackoverflow.com/questions/41186414/how-to-put-in-string-in-django, nadie es capaz de resolverlo – user1735921

6

En lugar de crear un campo de búsqueda, la mejor práctica sería la creación de un método gerente, que puede parecer un poco como esto:

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

continuación, el uso sería así:

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

Una forma más flexible de hacerlo es escribir un QuerySet personalizado así como un administrador personalizado. Trabajando desde el código de 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() 

Esto le permite encadenar su consulta personalizada. Así pues, estas dos consultas serían válidas:

Person.objects.in_age_range(20,30) 

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

¡Eso es excelente! Todavía tengo la esperanza de que haya alguna API para conectar con la sintaxis del campo de búsqueda, para aprovechar la lógica de cruce transversal. Seguiré cavando Mientras tanto, creo que esto me lleva un paso más cerca de comprender cómo se unen las piezas. ¡Gracias! – jcdyer

+0

Puedes hacerlo un poco más fácil/limpiamente con [django-qmethod] (https://github.com/zacharyvoase/django-qmethod). Parece que no se ha actualizado en un tiempo, pero lo he usado durante años y nunca tuve ningún problema. – Dave

6

En primer lugar, permítanme decir que no existe un mecanismo de Django en el lugar que está destinado a facilitar públicamente lo desea.

(Editar - en realidad desde Django 1.7 existe: https://docs.djangoproject.com/en/1.7/howto/custom-lookups/)

Dicho esto, si realmente quiere lograr esto, subclase QuerySet y reemplazar el método _filter_or_exclude(). A continuación, cree un administrador personalizado que solo devuelva su QuerySet personalizado (o mono-parche Django QuerySet, yuck). Hacemos esto en neo4django para reutilizar la mayor cantidad de código de interrogación ORM de Django posible mientras construimos objetos específicos de Neo4j Query.

Pruebe algo (más o menos) como este, adaptado de la respuesta de Zach. He dejado para el manejo del análisis de campo de búsqueda de error real como ejercicio para el lector :)

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

Observaciones finales - con claridad, si quieres las búsquedas de campos personalizados cadena, esto va a ser bastante peludo. Además, normalmente escribo esto un poco más funcionalmente y uso itertools para el rendimiento, pero pensé que era más claro dejarlo fuera. ¡Que te diviertas!

+1

Gracias amigo, esto fue útil. – nisc

+0

¡Me alegra que alguien lo esté usando! –

+1

¡Acabo de encontrar su respuesta! Muchas gracias por esto. Eso es lo que estaba buscando. – jcdyer

Cuestiones relacionadas