This package provides Morepath integration for the Pony Object-Relational Mapper library.
This package binds the database session to the request so
you can interact with the database in your App directly
without using db_session
.
Install more.pony
:
$ pip install -U more.pony
Extend your App class from PonyApp:
from more.pony import PonyApp
class App(PonyApp):
pass
Create your model:
from pony.orm import Database, PrimaryKey, Optional
db = Database()
class Document(db.Entity):
_table_ = 'document'
id = PrimaryKey(int, auto=True)
title = Optional(str)
content = Optional(str)
def update(self, payload={}):
self.set(**payload)
def remove(self):
self.delete()
Setup the database in your start script:
import morepath
from .app import App
from .model import db
def run():
db.bind(provider='sqlite', filename='app.db', create_db=True)
db.generate_mapping(create_tables=True)
morepath.autoscan()
morepath.run(App())
Now you can use the model in your path:
from .app import App
from .model import Document
@App.path(model=Document, path='documents/{id}')
def get_document(request, id=0):
return Document[id]
And in your view:
from .app import App
from .model import Document
@App.json(model=Document)
def document_default(self, request):
return {
'id': self.id,
'title': self.title,
'content': self.content,
'link': request.link(self)
}
@App.json(model=Document, request_method='PUT')
def document_update(self, request):
self.update(request.json)
@App.json(model=Document, request_method='DELETE')
def document_remove(self, request):
self.remove()
You can set the arguments which are passed to db_session
in the pony
section of your settings.
The default settings are:
@App.setting_section(section='pony')
def get_pony_settings():
return {
'allowed_exceptions': [],
'immediate': False,
'retry': 0,
'retry_exceptions': [TransactionError],
'serializable': False,
'strict': False
}
More information about the arguments you find in the Pony API documentation.
You can also use the database
settings section for your database setup,
which allows you to use different setups for production, development and
testing environments.
Just create create an App for each environment and load specific settings files:
class App(PonyApp):
pass
with open('settings/default.yml') as defaults:
defaults_dict = yaml.load(defaults)
App.init_settings(defaults_dict)
class ProductionApp(App):
pass
with open('settings/production.yml') as settings:
settings_dict = yaml.load(settings)
ProductionApp.init_settings(settings_dict)
class TestApp(App):
pass
with open('settings/test.yml') as settings:
settings_dict = yaml.load(settings)
TestApp.init_settings(settings_dict)
The database configuration in the YAML settings files, depending on the database you use, could look something like:
database:
provider: sqlite
filename: app.db
create_db: true
In your start script you setup the database and load
the App according to the RUN_ENV
environment variable:
import os
import morepath
from .app import App, ProductionApp, TestApp
from .model import db
def setup_db(app):
db_params = app.settings.database.__dict__.copy()
db.bind(**db_params)
db.generate_mapping(create_tables=True)
def run():
morepath.autoscan()
if os.getenv('RUN_ENV') == 'production':
ProductionApp.commit()
app = ProductionApp()
elif os.getenv('RUN_ENV') == 'test':
TestApp.commit()
app = TestApp()
else:
App.commit()
app = App()
setup_db(app)
morepath.run(app)
Detail about the database configuration you find in the PonyOrm documentation.
If you want to trigger side effects (like sending an email or
writing to filesystem) on database commits you can emit a signal
in the @request.after
of the view which triggers the side effects.
Like this the side effects will be triggered just before the database session gets committed and only if it wasn't rolled back.
This example uses more.emit:
@App.json(model=Document, request_method='PUT')
def document_update(self, request):
self.update(request.json)
@request.after
def after(response):
request.app.signal.emit('document_updated', self, request)
Altenatively you can use in your model the PonyORM after_insert(), after_update() or after_delete() entity-hooks. This makes sure that the side effect is triggered after the database session has committed.
The drawback is that you don't have easy access to the request or app in the model.