python - query - Filtrar por propiedad
django shell (6)
Nop. 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.
¿Es posible 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 esto de alguna manera posible?
POR FAVOR, alguien me corrija, pero creo 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 universal, puedo seleccionar todos los elementos que son exactamente iguales a mi diccionario de combinaciones ''especificar'' (nombre de propiedad, valor de propiedad).
El primer parámetro toma a (models.Model),
el segundo es un diccionario como: {"propiedad1": "77", "propiedad2": "12"}
Y crea una declaració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?
Parece que usar F () con anotaciones será mi solución a esto.
No va a filtrar por @property
, ya que F
comunica 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.
Entonces, algo como:
companies = Company.objects/
.annotate(chairs_needed=F(''num_employees'') - F(''num_chairs''))/
.filter(chairs_needed__lt=4)
en lugar de definir la propiedad para ser:
@property
def chairs_needed(self):
return self.num_employees - self.num_chairs
luego haciendo una lista de comprensión en todos los objetos.
Puede que esté malinterpretando su pregunta original, pero hay un filter incorporado en Python.
filtered = filter(myproperty, MyModel.objects)
Pero es mejor usar una lista de comprensión :
filtered = [x for x in MyModel.objects if x.myproperty()]
o mejor aún, una expresión de generador :
filtered = (x for x in MyModel.objects if x.myproperty())
Riffing @ TheGrimmScientist, la solución sugerida, puede hacer estas "propiedades sql" definiéndolas en el Administrador o el QuerySet, y reutilizar / encadenar / componerlas:
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()
Consulte https://docs.djangoproject.com/en/1.9/topics/db/managers/ para obtener más información. Tenga en cuenta que estoy saliendo de la documentación y no he probado lo anterior.
Sé que es una vieja pregunta, pero por el bien de los que están saltando aquí, creo que es útil leer la siguiente pregunta y la respuesta relativa: