An async ORM. 🗃

Related tags

ORMorm
Overview

ORM

Build Status Coverage Package version

The orm package is an async ORM for Python, with support for Postgres, MySQL, and SQLite. ORM is built with:

Because ORM is built on SQLAlchemy core, you can use Alembic to provide database migrations.

ORM is still under development: We recommend pinning any dependencies with orm~=0.1

Note: Use ipython to try this from the console, since it supports await.

import databases
import orm
import sqlalchemy

database = databases.Database("sqlite:///db.sqlite")
metadata = sqlalchemy.MetaData()


class Note(orm.Model):
    __tablename__ = "notes"
    __database__ = database
    __metadata__ = metadata

    id = orm.Integer(primary_key=True)
    text = orm.String(max_length=100)
    completed = orm.Boolean(default=False)

# Create the database
engine = sqlalchemy.create_engine(str(database.url))
metadata.create_all(engine)

# .create()
await Note.objects.create(text="Buy the groceries.", completed=False)
await Note.objects.create(text="Call Mum.", completed=True)
await Note.objects.create(text="Send invoices.", completed=True)

# .all()
notes = await Note.objects.all()

# .filter()
notes = await Note.objects.filter(completed=True).all()

# exact, iexact, contains, icontains, lt, lte, gt, gte, in
notes = await Note.objects.filter(text__icontains="mum").all()

# .get()
note = await Note.objects.get(id=1)

# .update()
await note.update(completed=True)

# .delete()
await note.delete()

# 'pk' always refers to the primary key
note = await Note.objects.get(pk=2)
note.pk  # 2

ORM supports loading and filtering across foreign keys...

import databases
import orm
import sqlalchemy

database = databases.Database("sqlite:///db.sqlite")
metadata = sqlalchemy.MetaData()


class Album(orm.Model):
    __tablename__ = "album"
    __metadata__ = metadata
    __database__ = database

    id = orm.Integer(primary_key=True)
    name = orm.String(max_length=100)


class Track(orm.Model):
    __tablename__ = "track"
    __metadata__ = metadata
    __database__ = database

    id = orm.Integer(primary_key=True)
    album = orm.ForeignKey(Album)
    title = orm.String(max_length=100)
    position = orm.Integer()


# Create some records to work with.
malibu = await Album.objects.create(name="Malibu")
await Track.objects.create(album=malibu, title="The Bird", position=1)
await Track.objects.create(album=malibu, title="Heart don't stand a chance", position=2)
await Track.objects.create(album=malibu, title="The Waters", position=3)

fantasies = await Album.objects.create(name="Fantasies")
await Track.objects.create(album=fantasies, title="Help I'm Alive", position=1)
await Track.objects.create(album=fantasies, title="Sick Muse", position=2)


# Fetch an instance, without loading a foreign key relationship on it.
track = await Track.objects.get(title="The Bird")

# We have an album instance, but it only has the primary key populated
print(track.album)       # Album(id=1) [sparse]
print(track.album.pk)    # 1
print(track.album.name)  # Raises AttributeError

# Load the relationship from the database
await track.album.load()
assert track.album.name == "Malibu"

# This time, fetch an instance, loading the foreign key relationship.
track = await Track.objects.select_related("album").get(title="The Bird")
assert track.album.name == "Malibu"

# Fetch instances, with a filter across an FK relationship.
tracks = Track.objects.filter(album__name="Fantasies")
assert len(tracks) == 2

# Fetch instances, with a filter and operator across an FK relationship.
tracks = Track.objects.filter(album__name__iexact="fantasies")
assert len(tracks) == 2

# Limit a query
tracks = await Track.objects.limit(1).all()
assert len(tracks) == 1

Data types

The following keyword arguments are supported on all field types.

  • primary_key
  • allow_null
  • default
  • index
  • unique

All fields are required unless one of the following is set:

  • allow_null - Creates a nullable column. Sets the default to None.
  • allow_blank - Allow empty strings to validate. Sets the default to "".
  • default - Set a default value for the field.

The following column types are supported. See TypeSystem for type-specific validation keyword arguments.

  • orm.String(max_length)
  • orm.Text()
  • orm.Boolean()
  • orm.Integer()
  • orm.Float()
  • orm.Date()
  • orm.Time()
  • orm.DateTime()
  • orm.JSON()
Comments
  • Unable to create models

    Unable to create models

    Checklist

    • [x] The bug is reproducible against the latest release and/or master.
    • [x] There are no similar issues or pull requests to fix it yet.

    Describe the bug

    Unable to create models due to a RuntimeError caused by anyio.

    To reproduce

    database = databases.Database("url")
    models = orm.ModelRegistry(database = database)
    models.create_all()
    

    Expected behavior

    Models getting created. Successfully connecting to the database.

    Actual behavior

    The models don't get created due to the RuntimeError being raised by anyio and it fails to connect to the database.

    Debugging material

    Traceback from ipython

        RuntimeError                         Traceback (most recent call last)
    <ipython-input-1-3765cc235a53> in <module>
         16
         17 # Create the tables
    ---> 18 models.create_all()
         19
         20 await Note.objects.create(text="Buy the groceries.", completed=False)
    
    /data/data/com.termux/files/usr/lib/python3.9/site-packages/orm/models.py in create_all(self)
         31     def create_all(self):
         32         url = self._get_database_url()
    ---> 33         anyio.run(self._create_all, url)
         34
         35     def drop_all(self):
    
    /data/data/com.termux/files/usr/lib/python3.9/site-packages/anyio/_core/_eventloop.py in run(func, backend, backend_options, *args)
         40         pass
         41     else:
    ---> 42         raise RuntimeError(f'Already running {asynclib_name} in this thread')
         43
         44     try:
    
    RuntimeError: Already running asyncio in this thread
    

    Environment

    • OS: Linux aarch64 Android
    • Python version: 3.9.7
    • ORM version: 0.2.0

    Additional context

    I fixed it by making create_all an async function and using await self._create_all(url) instead of anyio.run(self._create_all, url) although I don't think that's the best way to fix this.

    bug 
    opened by FlasH1719 38
  • NotNullViolationError: null value in column

    NotNullViolationError: null value in column "id"

    I just got an exception when tried to create new database entry via await SomeModel.objects.create(...):

    asyncpg.exceptions.NotNullViolationError: null value in column "id" violates not-null constraint
    DETAIL:  Failing row contains (null, John, Doe, [email protected], 1997, M, null, f, null, f, 0, 0, null, 0, 0).
    

    Model:

    class SomeModel(Model):
        __tablename__ = DATABASE_TABLE
        __database__ = DATABASE
        __metadata__ = DATABASE_METADATA
    
        id = Integer(primary_key=True)
        # Some other fields
    

    According to examples it should work without providing id field manually. Maybe I am missing something?

    opened by HarrySky 23
  • Primary key field not being added to any queries

    Primary key field not being added to any queries

    Checklist

    • [x] The bug is reproducible against the latest release and/or master.
    • [x] There are no similar issues or pull requests to fix it yet.

    Describe the bug

    Despite the column id (the table's primary key) being included in queries explicitly via the name (id=), and via implicit (pk=), the same result is still yielded. I don't know what could be causing this as I've been using encode/orm since 0.2 and its worked fine, up until this one god forsaken project.

    To reproduce

    I'm honestly not too sure what's causing this so im going to provide my table, the queries I'm making, and some example data

    # Define the table
    import datetime
    import orm
    from databases import Database
    
    registry = orm.ModelRegistry(Database("sqlite:///main.db"))
    
    
    class User(orm.Model):
        tablename = "users"
        registry = registry
        fields = {
            "id": orm.BigInteger(primary_key=True),
            "access_token": orm.Text(default=None),
            "refresh_token": orm.Text(default=None),
            "expires": orm.DateTime(default=None),
            "session": orm.Text(default=None),
            "bio": orm.String(default="Default bio", min_length=2, max_length=4069),
        }
    
    # elsewhere in an async function:
    async def main():  # not production code, I'm running this in a fastapi server - but that's probably irrelivant 
        data = {
            "id": 421698654180012064,
            "access_token": "aoigniawghaoiwhgnagwuwgnuip",
            "refresh_token": "aiwngfuagnpauiwaiwbguiwgbuiawg",
            "expires": datetime.datetime.now() + datetime.timedelta(days=7),
            "session": "hello world"
        }
        await User.objects.create(**data)  # , pk=data["id"]) also raises the same error
        # I also tried with the below code, and it yielded a stranger error, but pretty much the same
        """
        await User.objects.update_or_create(id=data["id"], defaults=data)  # specifying pk still does nothing
        """
    

    Expected behavior

    An entry is correctly created

    Actual behavior

    Sometimes (unclear why) the id field autoincrements itself(???), or (more often than not) the code errors out with a not null constraint failure

    Debugging material

    Notable tracebacks:

    Traceback from console
    Authorized user:
    {
        'id': '123456788989898989898',
        'username': '...',
        'avatar': 'redacted',
        'discriminator': '0000',
        'public_flags': 64,
        'flags': 64,
        'banner': None,
        'banner_color': '#36393f',
        'accent_color': 3553599,
        'locale': 'en-GB',
        'mfa_enabled': True,
        'email': '[email protected]',
        'verified': True
    }
    Authorized user's auth data:
    {'access_token': 'awdadaawdawdawd', 'token_type': 'Bearer', 'expires_in': 604800, 'refresh_token': 'dawsdwadswasd', 'scope': 'identify email guilds'}
    INFO:     127.0.0.1:44550 - "GET /callbacks/authorise?code=redacted HTTP/1.1" 500 Internal Server Error
    ERROR:    Exception in ASGI application
    Traceback (most recent call last):
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/uvicorn/protocols/http/httptools_impl.py", line 376, in run_asgi
        result = await app(self.scope, self.receive, self.send)
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/uvicorn/middleware/proxy_headers.py", line 75, in __call__
        return await self.app(scope, receive, send)
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/uvicorn/middleware/debug.py", line 96, in __call__
        raise exc from None
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/uvicorn/middleware/debug.py", line 93, in __call__
        await self.app(scope, receive, inner_send)
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/fastapi/applications.py", line 208, in __call__
        await super().__call__(scope, receive, send)
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/starlette/applications.py", line 112, in __call__
        await self.middleware_stack(scope, receive, send)
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/starlette/middleware/errors.py", line 181, in __call__
        raise exc
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/starlette/middleware/errors.py", line 159, in __call__
        await self.app(scope, receive, _send)
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/starlette/exceptions.py", line 82, in __call__
        raise exc
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/starlette/exceptions.py", line 71, in __call__
        await self.app(scope, receive, sender)
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/starlette/routing.py", line 656, in __call__
        await route.handle(scope, receive, send)
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/starlette/routing.py", line 259, in handle
        await self.app(scope, receive, send)
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/starlette/routing.py", line 61, in app
        response = await func(request)
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/fastapi/routing.py", line 226, in app
        raw_response = await run_endpoint_function(
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/fastapi/routing.py", line 159, in run_endpoint_function
        return await dependant.call(**values)
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/server/src/main.py", line 55, in authorise_user
        user_db = await User.objects.create(
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/orm/models.py", line 412, in create
        instance.pk = await self.database.execute(expr)
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/databases/core.py", line 169, in execute
        return await connection.execute(query, values)
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/databases/core.py", line 295, in execute
        return await self._connection.execute(built_query)
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/databases/backends/sqlite.py", line 128, in execute
        await cursor.execute(query_str, args)
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/aiosqlite/cursor.py", line 37, in execute
        await self._execute(self._cursor.execute, sql, parameters)
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/aiosqlite/cursor.py", line 31, in _execute
        return await self._conn._execute(fn, *args, **kwargs)
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/aiosqlite/core.py", line 129, in _execute
        return await future
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/aiosqlite/core.py", line 102, in run
        result = function()
    sqlite3.IntegrityError: NOT NULL constraint failed: users.id
    
    From debug logs (via `logging` module):
    DEBUG:databases:Query: INSERT INTO users (access_token, refresh_token, expires, session, bio) VALUES (?, ?, ?, ?, ?) Args: ('redacted (access token)', 'redacted (refresh token)', '2021-12-27 14:32:28.747869', 'session token', 'Default bio. Go to user settings -> bio to change.')
    DEBUG:aiosqlite:executing functools.partial(<built-in method cursor of sqlite3.Connection object at 0x7f4984848b70>)
    DEBUG:aiosqlite:operation functools.partial(<built-in method cursor of sqlite3.Connection object at 0x7f4984848b70>) completed
    DEBUG:aiosqlite:executing functools.partial(<built-in method execute of sqlite3.Cursor object at 0x7f49850cbab0>, 'INSERT INTO users (access_token, refresh_token, expires, session, bio) VALUES (?, ?, ?, ?, ?)', ['access token', 'refresh token', '2021-12-27 14:32:28.747869', 'session token', 'Default bio. Go to user settings -> bio to change.'])
    DEBUG:aiosqlite:returning exception NOT NULL constraint failed: users.id
    
    Traceback from web debugger
    500 Server Error
    Traceback (most recent call last):
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/starlette/middleware/errors.py", line 159, in __call__
        await self.app(scope, receive, _send)
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/starlette/exceptions.py", line 82, in __call__
        raise exc
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/starlette/exceptions.py", line 71, in __call__
        await self.app(scope, receive, sender)
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/starlette/routing.py", line 656, in __call__
        await route.handle(scope, receive, send)
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/starlette/routing.py", line 259, in handle
        await self.app(scope, receive, send)
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/starlette/routing.py", line 61, in app
        response = await func(request)
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/fastapi/routing.py", line 226, in app
        raw_response = await run_endpoint_function(
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/fastapi/routing.py", line 159, in run_endpoint_function
        return await dependant.call(**values)
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/server/src/main.py", line 55, in authorise_user
        user_db = await User.objects.create(
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/orm/models.py", line 412, in create
        instance.pk = await self.database.execute(expr)
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/databases/core.py", line 169, in execute
        return await connection.execute(query, values)
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/databases/core.py", line 295, in execute
        return await self._connection.execute(built_query)
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/databases/backends/sqlite.py", line 128, in execute
        await cursor.execute(query_str, args)
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/aiosqlite/cursor.py", line 37, in execute
        await self._execute(self._cursor.execute, sql, parameters)
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/aiosqlite/cursor.py", line 31, in _execute
        return await self._conn._execute(fn, *args, **kwargs)
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/aiosqlite/core.py", line 129, in _execute
        return await future
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/aiosqlite/core.py", line 102, in run
        result = function()
    sqlite3.IntegrityError: NOT NULL constraint failed: users.id
    
    During handling of the above exception, another exception occurred:
    
    Traceback (most recent call last):
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/uvicorn/middleware/debug.py", line 93, in __call__
        await self.app(scope, receive, inner_send)
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/fastapi/applications.py", line 208, in __call__
        await super().__call__(scope, receive, send)
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/starlette/applications.py", line 112, in __call__
        await self.middleware_stack(scope, receive, send)
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/starlette/middleware/errors.py", line 165, in __call__
        response = self.debug_response(request, exc)
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/starlette/middleware/errors.py", line 245, in debug_response
        content = self.generate_html(exc)
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/starlette/middleware/errors.py", line 217, in generate_html
        traceback_obj = traceback.TracebackException.from_exception(
      File "/home/nexus/.pyenv/versions/3.9.7/lib/python3.9/traceback.py", line 538, in from_exception
        return cls(type(exc), exc, exc.__traceback__, *args, **kwargs)
      File "/home/nexus/.pyenv/versions/3.9.7/lib/python3.9/traceback.py", line 517, in __init__
        self.stack = StackSummary.extract(
      File "/home/nexus/.pyenv/versions/3.9.7/lib/python3.9/traceback.py", line 359, in extract
        result.append(FrameSummary(
      File "/home/nexus/.pyenv/versions/3.9.7/lib/python3.9/traceback.py", line 260, in __init__
        self.locals = {k: repr(v) for k, v in locals.items()} if locals else None
      File "/home/nexus/.pyenv/versions/3.9.7/lib/python3.9/traceback.py", line 260, in <dictcomp>
        self.locals = {k: repr(v) for k, v in locals.items()} if locals else None
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/orm/models.py", line 493, in __repr__
        return f"<{self.__class__.__name__}: {self}>"
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/orm/models.py", line 496, in __str__
        return f"{self.__class__.__name__}({self.pkname}={self.pk})"
      File "/home/nexus/PycharmProjects/Unnamed-bot-List/venv/lib/python3.9/site-packages/orm/models.py", line 486, in pk
        return getattr(self, self.pkname)
    AttributeError: 'User' object has no attribute 'id'
    

    Environment

    • OS: Manjaro (Linux (me)-laptop 5.14.21-2-MANJARO #1 SMP PREEMPT Sun Nov 21 22:43:47 UTC 2021 x86_64 GNU/Linux)
    • Python version: Python 3.9.7, no optimisations, installed via pyenv
    • ORM version: master commit (0.3.1)
    opened by EEKIM10 13
  • AttributeError: module 'orm' has no attribute 'ModelRegistry'

    AttributeError: module 'orm' has no attribute 'ModelRegistry'

    To replicate,

    from fastapi import FastAPI
    import orm
    import databases
    
    app = FastAPI()
    
    database = databases.Database("sqlite:///db.sqlite")
    models = orm.ModelRegistry(database=database)
    
    
    class Note(orm.Model):
        tablename = "notes"
        registry = models
        fields = {
            "id": orm.Integer(primary_key=True),
            "text": orm.String(max_length=100),
            "completed": orm.Boolean(default=False),
        }
    
    @app.get("/")
    async def index():
    	await models.create_all()
    	await Note.objects.create(text="Buy the groceries.", completed=False)
    	note = await Note.objects.get(id=1)
    	print(note)
    	return {}
    
    opened by henshalb 9
  • Unexpected model create behaviour for non-integer primary keys

    Unexpected model create behaviour for non-integer primary keys

    When primary key is i.e. String, model instance returned from model.object.create has primary key as sequential integer. Once instance is fetched again, primary key is as expected - type and value wise. Postgres behaviour is similar. Returned instance primary key in this case is actually None. Same for UUID type. Once fetched again, everything is as expected.

    Could be that problem originates from databases library or incorrect usage of it.

    Two copy/paste tests.

    import random
    
    import databases
    import pytest
    import sqlalchemy
    
    import orm
    from tests.settings import DATABASE_URL
    from tests.test_columns import async_adapter
    
    database = databases.Database(DATABASE_URL, force_rollback=True)
    metadata = sqlalchemy.MetaData()
    
    
    def key():
        return "".join(random.choice("abcdefgh123456") for _ in range(8))
    
    
    class Model(orm.Model):
        __tablename__ = "model"
        __metadata__ = metadata
        __database__ = database
    
        id = orm.String(primary_key=True, default=key, max_length=8)
        name = orm.String(max_length=32)
    
    
    @pytest.fixture(autouse=True, scope="function")
    def create_test_database():
        engine = sqlalchemy.create_engine(DATABASE_URL)
        metadata.create_all(engine)
        yield
        metadata.drop_all(engine)
    
    
    @async_adapter
    async def test_pk_1():
        model = await Model.objects.create(name="NAME")
        assert isinstance(model.id, str)
    
    
    @async_adapter
    async def test_pk_2():
        model = await Model.objects.create(name="NAME")
        assert await Model.objects.all() == [model]
    
    opened by ambrozic 7
  • Add '.exclude()' method

    Add '.exclude()' method

    I want to select rows with a query where the values are not None when I've set a field as allow_null=True. It's very possible that I'm missing something and that this can be achieved in another way.

    opened by rick-pri 6
  • Alembic migrations don't work out of the box

    Alembic migrations don't work out of the box

    Checklist

    • [x] The bug is reproducible against the latest release and/or master.
    • [x] There are no similar issues or pull requests to fix it yet.

    Describe the bug

    The documentation says "Because ORM is built on SQLAlchemy core, you can use Alembic to provide database migrations.". However, running Alembic with the appropriate configuration (i.e., replacing the connection string with a non-async driver) produces empty migrations.

    In order for Alembic to produce actual migrations, one can run MyModelName.build_table() on all of the tables you want to produce migrations for. I actually don't know if this is the "correct" way to do this, but the docs have no information and I came up empty searching for the source code for any clues.

    Diving through the source code, I noticed that the build_table method on Model was the only place metadata was being mutated (via the sqlalchemy.Table class constructor), so I tried calling this class method, and lo-and-behold, it worked!

    To reproduce

    I can provide a repo if needed, since this could be a few minutes of work.

    Expected behavior

    Alembic works out of the box with encode/orm.

    Actual behavior

    Alembic generates empty migrations.

    Environment

    • OS: macOS 12.4 (21F79)
    • Python version: Python 3.10.5
    • ORM version: 0.3.1
    opened by dlo 5
  • Model `__repr__` and `__str__` methods

    Model `__repr__` and `__str__` methods

    Right now the Model class does not provide the __str__ and __repr__ methods.

    These methods need to be implemented, and updated in docs (as they are not accurate) right now.

    I think maybe not with all the fields need to be added, maybe only use PK same as Django does.

    enhancement 
    opened by aminalaee 5
  • Feature Request: save() method for models

    Feature Request: save() method for models

    Problem: If I create new model and want to save it - I need to use await ModelClass.objects.create(column_name=model.column_name, ...).

    Solution: Just call something like model.save() the same way model.update() is used right now. Also model.save() can fallback to model.update() if id is set.

    Maybe I am missing something and there is another easy and clean way to add a new row, I am ready to use it or implement one.

    opened by HarrySky 5
  • TypeError: 'modelname' object is not subscriptable

    TypeError: 'modelname' object is not subscriptable

    Checklist

    • [x] The bug is reproducible against the latest release and/or master.
    • [x] There are no similar issues or pull requests to fix it yet.

    Describe the bug

    can't read the content of the model

    To reproduce

    try the quickstart of the doc

    import databases
    import orm
    import asyncio
    
    database = databases.Database("sqlite:///db.sqlite")
    models = orm.ModelRegistry(database=database)
    
    
    class Note(orm.Model):
        tablename = "notes"
        registry = models
        fields = {
            "id": orm.Integer(primary_key=True),
            "text": orm.String(max_length=100),
            "completed": orm.Boolean(default=False),
        }
    
    # Create the database and tables
    # models.create_all()
    
    async def get_all():
        data = await Note.objects.get(id=1)
        print(data)
        data = await Note.objects.all()
        print(data)
        for result in data:
            print(result['id'])
    
    asyncio.run(get_all())
    
    11:58 $ python models_test.py 
    <__main__.Note object at 0x7f7dcdfc51f0>
    [<__main__.Note object at 0x7f7dcdfc5760>, <__main__.Note object at 0x7f7dcdfc5820>]
    Traceback (most recent call last):
      File "models_test.py", line 29, in <module>
        asyncio.run(get_all())
      File "/usr/lib/python3.8/asyncio/runners.py", line 44, in run
        return loop.run_until_complete(main)
      File "/usr/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete
        return future.result()
      File "models_test.py", line 27, in get_all
        print(result['id'])
    TypeError: 'Note' object is not subscriptable
    

    Expected behavior

    I would like ORM works as before :)

    Actual behavior

    return object instead of data

    the doc says

    note = await Note.objects.get(id=1)
    print(note)
    # Note(id=1, text="Buy the groceries.", completed=False)
    

    but I get

        data = await Trigger.objects.get(id=1)
        print(data)
    <yeoboseyo.models.Trigger object at 0x7f997488bac0>
    

    so then getting .all()

        data = await Trigger.objects.all()
        print(data)
        for result in data:
            print(result['id'])
    
    [<yeoboseyo.models.Trigger object at 0x7f997488bac0>]
        print(result['id'])
    TypeError: 'Trigger' object is not subscriptable
    
    

    Debugging material

    (yeoboseyo) ✔ ~/Projects/yeoboseyo/yeoboseyo/yeoboseyo [master|✚ 6…11] 11:35 $ python app.py 여보세요 ! INFO: Started server process [25851] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

    INFO: 127.0.0.1:48144 - "GET / HTTP/1.1" 200 OK INFO: 127.0.0.1:48144 - "GET /static/call-me-hand.png HTTP/1.1" 304 Not Modified <yeoboseyo.models.Trigger object at 0x7f6e2b64dee0> INFO: 127.0.0.1:48144 - "GET /api/yeoboseyo/ HTTP/1.1" 500 Internal Server Error ERROR: Exception in ASGI application Traceback (most recent call last): File "/home/foxmask/Projects/yeoboseyo/lib/python3.8/site-packages/uvicorn/protocols/http/h11_impl.py", line 373, in run_asgi result = await app(self.scope, self.receive, self.send) File "/home/foxmask/Projects/yeoboseyo/lib/python3.8/site-packages/uvicorn/middleware/proxy_headers.py", line 75, in call return await self.app(scope, receive, send) File "/home/foxmask/Projects/yeoboseyo/lib/python3.8/site-packages/starlette/applications.py", line 112, in call await self.middleware_stack(scope, receive, send) File "/home/foxmask/Projects/yeoboseyo/lib/python3.8/site-packages/starlette/middleware/errors.py", line 181, in call raise exc File "/home/foxmask/Projects/yeoboseyo/lib/python3.8/site-packages/starlette/middleware/errors.py", line 159, in call await self.app(scope, receive, _send) File "/home/foxmask/Projects/yeoboseyo/lib/python3.8/site-packages/starlette/exceptions.py", line 82, in call raise exc File "/home/foxmask/Projects/yeoboseyo/lib/python3.8/site-packages/starlette/exceptions.py", line 71, in call await self.app(scope, receive, sender) File "/home/foxmask/Projects/yeoboseyo/lib/python3.8/site-packages/starlette/routing.py", line 656, in call await route.handle(scope, receive, send) File "/home/foxmask/Projects/yeoboseyo/lib/python3.8/site-packages/starlette/routing.py", line 408, in handle await self.app(scope, receive, send) File "/home/foxmask/Projects/yeoboseyo/lib/python3.8/site-packages/starlette/routing.py", line 656, in call await route.handle(scope, receive, send) File "/home/foxmask/Projects/yeoboseyo/lib/python3.8/site-packages/starlette/routing.py", line 408, in handle await self.app(scope, receive, send) File "/home/foxmask/Projects/yeoboseyo/lib/python3.8/site-packages/starlette/routing.py", line 656, in call await route.handle(scope, receive, send) File "/home/foxmask/Projects/yeoboseyo/lib/python3.8/site-packages/starlette/routing.py", line 259, in handle await self.app(scope, receive, send) File "/home/foxmask/Projects/yeoboseyo/lib/python3.8/site-packages/starlette/routing.py", line 61, in app response = await func(request) File "app.py", line 43, in get_all for result in data: TypeError: 'Trigger' object is not iterable

    Environment

    • OS: Linux
    • Python 3.8.10
    • ORM version: 0.2.1

    Additional context

    the same code worked in 0.1.8 (https://framagit.org/annyong/yeoboseyo/-/blob/master/yeoboseyo/models.py)

    question 
    opened by foxmask 4
  • Add QuerySet-level method: bulk_create

    Add QuerySet-level method: bulk_create

    I need an efficient way to add records to a db in bulk. I don't think it matters so much what the API looks like for accomplishing that. If there is an efficient way to do that already, even if it's not a QS-level method, please let me know.

    Reference: https://docs.djangoproject.com/en/3.0/ref/models/querysets/#django.db.models.query.QuerySet.bulk_create

    opened by malikoth 4
  • Can you add something like comment in Sqlalchemy to each field?

    Can you add something like comment in Sqlalchemy to each field?

    Hi, I have read the official document, it seems that there is no function similar to this parameter in Field, can you add it? like this

    class User(orm.Model):
        tablename = "user"
        registry = models
        fields = {
            "id": orm.Integer(primary_key=True),
            "name": orm.String(max_length=100, comment="Student Name"),
        }
    
    opened by iasukas 0
  • Fix pk read only: now primary key can be written as a custom value

    Fix pk read only: now primary key can be written as a custom value

    Now primary key isn't a read only field. And if user tries to create a new model with non-integer (for example, UUID) field without default value, ValueError will be raised.

    opened by artemowkin 0
  • How to support Model Inheritance?

    How to support Model Inheritance?

    Hi According to the defining structure of columns in attribute fields, how would supporting the inheritance be? Because by redefining attribute fields, parent's attribute fields will override and we will loose all our former data. We could define a field under a name and determine what class it would inherit from! But for supporting the models we need to actually do the inheritance; so it would duplicate our tasks.

    opened by SepehrBazyar 2
  • How to support 'unique_together'

    How to support 'unique_together'

    class RoleMenu(orm.Model): tablename = "role_menu" registry = registry_main

    fields = {
        "id": orm.BigInteger(primary_key=True),
        "role_id": orm.BigInteger(index=True),
        "menu_id": orm.BigInteger(index=True),
        "is_active": orm.Boolean(default=False),
        "is_new": orm.Boolean(default=True),
    }
    # unique_together = (("role_id", "menu_id"),)
    
    opened by LuoLuo0101 1
Releases(0.3.1)
  • 0.3.1(Nov 29, 2021)

    Added

    • SQLAlchemy filter operators https://github.com/encode/orm/pull/130

    Fixed

    • Change private methods to internal https://github.com/encode/orm/pull/133
    • Change create_all and drop_all to async https://github.com/encode/orm/pull/135

    Full Changelog: https://github.com/encode/orm/compare/0.3.0...0.3.1

    Source code(tar.gz)
    Source code(zip)
  • 0.3.0(Nov 2, 2021)

    Added

    • Support for ON DELETE actions (#115)
    • OneToOne field (#116)
    • Email field (#123)
    • IPAddress field (#124)
    • Model class __repr__ and __str__ methods (#127)
    • URL field (#128)

    Fixed

    • create method not working for non-integer Primary Keys (#119)
    Source code(tar.gz)
    Source code(zip)
  • 0.2.1(Sep 28, 2021)

  • 0.2.0(Sep 17, 2021)

    Version 0.2.0 depends on typesystem>=0.3.0 for validation. This release changes how model fields are defined, now they are defined as a dict in the fieldsattribute of the model.

    import databases
    import orm
    
    
    database = databases.Database("sqlite:///db.sqlite")
    models = orm.ModelRegistry(database=database)
    
    
    class Note(orm.Model):
        tablename = "notes"
        registry = models
        fields = {
            "id": orm.Integer(primary_key=True),
            "text": orm.String(max_length=100),
            "completed": orm.Boolean(default=False),
        }
    

    There's no need for sync database drivers like psycopg2 for creating and destorying tables. ModelRegistry can do that now:

    models.create_all()
    
    models.drop_all()
    
    Source code(tar.gz)
    Source code(zip)
  • 0.1.9(Sep 11, 2021)

  • 0.1.8(Sep 3, 2021)

  • 0.1.7(Aug 25, 2021)

  • 0.1.6(Aug 16, 2021)

Owner
Encode
Collaboratively funded software development.
Encode
Rich Python data types for Redis

Created by Stephen McDonald Introduction HOT Redis is a wrapper library for the redis-py client. Rather than calling the Redis commands directly from

Stephen McDonald 281 Nov 10, 2022
An async ORM. 🗃

ORM The orm package is an async ORM for Python, with support for Postgres, MySQL, and SQLite. ORM is built with: SQLAlchemy core for query building. d

Encode 1.7k Dec 28, 2022
Twisted wrapper for asynchronous PostgreSQL connections

This is txpostgres is a library for accessing a PostgreSQL database from the Twisted framework. It builds upon asynchronous features of the Psycopg da

Jan Urbański 104 Apr 22, 2022
Global base classes for Pyramid SQLAlchemy applications.

pyramid_basemodel pyramid_basemodel is a thin, low level package that provides an SQLAlchemy declarative Base and a thread local scoped Session that c

Grzegorz Śliwiński 15 Jan 03, 2023
SQLAlchemy support for aiohttp.

aiohttp-sqlalchemy SQLAlchemy 1.4 / 2.0 support for AIOHTTP. The library provides the next features: initializing asynchronous sessions through a midd

Ruslan Ilyasovich Gilfanov 5 Dec 11, 2022
The Python SQL Toolkit and Object Relational Mapper

SQLAlchemy The Python SQL Toolkit and Object Relational Mapper Introduction SQLAlchemy is the Python SQL toolkit and Object Relational Mapper that giv

mike bayer 3.5k Dec 29, 2022
Pony Object Relational Mapper

Downloads Pony Object-Relational Mapper Pony is an advanced object-relational mapper. The most interesting feature of Pony is its ability to write que

3.1k Jan 01, 2023
Sqlalchemy seeder that supports nested relationships.

sqlalchemyseed Sqlalchemy seeder that supports nested relationships. Supported file types json yaml csv Installation Default installation pip install

Jedy Matt Tabasco 10 Aug 13, 2022
Easy-to-use data handling for SQL data stores with support for implicit table creation, bulk loading, and transactions.

dataset: databases for lazy people In short, dataset makes reading and writing data in databases as simple as reading and writing JSON files. Read the

Friedrich Lindenberg 4.2k Dec 26, 2022
The ormar package is an async mini ORM for Python, with support for Postgres, MySQL, and SQLite.

python async mini orm with fastapi in mind and pydantic validation

1.2k Jan 05, 2023
Solrorm : A sort-of solr ORM for python

solrorm : A sort-of solr ORM for python solrpy - deprecated solrorm - currently in dev Usage Cores The first step to interact with solr using solrorm

Aj 1 Nov 21, 2021
MongoEngine flask extension with WTF model forms support

Flask-MongoEngine Info: MongoEngine for Flask web applications. Repository: https://github.com/MongoEngine/flask-mongoengine About Flask-MongoEngine i

MongoEngine 815 Jan 03, 2023
A very simple CRUD class for SQLModel! ✨

Base SQLModel A very simple CRUD class for SQLModel! ✨ Inspired on: Full Stack FastAPI and PostgreSQL - Base Project Generator FastAPI Microservices I

Marcelo Trylesinski 40 Dec 14, 2022
Python helpers for using SQLAlchemy with Tornado.

tornado-sqlalchemy Python helpers for using SQLAlchemy with Tornado. Installation $ pip install tornado-sqlalchemy In case you prefer installing from

Siddhant Goel 122 Aug 23, 2022
Adds SQLAlchemy support to Flask

Flask-SQLAlchemy Flask-SQLAlchemy is an extension for Flask that adds support for SQLAlchemy to your application. It aims to simplify using SQLAlchemy

The Pallets Projects 3.9k Jan 09, 2023
Beanie - is an Asynchronous Python object-document mapper (ODM) for MongoDB

Beanie - is an Asynchronous Python object-document mapper (ODM) for MongoDB, based on Motor and Pydantic.

Roman 993 Jan 03, 2023
A curated list of awesome tools for SQLAlchemy

Awesome SQLAlchemy A curated list of awesome extra libraries and resources for SQLAlchemy. Inspired by awesome-python. (See also other awesome lists!)

Hong Minhee (洪 民憙) 2.5k Dec 31, 2022
a small, expressive orm -- supports postgresql, mysql and sqlite

peewee Peewee is a simple and small ORM. It has few (but expressive) concepts, making it easy to learn and intuitive to use. a small, expressive ORM p

Charles Leifer 9.7k Jan 08, 2023
A pythonic interface to Amazon's DynamoDB

PynamoDB A Pythonic interface for Amazon's DynamoDB. DynamoDB is a great NoSQL service provided by Amazon, but the API is verbose. PynamoDB presents y

2.1k Dec 30, 2022
Pydantic model support for Django ORM

Pydantic model support for Django ORM

Jordan Eremieff 318 Jan 03, 2023