Сам фреймворк Pyramid не имеет встроенных возможностей работы с базами данных, в отличии от таких фреймворков как Django (Django ORM) и Ruby on Rails (Active Record). Хорошим выбором для реляционных БД будет ORM SQLAlchemy.
Организация БД в пирамиде не зависит от фреймворка, поэтому можно использовать любую структуру, которая вам удобна. Ниже я приведу один из вариантов, более подробно про SQLAlchemy можно прочитать в разделе SQLAlchemy ORM.
Вынесем модели и то что касается соединения с БД в отдельный файл models.py.
# models.py
from sqlalchemy import Column, Integer, Text, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
engine = create_engine('sqlite:///foo.db')
Session = sessionmaker()
Base = declarative_base(bind=engine)
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(Text)
def __repr__(self):
return self.nameВ представлениях мы просто создаем объект
sqlalchemy.orm.session.Session и работаем с объектами, как описано в
документации SQLAlchemy. При этом в каждом представлении нам необходимо
создавать новую SQLAlchemy сессию, а если были изменения подтверждать их при
помощи метода sqlalchemy.orm.session.Session.commit().
# __init__.py
from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from pyramid.response import Response
from models import User, Session, Base, engine
def hello(request):
DBSession = Session(bind=engine)
result = DBSession.query(User).all()
import time
timestamp = int(time.time())
new_user = User(name=str(timestamp))
DBSession.add(new_user)
DBSession.commit()
return Response(str(result))
if __name__ == '__main__':
Base.metadata.create_all()
DBSession = Session(bind=engine)
DBSession.add(User(name='Vasya'))
DBSession.add(User(name='Petya'))
DBSession.commit()
config = Configurator()
config.add_route('hello_world', '/')
config.add_view(hello, route_name='hello_world')
app = config.make_wsgi_app()
server = make_server('0.0.0.0', 8000, app)
server.serve_forever()Данный пример при каждом обновлении делает новую запись в БД и отдает их браузеру.
См.также
ZopeTransactionExtension это расширение для SQLAlchemy, которое привязывает
сессии к универсальному менеджеру транзакций transaction.
Добавим его в наш пример:
# models.py
from sqlalchemy import Column, Integer, Text, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from zope.sqlalchemy import ZopeTransactionExtension
engine = create_engine('sqlite:///foo.db')
Session = sessionmaker(bind=engine,
extension=ZopeTransactionExtension())
Base = declarative_base(bind=engine)
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(Text)
def __repr__(self):
return self.nameТеперь вместо DBSession.commit, нужно использовать transaction.commit().
# __init__.py
from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from pyramid.response import Response
from models import User, Session, Base, engine
import transaction
def hello(request):
DBSession = Session(bind=engine)
result = str(DBSession.query(User).all())
import time
timestamp = int(time.time())
new_user = User(name=str(timestamp))
DBSession.add(new_user)
transaction.commit()
return Response(result)
if __name__ == '__main__':
Base.metadata.create_all()
DBSession = Session(bind=engine)
DBSession.add(User(name='Vasya'))
DBSession.add(User(name='Petya'))
transaction.commit()
config = Configurator()
config.add_route('hello_world', '/')
config.add_view(hello, route_name='hello_world')
app = config.make_wsgi_app()
server = make_server('0.0.0.0', 8000, app)
server.serve_forever()Теперь мы используем общий, глобальный менеджер транзакций, который работает не только с SQLAlchemy но и со всеми модулями которые его поддерживают. Ниже пример сессии в которой одновременно участвуют SQLAlchemy и pyramid_mailer.
Внимание
Пример ниже работает с версией repoze.sendmail==4.1. Установить ее можно
припомощи команды:
pip install repoze.sendmail==4.1Если возникает ошибка raise ValueError("TPC in progress"), cмотри
https://github.com/repoze/repoze.sendmail/issues/31
# __init__.py
from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from pyramid.response import Response
from models import User, Session, Base, engine
import transaction
from pyramid_mailer.message import Message
message = Message(subject="hello world",
sender="example@yandex.ru",
recipients=["me@uralbash.ru"],
body="hello, uralbash")
def hello(request):
DBSession = Session(bind=engine)
result = str(DBSession.query(User).all())
import time
timestamp = int(time.time())
new_user = User(name=str(timestamp),
id=100500)
DBSession.add(new_user)
from pyramid_mailer import get_mailer
mailer = get_mailer(request)
mailer.send(message)
try:
transaction.commit()
except Exception as e:
transaction.abort()
return Response(str(e))
return Response(result)
if __name__ == '__main__':
Base.metadata.create_all()
DBSession = Session(bind=engine)
DBSession.add(User(name='Vasya'))
DBSession.add(User(name='Petya'))
transaction.commit()
settings = {'mail.host': 'smtp.yandex.ru',
'mail.port': '465',
'mail.ssl': True,
'pyramid_mailer.prefix': 'mail.',
'mail.username': 'example@yandex.ru',
'mail.password': 'example password'}
config = Configurator(settings=settings)
config.include('pyramid_mailer')
config.add_route('hello_world', '/')
config.add_view(hello, route_name='hello_world')
app = config.make_wsgi_app()
server = make_server('0.0.0.0', 8000, app)
server.serve_forever()В этом примере вьюха hello записывает нового пользователя с id=100500 в
БД и отправляет письмо на адрес me@uralbash.ru. При первом обновлении
страницы пользователь добавится в БД и отправится письмо. При последующих
обновлениях произойдет ошибка т.к. пользователь с таким id уже существует,
при этом transaction.abort() откатит изменения как в сессии
SQLAlchemy, так и в сессии pyramid_mailer, поэтому письмо не
отправится.
pyramid_tm автоматически подтверждает транзакцию в каждом запросе. Т.е.
если мы забыли написать transaction.commit(), то он все равно вызовется,
при этом мы также можем вызывать его явно.
# __init__.py
from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from pyramid.response import Response
from models import User, Session, Base, engine
def hello(request):
DBSession = Session(bind=engine)
result = str(DBSession.query(User).all())
import time
timestamp = int(time.time())
new_user = User(name=str(timestamp))
DBSession.add(new_user)
return Response(result)
if __name__ == '__main__':
Base.metadata.create_all()
DBSession = Session(bind=engine)
DBSession.add(User(name='Vasya'))
DBSession.add(User(name='Petya'))
config = Configurator()
config.include('pyramid_tm')
config.add_route('hello_world', '/')
config.add_view(hello, route_name='hello_world')
app = config.make_wsgi_app()
server = make_server('0.0.0.0', 8000, app)
server.serve_forever()pyramid_sqlalchemy создает объект базового класса Base и сессии
Session автоматически. Мы просто указываем строку подключения к БД в
настройках и включаем модуль pyramid_sqlalchemy в проект.
# __init__.py
from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from pyramid.response import Response
from models import User
from pyramid_sqlalchemy import BaseObject, Session
import transaction
def hello(request):
result = str(Session.query(User).all())
import time
timestamp = int(time.time())
new_user = User(name=str(timestamp))
Session.add(new_user)
return Response(result)
if __name__ == '__main__':
settings = {'sqlalchemy.url': 'sqlite:///:memory:'}
config = Configurator(settings=settings)
config.include('pyramid_tm')
config.include('pyramid_sqlalchemy')
BaseObject.metadata.create_all()
Session.add(User(name='Vasya'))
Session.add(User(name='Petya'))
transaction.commit()
config.add_route('hello_world', '/')
config.add_view(hello, route_name='hello_world')
app = config.make_wsgi_app()
server = make_server('0.0.0.0', 8000, app)
server.serve_forever()Файл с моделями теперь выглядит значительно проще.
# models.py
from sqlalchemy import Column, Integer, Text
from pyramid_sqlalchemy import BaseObject
class User(BaseObject):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(Text)
def __repr__(self):
return self.nameНесмотря на то, что фреймворк Pyramid не предоставляет инструментов для работы с базами дынных, есть большое количество сторонних модулей и расширений (написанных специально для Pyramid) которые реализуют этот функционал.
Для быстрого старта существует шаблон проекта alchemy, по которому можно
быстро начать использовать пирамиду вместе с SQLAlchemy.
$ pcreate --scaffold alchemy sqla_demo
$ cd sqla_demo
$ python setup.py developДополнительную информацию можно найти в Pyramid Cookbook.