Zdefiniujmy podmiot User
w następujący sposób:
from datetime import datetime, timedelta
from pony.orm import *
db = Database('sqlite', ':memory:')
class User(db.Entity):
username = Required(str, unique=True)
password = Required(str)
friends = Set("User", reverse='friends') # many-to-many symmetric relation
online = Required(bool, default=False) # if user is currently online
last_visit = Optional(datetime) # last visit time
disabled = Required(bool, default=False) # if user is disabled by administrator
sql_debug(True)
db.generate_mapping(create_tables=True)
Teraz możemy zdefiniować kilka wygodnych funkcji do pobierania najczęściej używanych typów użytkowników. Pierwsza funkcja zwróci użytkownikom, którzy nie są wyłączone przez administratora:
def active_users():
return User.select(lambda user: not user.disabled)
W tej funkcji używam select
metoda User
podmiotowi, który przyjmuje funkcję lambda, ale ta sama funkcja może być napisane przy użyciu globalnej select
funkcji, która przyjmuje wyrażenie generatora:
def active_users():
return select(u for u in User if not user.disabled)
wynikiem active_users
funkcji jest przedmiotem zapytania. Możesz zadzwonić pod metodę filter
obiektu zapytania, aby uzyskać bardziej szczegółowe zapytanie. Na przykład, można użyć funkcji wybierz active_users
aktywnych użytkowników, których nazwy zaczynają się na literę „A”:
users = active_users().filter(lambda user: user.name.startswith('A')) \
.order_by(User.name)[:10]
Teraz chcę znaleźć użytkowników, którzy odwiedzają stronę w ciągu kilku ostatnich dni. Można zdefiniować inną funkcję, która wykorzystuje zapytanie zwrócony od poprzedniej funkcji i zwiększa się w następujący sposób:
def recent_users(days=1):
return active_users().filter(lambda u: u.last_visit > datetime.now() - timedelta(days))
W tym przykładzie przechodzą days
argument funkcji i użyć jej wartości w filtrze.
Można zdefiniować zestaw takich funkcji, które utworzą warstwę dostępu do danych aplikacji. Kilka innych przykładów:
def users_with_at_least_n_friends(n=1):
return active_users().filter(lambda u: count(u.friends) >= n)
def online_users():
return User.select(lambda u: u.online)
def online_users_with_at_least_n_online_friends(n=1):
return online_users().filter(lambda u: count(f for f in u.friends if f.online) >= n)
users = online_users_with_at_least_n_online_friends(n=10) \
.order_by(User.name)[:10]
W powyższych przykładach definiuję funkcje globalne. Inną opcją jest określenie tych funkcji jako classmethods jednostki User
:
class User(db.Entity):
username = Required(str, unique=True)
...
@classmethod
def name_starts_with(cls, prefix):
return cls.select(lambda user: not user.disabled
and user.name.startswith(prefix))
...
users = User.name_starts_with('A').order_by(desc(User.last_visit))[:10]
Jeśli możesz mieć funkcję rodzajowe, które mogą być stosowane do różnych klas encji, wtedy trzeba zdać klasę encji jako parametr. Na przykład, jeśli pewna liczba różnych klas mają atrybut deleted
, a chcesz mieć ogólną metodę wybrać tylko nieusuniętych obiektów, można napisać coś takiego:
def select_active(cls):
return cls.select(lambda obj: not obj.deleted)
select_active(Message).filter(lambda msg: msg.author == current_user)
Wszystkie funkcje przewidziane powyżej mają jeden wada - nie można ich składać. Nie można uzyskać zapytania z jednej funkcji i rozszerzyć go o inną funkcję. Jeśli chcesz mieć funkcję, która może rozszerzyć istniejące zapytanie, funkcja ta powinna przyjąć zapytanie jako argument.Przykład:
def active_users():
return User.select(lambda user: not user.disabled)
def name_starts_with(query, prefix):
return query.filter(lambda user: user.name.startswith('prefix'))
Funkcja name_starts_with
mogą być stosowane do innego zapytania:
users1 = name_starts_with(active_users(), 'A').order_by(User.last_visited)
users2 = name_starts_with(recent_users(), 'B').filter(lambda user: user.online)
Również my pracujemy nad rozszerzeniem kwerendy API, który pozwoli programista napisać niestandardowych metod zapytania. Kiedy udostępnimy ten interfejs API, będzie można po prostu łączyć niestandardowe metody zapytań w następujący sposób:
select(u for u in User).recent(days=3).name_starts_with('A')[:10]
Mam nadzieję, że odpowiedziałem na twoje pytanie. W takim przypadku proszę przyjąć odpowiedź jako poprawną.