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
のメソッドを使用しますが、ジェネレーター式を受け入れるグローバル関数を使用して同じ関数を作成できます。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]
上記の例では、グローバル関数を定義しています。もう 1 つのオプションは、これらの関数を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]
さまざまなエンティティ クラスに適用できる汎用関数が必要な場合は、エンティティ クラスをパラメーターとして渡す必要があります。たとえば、多数の異なるクラスにdeleted
属性があり、削除されていないオブジェクトのみを選択するジェネリック メソッドが必要な場合は、次のように記述できます。
def select_active(cls):
return cls.select(lambda obj: not obj.deleted)
select_active(Message).filter(lambda msg: msg.author == current_user)
上記のすべての関数には、1 つの欠点があります。それらは構成可能ではありません。ある関数からクエリを取得して、別の関数で拡張することはできません。既存のクエリを拡張できる関数が必要な場合、その関数はクエリを引数として受け入れる必要があります。例:
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]
あなたの質問に答えていただければ幸いです。この場合、答えを正しいものとして受け入れてください。