12. MetaData: что умеет
Создавать/удалять:
●
Всё сразу:
–
meta.create_all() / meta.drop_all()
По отдельности:
–
users.create() / users.drop()
Рефлексия:
●
Потаблично:
–
users = Table('users', meta, autoload=True)
Всё сразу:
–
meta.reflect()
Интроспекция:
●
meta.tables
–
12
13. MetaData: пример из жизни
# ... описание схемы
def get_indexes():
return [
Index('ix_users_id', users.c.id, unique=True),
Index('ix_creds_id', creds.c.id, unique=True),
Index('ix_creds_user_id', creds.c.user_id),
]
def load():
# ... загрузка данных
pass
def postload():
for ix in get_indexes():
ix.create()
13
16. Insert
Данные указываются при создании
●
ins = users.insert(values={'name': u'Jack'})
ins.execute()
Данные указываются при выполнении
●
ins = users.insert()
ins.execute(name=u'Jack')
Несколько записей сразу
●
ins = users.insert()
ins.execute([{'name': u'Jack'}, {'name': u'Ed'}])
Явно указывая соединение
●
engine = create_engine('sqlite:///')
ins = insert(users)
engine.connect().execute(ins, {'name': u'Jack'})
16
19. Select
SELECT id
FROM users
WHERE name='Jack'
q = users.select([users.c.id],
users.c.name==u'Jack')
SELECT users.id
FROM users
WHERE users.name=:name
19
20. Select
SELECT *
FROM users
JOIN creds ON
creds.user_id=users.id
q = users.join(creds).select()
SELECT users.id, users.name,
creds.id, creds.user_id,
creds.login, creds.passwd
JOIN creds ON
users.id=creds.user_id 20
21. Почему SA SQL Expr?
Потому что Python круче SQL
●
21
22. Почему SA SQL Expr?
Потому что Python круче SQL
●
Генерация SQL на лету:
●
q = select([users, creds.c.login],
–
from_obj=users.join(creds),
whereclause=users.c.name==u'Jack')
q = users.select()
–
q = q.where(users.c.name==u'Jack')
q = q.column(creds.c.login)
q.append_from(join(users, creds))
SELECT users.id, users.name, creds.login
–
FROM users
JOIN creds ON creds.user_id = users.id
WHERE users.name = 'Jack'
22
29. Рабочий пример:
Mappings
Session = scoped_session(sessionmaker(autoflush=False))
class Base(object):
def __init__(self, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
class User(Base):
pass
class Cred(Base):
pass
class Message(Base):
pass
Session.mapper(User, users) # (1)
Session.mapper(Cred, creds, properties={ # (2)
'user': relation(User, backref='credentials'),})
Session.mapper(Message, messages, properties={ # (3)
'recipients': relation(User, backref='inbox',
secondary=message_recipients),
29
'author': relation(User, backref='outbox'),})
30. Рабочий пример:
готовность №1
[ 1]>>> import schema as sch
[ 2]>>> import mappings as m
[ 3]>>> engine = create_engine('sqlite:///example.sqlite')
[ 4]>>> sch.meta.bind = engine
[ 5]>>> sch.meta.create_all()
[ 6]>>> m.Session.bind = engine
[ 7]>>> u1 = m.User.query.get(1)
[ 8]>>> u1.id
<<< 1
[ 9]>>> u1.name
<<< u'Jack'
[10]>>> u1.credentials
<<< [<Cred: jack>, <Cred: jack-rabbit>]
[11]>>> u1.outbox
<<< [<Message: from Jack to Edvard, Mary (subj: The first)>]
30
31. Рабочий пример:
выборки
[1]>>> q = m.User.query.filter(User.id>1)
[2]>>> print str(q)
SELECT users.id AS users_id, users.name AS users_name
FROM users
WHERE users.id > ?
[3]>>> q = q.filter(m.User.name!=None)
[4]>>> print str(q)
SELECT users.id AS users_id, users.name AS users_name
FROM users
WHERE users.id > ? AND users.name IS NOT NULL
[5]>>> list(q)
<<< [<User u'Edvard'>, <User u'Mary'>]
[6]>>> q.first()
<<< <User u'Edvard'>
31
32. Рабочий пример:
выборки (продолжение)
[1]>>> rabbit = m.Cred.query.
filter_by(login='jack-rabbit').one()
[2]>>> rabbit_user = m.User.query.
filter(User.credentials.contains(rabbit)).
one()
[3]>>> rabbit_messages = m.Message.query.
filter(or_(
Message.author==rabbit_user,
Message.recipients.contains(rabbit_user)
))
[4]>>> list(rabbit_messages)
<<<
[<Message: from Jack to Edvard, Mary (subj: The first)>,
<Message: from Edvard to Jack, Mary (subj: Hey all)>,
<Message: from Mary to Jack (subj: Hi)>] 32
33. Рабочий пример:
выборки (хардкор)
# Выбрать пользователей, у которых
# в исходящих больше одного сообщения
[1]>>> sess = m.Session()
[2]>>> q = sess.query(m.User, func.count('*')).
join(m.Message).group_by(m.User).
having(func.count('*')>1)
[3]>>> list(q)
<<< [(<User u'Edvard'>, 2)]
# Выбрать пользователей, у которых
# во входящих больше одного сообщения
[4]>>> sess = m.Session()
[5]>>> q = sess.query(m.User, func.count('*')).
join(sch.message_recipients).group_by(m.User).
having(func.count('*')>1)
[6]>>> list(q)
<<< [(<User u'Jack'>, 2), (<User u'Mary'>, 3)]
33
34. Рабочий пример:
сессия, unit of work
[ 1]>>> engine = create_engine('sqlite:///example.sqlite', echo=True)
[ 2]>>> sch.meta.bind = engine
[ 3]>>> m.Session.bind = engine
[ 4]>>> sess = m.Session()
[ 5]>>> jack = m.User.query.filter_by(name=u'Jack').one()
2008-09-14 14:19:11,504 INFO sqlalchemy.engine.base.Engine.0x...ca2c SELECT...
[ 6]>>> ed = m.User.query.filter_by(name=u'Edvard').one()
2008-09-14 14:20:22,731 INFO sqlalchemy.engine.base.Engine.0x...ca2c SELECT...
[ 7]>>> jack.name = u'Jack Daniels'
[ 8]>>> sess.dirty
<<< IdentitySet([<User u'Jack Daniels'>])
[ 9]>>> ed.name = u'Edvard Noringthon'
[10]>>> sess.dirty
<<< IdentitySet([<User u'Edvard Noringthon'>, <User u'Jack Daniels'>])
[11]>>> sess.flush()
2008-09-14 14:21:00,535 INFO sqlalchemy.engine.base.Engine.0x...ca2c
UPDATE users SET name=? WHERE users.id = ?
2008-09-14 14:21:00,535 INFO sqlalchemy.engine.base.Engine.0x...ca2c
['Jack Daniels', 1]
2008-09-14 14:21:00,604 INFO sqlalchemy.engine.base.Engine.0x...ca2c
UPDATE users SET name=? WHERE users.id = ?
2008-09-14 14:21:00,604 INFO sqlalchemy.engine.base.Engine.0x...ca2c
['Edvard Noringthon', 2]
34
35. users = Table(
'users', meta, ...)
class User:
pass
mapper(users, User)
«user» 5 раз, скучно?
35
36. Elixir:
ActiveRecord поверх SQLAlchemy
Elixir (http://elixir.ematia.de):
●
Декларативное описание в стиле Django
–
События:
–
До (update, insert, delete)
●
После (update, insert, delete)
●
Плагины:
–
acts_as_versioned (автоматическое хранение истории)
●
acts_as_encrypted (шифрование колонок)
●
associable (аналог Django generic relations)
●
...
●
36
37. Еще вкусности SQLAlchemy
«Хитрые» атрибуты:
●
Отложенная/непосредственная подгрузка
–
SQL-выражение как атрибут
–
Партицирование:
●
Вертикальное (часть таблиц в одной БД, часть в другой)
–
Горизонтальное (шардинг, содержимое одной таблицы
–
«раскидано» по таблицам/БД)
Нетривиальные маппинги:
●
Несколько маппингов к одному классу
–
Маппинг классов к SQL-запросам
–
...
● 37
38. Диагноз:
нужна ли вам SA
Используете другой Используете DB
● ●
ORM и счастливы? API2 и «чистый»
SQL?
Нет, в SQLAlchemy
–
«много буков», нет Да, SQLAlchemy
–
смысла даст вам это и много
что еще
38
39. Вопросы?
... не успели задать? - шлите почтой: the.pythy@gmail.com
39