讓我們以下面的方式定義User
實體:
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)
現在我們可以定義一些方便的功能來獲取最常用的類型的用戶。第一個函數將返回誰沒有被管理員停用用戶:
def active_users():
return User.select(lambda user: not user.disabled)
在該功能我用select
方法,它接受一個lambda函數User
實體的,但可以使用全球select
函數寫相同功能,接受一個發電機表達式:
def active_users():
return select(u for u in User if not user.disabled)
active_users
函數的結果是查詢的對象。您可以調用filter
查詢對象的方法來產生更具體的查詢。例如,我可以用active_users
功能來選擇活躍用戶的名字開始與「A」字母:
users = active_users().filter(lambda user: user.name.startswith('A')) \
.order_by(User.name)[:10]
現在我想找個誰參觀了幾個最後的日子裏該網站的用戶。我可以定義一個使用從先前的函數返回的查詢另一個函數和以下列方式增加它:
def recent_users(days=1):
return active_users().filter(lambda u: u.last_visit > datetime.now() - timedelta(days))
在這個例子中我通過days
參數的功能和使用的過濾器內部的價值。
您可以定義一組這樣的函數,它們將形成應用程序的數據訪問層。一些更多的例子:
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]
在上面的例子中,我定義了全局函數。另一種選擇是定義這些功能作爲User
實體的classmethods:
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]
如果你可能希望有哪些可以適用於不同的實體類的通用功能,那麼你就需要通過實體類作爲參數。例如,如果許多不同類的有deleted
屬性,你想有一個通用的方法來選擇唯一的非刪除的對象,你可以寫類似的東西:上面
def select_active(cls):
return cls.select(lambda obj: not obj.deleted)
select_active(Message).filter(lambda msg: msg.author == current_user)
提供的所有功能都有一個缺點 - 它們不可組合。您無法從一個函數獲取查詢並使用另一個函數對其進行擴充。如果你想有一個可以增加現有查詢的函數,該函數應該接受查詢作爲參數。例如:
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'))
的name_starts_with
功能可以應用到其他查詢:
users1 = name_starts_with(active_users(), 'A').order_by(User.last_visited)
users2 = name_starts_with(recent_users(), 'B').filter(lambda user: user.online)
此外,我們正在處理的查詢擴展API,這將允許程序員編寫自定義的查詢方法。當我們發佈這個API將有可能只是連鎖自定義查詢方法一起以下列方式:
select(u for u in User).recent(days=3).name_starts_with('A')[:10]
希望我回答你的問題。如果是這種情況,請接受答案爲正確答案。