Давайте определим 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
функцию для выбора активных пользователей, чьи имена начинаются с «А» письма:
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
лица:
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]
Надежда Я ответил на ваш вопрос. Если это так, пожалуйста, примите ответ как правильный.