Definiamo un'entità User
nel seguente modo:
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)
Ora possiamo definire alcune comode funzioni per recuperare i tipi di uso più frequente di utenti. La prima funzione restituirà gli utenti che non sono disabilitati da un amministratore:
def active_users():
return User.select(lambda user: not user.disabled)
In quella funzione io uso select
metodo User
soggetto che accetta una funzione lambda, ma la stessa funzione può essere scritto utilizzando funzione globale select
che accetta un generatore di espressione:
def active_users():
return select(u for u in User if not user.disabled)
Il risultato di active_users
funzione è un oggetto query. È possibile chiamare il metodo filter
dell'oggetto query per produrre una query più specifica. Ad esempio, posso usare active_users
funzione per selezionare gli utenti attivi i cui nomi iniziano con la 'A' lettera:
users = active_users().filter(lambda user: user.name.startswith('A')) \
.order_by(User.name)[:10]
Ora voglio trovare gli utenti che visitano il sito in pochi ultimi giorni. Posso definire un'altra funzione che utilizza la query restituito dalla funzione precedente e aumentare nel seguente modo:
def recent_users(days=1):
return active_users().filter(lambda u: u.last_visit > datetime.now() - timedelta(days))
In questo esempio passare l'argomento days
alla funzione e utilizzare il suo valore all'interno del filtro.
È possibile definire un insieme di tali funzioni che formeranno il livello di accesso ai dati dell'applicazione. Alcuni altri esempi:
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]
Negli esempi sopra definisco le funzioni globali. Un'altra opzione è quella di definire queste funzioni come classmethods del User
entità:
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]
Se si potrebbe desiderare di avere una funzione generica che può essere applicata a diverse classi di entità, allora è necessario passare la classe di entità come parametro. Ad esempio, se un certo numero di classi diverse hanno l'attributo deleted
, e si desidera avere un metodo generico per selezionare solo gli oggetti non eliminati, è possibile scrivere qualcosa di simile:
def select_active(cls):
return cls.select(lambda obj: not obj.deleted)
select_active(Message).filter(lambda msg: msg.author == current_user)
Tutte le funzioni sopra riportate sono uno svantaggio - non sono componibili. Non è possibile ottenere una query da una funzione e aumentarla con un'altra funzione. Se si desidera avere una funzione che può aumentare la query esistente, quella funzione dovrebbe accettare la query come argomento.Esempio:
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'))
La funzione name_starts_with
può essere applicato a un'altra domanda:
users1 = name_starts_with(active_users(), 'A').order_by(User.last_visited)
users2 = name_starts_with(recent_users(), 'B').filter(lambda user: user.online)
Inoltre stiamo lavorando sull'estensione API interrogazione che permetterà un programmatore di scrivere metodi di query personalizzati. Quando rilasciamo questa API sarà possibile suddividere i metodi di query personalizzati nel seguente modo:
select(u for u in User).recent(days=3).name_starts_with('A')[:10]
Spero di aver risposto alla tua domanda. Se questo è il caso, si prega di accettare la risposta come corretta.