Skip to main content

A simple database API

Project description

code-of-conduct build-status cover-status pyver-status pypiv-status coding-style

Introduction

What is DINAO? Well it might be easier to tell you what it’s not. DINAO Is Not An ORM. If you want an ORM, SQLAlchemy is absolutely the best python has to offer.

Target Audience

Do you like writing SQL? Do you hate all the boiler plate involved with setting up connections and cursors then cleaning them up? Would you just like something simple that executes a query and can map the results to simple data classes? Then DINAO is for you!

Influences and Guiding Principles

The APIs implemented mirror libraries I’ve used in other ecosystems. Specifically, you may notice similarities to the JDBI Declarative API or the MyBatis interface mappers. This is because I very much like this approach. You’re the developer, I’m just here to reduce the number of lines of code you have to write to meet your goal. At the end of the day you know your schema and database better than I do, and so you know what kinds of queries you need to write better than I do.

How do you pronounce DINAO?

You pronounce it “Dino” like “Dinosaur”. Going back to plain old SQL probably seems rather archaic after all.

Usage

Install via pip:

$ pip install dinao

You will also need to install your backend driver. Backends + drivers supported are:

For detailed connection string formats and examples see the backends documentation.

Basic Example

DINAO focuses binding functions to scoped connections / transactions against the database and using function signatures and type hinting to infer mapping and query parameterization.

Below shows a simple example of DINAO usage with async PostgreSQL. For more comprehensive usage and feature showcase see examples.

import asyncio
from typing import List
from dataclasses import dataclass
from dinao.backend import create_connection_pool, AsyncConnection
from dinao.binding import AsyncFunctionBinder

binder = AsyncFunctionBinder()

@dataclass
class MyModel:
    name: str
    value: int

@binder.execute(
    "CREATE TABLE IF NOT EXISTS my_table ( "
    "  name VARCHAR(32) PRIMARY KEY, "
    "  value INTEGER DEFAULT 0"
    ")"
)
async def make_table():
    pass


@binder.execute(
    "INSERT INTO my_table (name, value) "
    "VALUES(#{model.name}, #{model.value}) "
    "ON CONFLICT (name) DO UPDATE "
    "  SET value = #{model.value} "
    "WHERE my_table.name = #{model.name}"
)
async def upsert(model: MyModel) -> int:
    pass


# This is an example of a query where a template variable is
# directly replaced in a template.  This is via a template
# argument denoted with !{column_name}.  The #{search_term} on
# the other hand uses proper escaping and parameterization in the
# underlying SQL engine.
#
# IMPORTANT: This is a vector for SQL Injection, do not use
#            direct template replacement on untrusted inputs,
#            especially those coming from users.  Ensure that you
#            validate, restrict, or otherwise limit the values
#            that can be used in direct template replacement.
#
@binder.query(
    "SELECT name, value FROM my_table "
    "WHERE !{column_name} LIKE #{search_term}"
)
async def search(
    column_name: str, search_term: str
) -> List[MyModel]:
    pass


@binder.transaction()
async def populate(cnx: AsyncConnection = None):
    await make_table()
    await cnx.commit()
    await upsert(MyModel("testing", 52))
    await upsert(MyModel("test", 39))
    await upsert(MyModel("other_thing", 20))


async def main():
    con_url = (
        "postgresql+psycopg+async://"
        "user:pass@localhost:5432/mydb"
    )
    db_pool = create_connection_pool(con_url)
    binder.pool = db_pool
    await populate()
    for model in await search("name", "test%"):
        print(f"{model.name}: {model.value}")
    await db_pool.dispose()


if __name__ == '__main__':
    asyncio.run(main())

Contributing

Check out our code of conduct and contributing documentation.

Release Process

This library adheres too semantic versioning 2.0.0 standards, in general that means, given a version number MAJOR.MINOR.PATCH, increment:

  1. MAJOR version when you make incompatible API changes

  2. MINOR version when you add functionality in a backwards compatible manner

  3. PATCH version when you make backwards compatible bug fixes

Preview Builds

Every merge to main automatically publishes a development preview to PyPI, provided that __version__.py contains a .dev suffix. These builds use PEP 440 development release versions (e.g. 2.1.0.dev42).

To install the latest preview:

$ pip install --pre dinao

Preview builds are not installed by default; pip install dinao will always resolve to the latest stable release.

Stable Releases

Changes for the next version accumulate on the main branch until there is enough confidence in the build that it can be released. The release workflow is:

  1. A repository administrator opens a PR to set __version__.py to the release version (e.g. 2.1.0), and updates the change logs

  2. The PR is merged to main and the merge commit is tagged with the release version (e.g. release/2.1.0)

  3. Only tagged commits of main are built and published as stable releases

  4. Immediately after tagging, a follow-up PR bumps __version__.py to the next anticipated version with a .dev0 suffix (e.g. 2.2.0.dev0) so that preview builds resume

Project details


Download files

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

Source Distribution

dinao-2.1.0.dev154.tar.gz (24.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dinao-2.1.0.dev154-py3-none-any.whl (31.9 kB view details)

Uploaded Python 3

File details

Details for the file dinao-2.1.0.dev154.tar.gz.

File metadata

  • Download URL: dinao-2.1.0.dev154.tar.gz
  • Upload date:
  • Size: 24.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for dinao-2.1.0.dev154.tar.gz
Algorithm Hash digest
SHA256 50972462061f524acfaabecc94ffa632643ba8edb9571b3a6a91796d59bb7f92
MD5 35057f9d3d3a1a376f8c5ae0922633d6
BLAKE2b-256 2a0f7dd58a475eda2c013b923e361f38e26e67ed6abb5ea32959deb35383b87e

See more details on using hashes here.

File details

Details for the file dinao-2.1.0.dev154-py3-none-any.whl.

File metadata

  • Download URL: dinao-2.1.0.dev154-py3-none-any.whl
  • Upload date:
  • Size: 31.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for dinao-2.1.0.dev154-py3-none-any.whl
Algorithm Hash digest
SHA256 bf0a2a88d30edd6c720d53b4024148c4d2b08e0f60fe424c59c95909700aad83
MD5 50ba5bf90b2a330e3cbaf45c53cd9636
BLAKE2b-256 7bf9ec576b060c84b5e784e99ad900138bc23443a6146147786eb8319ecc3e35

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page