2009-05-26 16 views
5

Próbuję zaimplementować coś w rodzaju dynamicznych wyszukiwaczy Rails w Pythonie (dla webapp/GAE). Dynamiczne wyszukiwania działają w ten sposób:Dynamic Finders and Method Missing in Python

  • Twoja osoba ma kilka pól: imię i nazwisko, wiek i adres e-mail.
  • Załóżmy, że chcesz znaleźć wszystkich użytkowników o nazwie "Robot".

Klasa Person ma metodę zwaną „find_by_name”, który otrzymuje nazwę i zwraca wynik zapytania:

@classmethod 
def find_by_name(cls, name): 
    return Person.gql("WHERE name = :1", name).get() 

Zamiast napisać metodę tak dla każdego atrybutu, ja "chciałbym, aby coś takiego jak Ruby's method_missing, która pozwala mi to zrobić.

tej pory widziałem te posty 2 blogu: http://blog.iffy.us/?p=43 i http://www.whatspop.com/blog/2008/08/method-missing-in-python.cfm ale ja bym lubią słuchać, co jest „najbardziej jest właściwa” sposób to zrobić.

Odpowiedz

7

Tak naprawdę nie ma potrzeby używania GQL tutaj - to tylko komplikuje sprawy. Oto proste wdrożenie:

class FindableModel(db.Model): 
    def __getattr__(self, name): 
    if not name.startswith("find_by_"): 
     raise AttributeError(name) 
    field = name[len("find_by_"):] 
    return lambda value: self.all().filter(field, value) 

Zauważ, że zwraca obiekt Query, który można nazwać .get(), .fetch() itp na; jest to bardziej wszechstronne, ale jeśli chcesz, możesz oczywiście zwrócić mu tylko jedną jednostkę.

+0

Próbowałem użyć tego kodu, ale nadal otrzymuję: 'AttributeError (" typ obiektu 'FindableModel' nie ma atrybutu "find_by_name" ",)'. Czy możesz wyjaśnić nieco więcej, jak tego użyć? – hakunin

+0

@hakunin Jak się go używa? Powinieneś uczynić tę klasę nadrzędną swojego modelu. –

+0

Oto model, którego używam: https://gist.github.com/2014164 Chciałbym użyć '__getattr__', ale po prostu nie zostanie on wywołany z jakiegoś powodu. – hakunin

0
class Person(object): 
    def __getattr__(self, name): 
     if not name.startswith("find_by"): raise AttributeError(name) 
     field_name = name.split("find_by_",1)[1] 
     return lambda name: Person.gql("WHERE %s = :1" % field_name, name).get() 
+1

__getattribute__ to zły pomysł, użyj __getattr__, zostanie on wywołany tylko z powodu brakujących atrybutów. –

+0

ok, zmieniłem ją. – Unknown

1

Można użyć „find_by metody” oraz słowa kluczowe, jak Django robi:

class Person (object): 
    def find_by(self, *kw): 
     qspec = ' AND '.join('%s=%s' % kv for kv in kw.items()) 
     return self.gql('WHERE ' + qspec) 

person.find_by(name='Robot') 
person.find_by(name='Robot', age='2') 

na tej drodze może skończyć się projektując własny składni zapytania. Zobacz, co Django robi ...

0
class Person: 

    name = "" 
    age = 0 
    salary = 0 

    def __init__(self, name, age): 
     self.name = name 
     self.age = age 

def znaleźć (clsobj, * args): powrót Person (name = "Jack", wiek = 20)

pętli for poniżej wkładek @classmethod dla wszystkich atrybutów klasy. To sprawia, że ​​dostępne są metody "find_by_name", "find_by_age" i "sinf_by_salary".

for attr in Person.__dict__.keys(): 
    setattr(Person, 'find_by_'+attr, find) 
    setattr(Person, 'find_by_'+attr, classmethod(find)) 

druku Person.find_by_name ("jack"). Age # wypisze wartość 20.

Nie jestem pewien, czy to właściwy sposób robienia rzeczy. Ale jeśli jesteś w stanie zaimplementować jednolite "znalezisko" dla wszystkich atrybutów, powyższy skrypt działa.