2009-07-30 12 views
56

¿Es posible filtrar por propiedad?Filtrar por propiedad

tengo un método en mi modelo:

@property 
def myproperty(self): 
    [..] 

y ahora quiero filtrar por esta propiedad como:

MyModel.objects.filter(myproperty=[..]) 

es posible esto de alguna manera?

+0

Está en SQLAlchemy: http://docs.sqlalchemy.org/en/latest/orm/extensions/hybrid.html y puede conectar django con SQLAlchemy a través de https://pypi.python.org/pypi/aldjemy pero dudo que los dos puedan estar conectados de la manera que usted quiere que sean. – rattray

Respuesta

47

No. Los filtros de Django operan en el nivel de la base de datos, generando SQL. Para filtrar en función de las propiedades de Python, debe cargar el objeto en Python para evaluar la propiedad, y en ese punto, ya habrá realizado todo el trabajo para cargarlo.

+2

mala suerte de que esta característica no esté implementada, sería una extensión interesante para, al menos, filtrar objetos coincidentes _después_ el conjunto de resultados ha sido compilado. – schneck

+0

cómo lidiar con esto en administración? ¿Hay alguna solución? – andi

30

Podría estar malinterpretando su pregunta original, pero hay un filter incorporado en python.

filtered = filter(myproperty, MyModel.objects) 

Pero es mejor utilizar un list comprehension:

filtered = [x for x in MyModel.objects if x.myproperty()] 

o mejor aún, un generator expression:

filtered = (x for x in MyModel.objects if x.myproperty()) 
+8

Eso funciona para filtrarlo una vez que tiene un objeto de Python, pero pregunta sobre Django QuerySet.filter, que construye consultas SQL. –

+0

a la derecha, pero como se explicó anteriormente, me gustaría agregar la propiedad a mi filtro de base de datos. filtrar después de que se haya realizado la consulta es exactamente lo que quiero evitar. – schneck

+0

genial, ¡muchas gracias! –

3

POR FAVOR alguien me corrija, pero supongo que he encontrado una solución, al menos para mi propio caso.

Quiero trabajar en todos aquellos elementos cuyas propiedades son exactamente iguales a ... lo que sea.

Pero tengo varios modelos, y esta rutina debería funcionar para todos los modelos. Y lo hace:

def selectByProperties(modelType, specify): 
    clause = "SELECT * from %s" % modelType._meta.db_table 

    if len(specify) > 0: 
     clause += " WHERE " 
     for field, eqvalue in specify.items(): 
      clause += "%s = '%s' AND " % (field, eqvalue) 
     clause = clause [:-5] # remove last AND 

    print clause 
    return modelType.objects.raw(clause) 

Con esta subrutina universales, puedo seleccionar todos aquellos elementos que exactamente igual a mi diccionario de (PropertyName, propertyvalue) combinaciones 'escribe'.

El primer parámetro toma un (models.Model),

el segundo un diccionario como: { "propiedad1": "77", "propiedad2": "12"}

Y crea una instrucción SQL como

SELECT * from appname_modelname WHERE property1 = '77' AND property2 = '12' 

y devuelve un QuerySet en esos elementos.

Esta es una función de prueba:

from myApp.models import myModel 

def testSelectByProperties(): 

    specify = {"property1" : "77" , "property2" : "12"} 
    subset = selectByProperties(myModel, specify) 

    nameField = "property0" 
    ## checking if that is what I expected: 
    for i in subset: 
     print i.__dict__[nameField], 
     for j in specify.keys(): 
      print i.__dict__[j], 
     print 

Y? ¿Qué piensas?

7

Parece que using F() with annotations será mi solución para esto.

No va a filtrar por @property, ya que F habla con el databse antes de que los objetos entren en python. Pero aún lo pongo aquí como una respuesta ya que mi razón para querer filtrar por propiedad realmente quería filtrar los objetos por el resultado de la aritmética simple en dos campos diferentes.

así, algo a lo largo de las líneas de:

companies = Company.objects\ 
    .annotate(chairs_needed=F('num_employees') - F('num_chairs'))\ 
    .filter(chairs_needed__lt=4) 

en lugar de definir la propiedad de ser:

@property 
def chairs_needed(self): 
    return self.num_employees - self.num_chairs 

luego hacer una lista por comprensión en todos los objetos.

3

Riffing fuera @ TheGrimmScientist Ha solución sugerida, puede hacer que estos "Propiedades de SQL" definiéndolos en el Manager o el QuerySet y reutilización/cadena/componerlos:

con un gerente:

class CompanyManager(models.Manager): 
    def with_chairs_needed(self): 
     return self.annotate(chairs_needed=F('num_employees') - F('num_chairs')) 

class Company(models.Model): 
    # ... 
    objects = CompanyManager() 

Company.objects.with_chairs_needed().filter(chairs_needed__lt=4) 

Con un QuerySet:

class CompanyQuerySet(models.QuerySet): 
    def many_employees(self, n=50): 
     return self.filter(num_employees__gte=n) 

    def needs_fewer_chairs_than(self, n=5): 
     return self.with_chairs_needed().filter(chairs_needed__lt=n) 

    def with_chairs_needed(self): 
     return self.annotate(chairs_needed=F('num_employees') - F('num_chairs')) 

class Company(models.Model): 
    # ... 
    objects = CompanyQuerySet.as_manager() 

Company.objects.needs_fewer_chairs_than(4).many_employees() 

Ver https://docs.djangoproject.com/en/1.9/topics/db/managers/ por más. Tenga en cuenta que estoy saliendo de la documentación y no he probado lo anterior.

Cuestiones relacionadas