Czy można filtrować zestaw zapytań Django według właściwości modelu?
mam metodę w swoim modelu:
@property
def myproperty(self):
[..]
a teraz chcę filtrować według tej właściwości, takiej jak:
MyModel.objects.filter(myproperty=[..])
czy to jest w jakiś sposób możliwe?
Odpowiedzi:
Nie. Filtry Django działają na poziomie bazy danych, generując SQL. Aby filtrować na podstawie właściwości Pythona, musisz załadować obiekt do Pythona, aby ocenić właściwość - iw tym momencie wykonałeś już całą pracę, aby go załadować.
źródło
Mogę źle zrozumieć twoje pierwotne pytanie, ale w Pythonie jest wbudowany filtr .
Ale lepiej jest użyć rozumienia listy :
filtered = [x for x in MyModel.objects if x.myproperty()]
lub jeszcze lepiej, wyrażenie generatora :
filtered = (x for x in MyModel.objects if x.myproperty())
źródło
Pomijając sugerowane obejście @ TheGrimmScientist, możesz utworzyć te „właściwości sql”, definiując je w Menedżerze lub QuerySet, a następnie ponownie użyć / połączyć / skomponować je:
Z menedżerem:
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)
Za pomocą 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()
Więcej informacji można znaleźć pod adresem https://docs.djangoproject.com/en/1.9/topics/db/managers/ . Zauważ, że wychodzę z dokumentacji i nie testowałem powyższego.
źródło
Wygląda na to, że moim rozwiązaniem będzie użycie F () z adnotacjami .
Nie będzie filtrować
@property
, ponieważF
rozmawia z bazą danych, zanim obiekty zostaną przeniesione do Pythona. Ale nadal umieszczam to tutaj jako odpowiedź, ponieważ moim powodem, dla którego chciałem filtrować według właściwości, było tak naprawdę chęć filtrowania obiektów według wyniku prostej arytmetyki na dwóch różnych polach.więc coś w rodzaju:
companies = Company.objects\ .annotate(chairs_needed=F('num_employees') - F('num_chairs'))\ .filter(chairs_needed__lt=4)
zamiast definiować właściwość jako:
@property def chairs_needed(self): return self.num_employees - self.num_chairs
następnie sporządzenie listy obejmującej wszystkie obiekty.
źródło
Miałem ten sam problem i opracowałem to proste rozwiązanie:
objects_id = [x.id for x in MyModel.objects.all() if x.myProperty == [...]] MyModel.objects.filter(id__in=objects_id)
Wiem, że nie jest to najbardziej wydajne rozwiązanie, ale może pomóc w prostych przypadkach, takich jak moje
źródło
PROSZĘ, aby ktoś mnie poprawił, ale myślę, że znalazłem rozwiązanie, przynajmniej dla mojego własnego przypadku.
Chcę popracować nad wszystkimi elementami, których właściwości są dokładnie równe… cokolwiek.
Ale mam kilka modeli i ta procedura powinna działać dla wszystkich modeli. I tak:
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)
Dzięki tej uniwersalnej podprogramie mogę wybrać wszystkie te elementy, które dokładnie odpowiadają mojemu słownikowi kombinacji „określić” (nazwa właściwości, wartość właściwości).
Pierwszy parametr przyjmuje (modeles.Model),
drugi to słownik, taki jak: {"property1": "77", "property2": "12"}
I tworzy instrukcję SQL, taką jak
SELECT * from appname_modelname WHERE property1 = '77' AND property2 = '12'
i zwraca QuerySet dla tych elementów.
To jest funkcja testowa:
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
I? Co myślisz?
źródło
AttributeError: 'RawQuerySet' object has no attribute 'values'
Wiem, że to stare pytanie, ale ze względu na skaczących tutaj myślę, że warto przeczytać poniższe pytanie i względną odpowiedź:
Jak dostosować filtr administratora w Django 1.4
źródło
Może to być również możliwe użycie adnotacji queryset które powielają się nieruchomość get / set-Logic, jak sugeruje np @rattray i @thegrimmscientist , w połączeniu z
property
. Może to dać coś, co działa zarówno na poziomie Pythona, jak i na poziomie bazy danych.Nie jestem jednak pewien wad: zobacz przykład w tym pytaniu SO .
źródło