Skip to main content

Deeno is not an ORM.

Project description

Deeno is not an ORM. It is a thin database abstraction layer.

Disclaimer: Deeno development is in an early stage. It has good test coverage but still lacks experience in production environments.

Deeno provides a slick Python API for typical CRUD operations while motivating you to use plain SQL for more smart things. The rationale here is to not enforce object oriented paradigms on relational data. If you ever experienced a lot of WTF when reading chained ORM monsters but also are tired of doing plain CRUD stuff in raw SQL, then Deeno is for you.

Deeno assumes you define and manage database schemas outside the application which interacts with the database. This is typically the case for databases used by different applications running in different environments and on different platforms. In that case it is redundant to re-define a good deal of the schema (e.g. primary keys, columns and their default values) in models – DRY. Deeno collects (and caches) necessary schema information like primary key information and relation types using database introspection.

Currently SQLite and PostgreSQL are supported.

Reasons for Deeno:

  • You manage the database schema in SQL (i.e. not derived from Python model objects) and you do not want to specify your schema twice.
  • You prefer raw SQL over complex chained model methods where it is not obvious how things map to SQL.
  • You prefer to avoid raw SQL for simple CRUD operations.
  • You do not want your DB layer to be a black box (i.e. it should be easy to guess how Python statements map to SQL statements).

Quick Intro

Let’s set up a test database:

>>> import os
>>> from deeno.db import SQLiteDatabase
>>> db = SQLiteDatabase(':memory:')
>>> db.execute("""
...     CREATE TABLE customer (
...         customer_id INTEGER PRIMARY KEY,
...         name TEXT
...     )
... """)
>>> db.execute("""
...     CREATE TABLE user (
...         user_id INTEGER PRIMARY KEY,
...         name TEXT,
...         age INTEGER,
...         active BOOLEAN DEFAULT 1,
...         customer_id INTEGER REFERENCES customer (customer_id)
...     )
... """)
>>> db.execute("""
...     INSERT INTO user (name, age, active)
...     VALUES ('Bob', 28, 1), ('Joe', 32, 0)
... """)

You can work with relations without defining a single model:

>>> user = db.r.user.get(user_id=1)
>>> == 'Bob'
>>> = 'Bøb'

So the basic concept is that you have a database connection object db on which you can perform arbitrary SQL statements and which provides a minimal interface to relations using the scheme db.r.<relation-name>.<relation-action>.

Create a new user:

>>> user ='Brian', age=48)
>>> print('%s has ID %s' % (, user.user_id))
Brian has ID 3

The get and new functions on relations return active-record-like objects. That’s the most ORM-like behavior of Deeno. For all other stuff Deeno provides merely convenience functions for performing SQL queries:

>>> users ='name', where={'active': True}, limit=10)
>>> for user in users:
...    print(

For where clauses with not only equality checks, use parameterized SQL fragments:

>>> users ='name', where=('active AND age > ?', [40]))
>>> for user in users:
...    print(

This is equivalent:

>>> users = db.fetchall("SELECT * FROM user WHERE active AND age > ? LIMIT ?", [40, 10])

The select and fetchall functions do not return active records but named tuples. IMHO active records do not fit well when working with multiple rows from a relation.

Joins are written like that:

>>> users = db.r.user.left_join('customer', using=('customer_id',)).select()

Some more impressions:

>>> n = db.r.user.update({'active': True}, where=('age < ?', [40]))
>>> print('affected rows: %s' % n)
affected rows: 2
>>> rows = db.r.user.delete(returning=True)  # postgres only # doctest: +SKIP

Flattr this

Deeno Highlights:

  • no model definitions (deeno uses introspection when needed)
  • easy to execute plain SQL
  • CRUD in Python, fancy stuff in SQL


Deeno requires Python 3.


pip install deeno

If you want to interact with Postgres, you also need to install the psycopg2 module:

pip install psycopg2




Releases and documentation:
Issues and source code:


To contribute to Deeno, fork the project at BitBucket.

Every fix or new feature should include one or more corresponding test cases. Please also post an issue describing your fix or enhancement.

Deeno uses Buildout to easily set up the development environment. Buildout automates the process of downloading and installing requirements to use and develop Deeno. Requirements are installed local to the project source directory, i.e. it does not clutter the system Python installation.

In a fresh source checkout, run:

$ python3
$ bin/buildout

When done, the following scripts can be found in the bin/ directory:

Test runner script (a wrapper for nose).
Fabric binary to use for the project’s fabfile.
A Python interpreter with access to the local development version of the deeno module.


Version 0.5

  • More robust URI parsing (let DB do the job).

Version 0.4

  • Databases can now created using an a factory function which accepts an URI.

Version 0.3

  • Use keyword arguments for database setup (instead of generic config dictionaries, which are a pain to type).

Version 0.2

  • Minor exception handling improvements (e.g. use distinct exceptions when a relation is not found).
  • Minor README improvements.

Version 0.1

  • Initial release.

Project details

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Filename, size & hash SHA256 hash help File type Python version Upload date
deeno-0.5.tar.gz (13.1 kB) Copy SHA256 hash SHA256 Source None

Supported by

Elastic Elastic Search Pingdom Pingdom Monitoring Google Google BigQuery Sentry Sentry Error logging AWS AWS Cloud computing DataDog DataDog Monitoring Fastly Fastly CDN SignalFx SignalFx Supporter DigiCert DigiCert EV certificate StatusPage StatusPage Status page