Pony ORM
Pony ORM is a Python Object-Relational Mapper that allows developers to work with databases using Python objects and queries written in plain Python. It translates Python queries into optimized SQL, supporting SQLite, PostgreSQL, MySQL, and Oracle. The current version is 0.7.19. Releases occur periodically, often driven by new Python version support or significant bugfixes, indicating an active but not rapid development cadence with several months to a year between minor releases.
Common errors
-
TransactionError: An attempt to work with detached object
cause Trying to access or modify Pony ORM entities outside of an active `db_session` context.fixWrap your database operations within a `with db_session():` block or decorate functions with `@db_session`. -
TypeError: DBAPIProvider.__init__() got multiple values for argument 'database'
cause An internal bug in Pony ORM versions prior to 0.7.16, specifically affecting `db.bind()` calls with certain argument combinations.fixUpgrade Pony ORM to version 0.7.16 or newer. This bug was resolved in that release. -
OperationalError: (2013, 'Lost connection to MySQL server during query')
cause Network instability, database server restart, or connection timeouts for long-running processes that hold open connections.fixWhile Pony ORM includes some reconnection logic, ensure each logical unit of work is encapsulated within a `db_session`. For very long-running applications, consider explicit connection management or shorter `wait_timeout` on the database. -
AttributeError: module 'pony.orm.parser' has no attribute 'suite'
cause Using Pony ORM older than 0.7.15 with Python 3.10+. Python 3.10 removed the `parser` module which older Pony versions relied upon.fixUpgrade Pony ORM to version 0.7.15 or newer, which transitioned to using Python's standard `ast` module.
Warnings
- breaking Pony ORM 0.7.17 dropped support for Python versions older than 3.8. Users on Python 3.7 or earlier must either upgrade their Python environment or use an older Pony ORM version (e.g., 0.7.16 or earlier).
- breaking Pony ORM versions prior to 0.7.15 were incompatible with Python 3.10+ due to reliance on the deprecated `parser` module. Attempting to use older versions on Python 3.10+ will result in import errors.
- gotcha All database operations (entity creation, updates, queries) must occur within an active `db_session`. Forgetting to use the `@db_session` decorator or `with db_session():` context manager will lead to `TransactionError` or `ObjectIsFreedException`.
- gotcha Pony ORM's unique query language uses generator expressions (e.g., `select(p for p in Person if p.age < 35)`). New users often try to use SQL strings or SQLAlchemy-style expressions, which will not work as expected.
Install
-
pip install pony
Imports
- Database
from pony.orm import Database
- Required
from pony.orm import Required
- Optional
from pony.orm import Optional
- Set
from pony.orm import Set
- PrimaryKey
from pony.orm import PrimaryKey
- db_session
from pony.orm import db_session
- select
from pony.orm import select
Quickstart
from pony.orm import *
db = Database()
class Person(db.Entity):
id = PrimaryKey(int, auto=True)
name = Required(str)
age = Optional(int)
cars = Set('Car')
class Car(db.Entity):
make = Required(str)
model = Required(str)
owner = Required(Person)
db.bind(provider='sqlite', filename=':memory:', create_db=True)
db.generate_mapping(create_tables=True)
@db_session
def create_data():
p1 = Person(name='John Doe', age=30)
c1 = Car(make='Toyota', model='Camry', owner=p1)
p2 = Person(name='Jane Smith', age=25)
print("Data created.")
@db_session
def query_data():
for p in select(p for p in Person if p.age < 35):
print(f"Name: {p.name}, Age: {p.age}")
for car in p.cars:
print(f" Car: {car.make} {car.model}")
create_data()
query_data()