The Orator ORM provides a simple yet beautiful ActiveRecord implementation.

Related tags

python orm database
Overview

Orator

Orator Build status

The Orator ORM provides a simple yet beautiful ActiveRecord implementation.

It is inspired by the database part of the Laravel framework, but largely modified to be more pythonic.

The full documentation is available here: http://orator-orm.com/docs

Installation

You can install Orator in 2 different ways:

  • The easier and more straightforward is to use pip
pip install orator

The different dbapi packages are not part of the package dependencies, so you must install them in order to connect to corresponding databases:

  • Postgres: psycopg2
  • MySQL: PyMySQL or mysqlclient
  • Sqlite: The sqlite3 module is bundled with Python by default

Basic Usage

Configuration

All you need to get you started is the configuration describing your database connections and passing it to a DatabaseManager instance.

from orator import DatabaseManager, Model

config = {
    'mysql': {
        'driver': 'mysql',
        'host': 'localhost',
        'database': 'database',
        'user': 'root',
        'password': '',
        'prefix': ''
    }
}

db = DatabaseManager(config)
Model.set_connection_resolver(db)

Defining a model

class User(Model):
    pass

Note that we did not tell the ORM which table to use for the User model. The plural "snake case" name of the class name will be used as the table name unless another name is explicitly specified. In this case, the ORM will assume the User model stores records in the users table. You can specify a custom table by defining a __table__ property on your model:

class User(Model):

    __table__ = 'my_users'

The ORM will also assume that each table has a primary key column named id. You can define a __primary_key__ property to override this convention. Likewise, you can define a __connection__ property to override the name of the database connection that should be used when using the model.

Once a model is defined, you are ready to start retrieving and creating records in your table. Note that you will need to place updated_at and created_at columns on your table by default. If you do not wish to have these columns automatically maintained, set the __timestamps__ property on your model to False.

Retrieving all models

users = User.all()

Retrieving a record by primary key

user = User.find(1)

print(user.name)

Querying using models

users = User.where('votes', '>', 100).take(10).get()

for user in users:
    print(user.name)

Aggregates

You can also use the query builder aggregate functions:

count = User.where('votes', '>', 100).count()

If you feel limited by the builder's fluent interface, you can use the where_raw method:

users = User.where_raw('age > ? and votes = 100', [25]).get()

Chunking Results

If you need to process a lot of records, you can use the chunk method to avoid consuming a lot of RAM:

for users in User.chunk(100):
    for user in users:
        # ...

Specifying the query connection

You can specify which database connection to use when querying a model by using the on method:

user = User.on('connection-name').find(1)

If you are using read / write connections, you can force the query to use the "write" connection with the following method:

user = User.on_write_connection().find(1)

Mass assignment

When creating a new model, you pass attributes to the model constructor. These attributes are then assigned to the model via mass-assignment. Though convenient, this can be a serious security concern when passing user input into a model, since the user is then free to modify any and all of the model's attributes. For this reason, all models protect against mass-assignment by default.

To get started, set the __fillable__ or __guarded__ properties on your model.

Defining fillable attributes on a model

The __fillable__ property specifies which attributes can be mass-assigned.

class User(Model):

    __fillable__ = ['first_name', 'last_name', 'email']

Defining guarded attributes on a model

The __guarded__ is the inverse and acts as "blacklist".

class User(Model):

    __guarded__ = ['id', 'password']

You can also block all attributes from mass-assignment:

__guarded__ = ['*']

Insert, update and delete

Saving a new model

To create a new record in the database, simply create a new model instance and call the save method.

user = User()

user.name = 'John'

user.save()

You can also use the create method to save a model in a single line, but you will need to specify either the __fillable__ or __guarded__ property on the model since all models are protected against mass-assignment by default.

After saving or creating a new model with auto-incrementing IDs, you can retrieve the ID by accessing the object's id attribute:

inserted_id = user.id

Using the create method

# Create a new user in the database
user = User.create(name='John')

# Retrieve the user by attributes, or create it if it does not exist
user = User.first_or_create(name='John')

# Retrieve the user by attributes, or instantiate it if it does not exist
user = User.first_or_new(name='John')

Updating a retrieved model

user = User.find(1)

user.name = 'Foo'

user.save()

You can also run updates as queries against a set of models:

affected_rows = User.where('votes', '>', 100).update(status=2)

Deleting an existing model

To delete a model, simply call the delete model:

user = User.find(1)

user.delete()

Deleting an existing model by key

User.destroy(1)

User.destroy(1, 2, 3)

You can also run a delete query on a set of models:

affected_rows = User.where('votes', '>' 100).delete()

Updating only the model's timestamps

If you want to only update the timestamps on a model, you can use the touch method:

user.touch()

Timestamps

By default, the ORM will maintain the created_at and updated_at columns on your database table automatically. Simply add these timestamp columns to your table. If you do not wish for the ORM to maintain these columns, just add the __timestamps__ property:

class User(Model):

    __timestamps__ = False

Providing a custom timestamp format

If you wish to customize the format of your timestamps (the default is the ISO Format) that will be returned when using the to_dict or the to_json methods, you can override the get_date_format method:

class User(Model):

    def get_date_format():
        return 'DD-MM-YY'

Converting to dictionaries / JSON

Converting a model to a dictionary

When building JSON APIs, you may often need to convert your models and relationships to dictionaries or JSON. So, Orator includes methods for doing so. To convert a model and its loaded relationship to a dictionary, you may use the to_dict method:

user = User.with_('roles').first()

return user.to_dict()

Note that entire collections of models can also be converted to dictionaries:

return User.all().serailize()

Converting a model to JSON

To convert a model to JSON, you can use the to_json method!

return User.find(1).to_json()

Query Builder

Introduction

The database query builder provides a fluent interface to create and run database queries. It can be used to perform most database operations in your application, and works on all supported database systems.

Selects

Retrieving all row from a table

users = db.table('users').get()

for user in users:
    print(user['name'])

Chunking results from a table

for users in db.table('users').chunk(100):
    for user in users:
        # ...

Retrieving a single row from a table

user = db.table('users').where('name', 'John').first()
print(user['name'])

Retrieving a single column from a row

user = db.table('users').where('name', 'John').pluck('name')

Retrieving a list of column values

roles = db.table('roles').lists('title')

This method will return a list of role titles. It can return a dictionary if you pass an extra key parameter.

roles = db.table('roles').lists('title', 'name')

Specifying a select clause

users = db.table('users').select('name', 'email').get()

users = db.table('users').distinct().get()

users = db.table('users').select('name as user_name').get()

Adding a select clause to an existing query

query = db.table('users').select('name')

users = query.add_select('age').get()

Using where operators

users = db.table('users').where('age', '>', 25).get()

Or statements

users = db.table('users').where('age', '>', 25).or_where('name', 'John').get()

Using Where Between

users = db.table('users').where_between('age', [25, 35]).get()

Using Where Not Between

users = db.table('users').where_not_between('age', [25, 35]).get()

Using Where In

users = db.table('users').where_in('id', [1, 2, 3]).get()

users = db.table('users').where_not_in('id', [1, 2, 3]).get()

Using Where Null to find records with null values

users = db.table('users').where_null('updated_at').get()

Order by, group by and having

query = db.table('users').order_by('name', 'desc')
query = query.group_by('count')
query = query.having('count', '>', 100)

users = query.get()

Offset and limit

users = db.table('users').skip(10).take(5).get()

users = db.table('users').offset(10).limit(5).get()

Joins

The query builder can also be used to write join statements.

Basic join statement

db.table('users') \
    .join('contacts', 'users.id', '=', 'contacts.user_id') \
    .join('orders', 'users.id', '=', 'orders.user_id') \
    .select('users.id', 'contacts.phone', 'orders.price') \
    .get()

Left join statement

db.table('users').left_join('posts', 'users.id', '=', 'posts.user_id').get()

You can also specify more advance join clauses:

clause = JoinClause('contacts').on('users.id', '=', 'contacts.user_id').or_on(...)

db.table('users').join(clause).get()

If you would like to use a "where" style clause on your joins, you may use the where and or_where methods on a join. Instead of comparing two columns, these methods will compare the column against a value:

clause = JoinClause('contacts').on('users.id', '=', 'contacts.user_id').where('contacts.user_id', '>', 5)

db.table('users').join(clause).get()

Advanced where

Sometimes you may need to create more advanced where clauses such as "where exists" or nested parameter groupings. It is pretty easy to do with the Orator query builder

Parameter grouping

db.table('users') \
    .where('name', '=', 'John') \
    .or_where(
        db.query().where('votes', '>', 100).where('title', '!=', 'admin')
    ).get()

The query above will produce the following SQL:

SELECT * FROM users WHERE name = 'John' OR (votes > 100 AND title != 'Admin')

Exists statement

db.table('users').where_exists(
    db.table('orders').select(db.raw(1)).where_raw('order.user_id = users.id')
)

The query above will produce the following SQL:

SELECT * FROM users
WHERE EXISTS (
    SELECT 1 FROM orders WHERE orders.user_id = users.id
)

Aggregates

The query builder also provides a variety of aggregate methods, ` such as count, max, min, avg, and sum.

users = db.table('users').count()

price = db.table('orders').max('price')

price = db.table('orders').min('price')

price = db.table('orders').avg('price')

total = db.table('users').sum('votes')

Raw expressions

Sometimes you may need to use a raw expression in a query. These expressions will be injected into the query as strings, so be careful not to create any SQL injection points! To create a raw expression, you may use the raw() method:

db.table('users') \
    .select(db.raw('count(*) as user_count, status')) \
    .where('status', '!=', 1) \
    .group_by('status') \
    .get()

Inserts

Insert records into a table

db.table('users').insert(email='[email protected]', votes=0)

db.table('users').insert({
    'email': '[email protected]',
    'votes': 0
})

It is important to note that there is two notations available. The reason is quite simple: the dictionary notation, though a little less practical, is here to handle columns names which cannot be passed as keywords arguments.

Inserting records into a table with an auto-incrementing ID

If the table has an auto-incrementing id, use insert_get_id to insert a record and retrieve the id:

id = db.table('users').insert_get_id({
    'email': '[email protected]',
    'votes': 0
})

Inserting multiple record into a table

db.table('users').insert([
    {'email': '[email protected]', 'votes': 0},
    {'email': '[email protected]', 'votes': 0}
])

Updates

Updating records

db.table('users').where('id', 1).update(votes=1)

db.table('users').where('id', 1).update({'votes': 1})

Like the insert statement, there is two notations available. The reason is quite simple: the dictionary notation, though a little less practical, is here to handle columns names which cannot be passed as keywords arguments.

Incrementing or decrementing the value of a column

db.table('users').increment('votes')  # Increment the value by 1

db.table('users').increment('votes', 5)  # Increment the value by 5

db.table('users').decrement('votes')  # Decrement the value by 1

db.table('users').decrement('votes', 5)  # Decrement the value by 5

You can also specify additional columns to update:

db.table('users').increment('votes', 1, name='John')

Deletes

Deleting records

db.table('users').where('age', '<', 25).delete()

Delete all records

db.table('users').delete()

Truncate

db.table('users').truncate()

Unions

The query builder provides a quick and easy way to "union" two queries:

first = db.table('users').where_null('first_name')

users = db.table('users').where_null('last_name').union(first).get()

The union_all method is also available.

Read / Write connections

Sometimes you may wish to use one database connection for SELECT statements, and another for INSERT, UPDATE, and DELETE statements. Orator makes this easy, and the proper connections will always be used whether you use raw queries, the query builder or the actual ORM

Here is an example of how read / write connections should be configured:

config = {
    'mysql': {
        'read': {
            'host': '192.168.1.1'
        },
        'write': {
            'host': '192.168.1.2'
        },
        'driver': 'mysql',
        'database': 'database',
        'user': 'root',
        'password': '',
        'prefix': ''
    }
}

Note that two keys have been added to the configuration dictionary: read and write. Both of these keys have dictionary values containing a single key: host. The rest of the database options for the read and write connections will be merged from the main mysql dictionary. So, you only need to place items in the read and write dictionaries if you wish to override the values in the main dictionary. So, in this case, 192.168.1.1 will be used as the "read" connection, while 192.168.1.2 will be used as the "write" connection. The database credentials, prefix, character set, and all other options in the main mysql dictionary will be shared across both connections.

Database transactions

To run a set of operations within a database transaction, you can use the transaction method which is a context manager:

with db.transaction():
    db.table('users').update({votes: 1})
    db.table('posts').delete()

Note

Any exception thrown within a transaction block will cause the transaction to be rolled back automatically.

Sometimes you may need to start a transaction yourself:

db.begin_transaction()

You can rollback a transaction with the rollback method:

db.rollback()

You can also commit a transaction via the commit method:

db.commit()

By default, all underlying DBAPI connections are set to be in autocommit mode meaning that you don't need to explicitly commit after each operation.

Accessing connections

When using multiple connections, you can access them via the connection() method:

users = db.connection('foo').table('users').get()

You also can access the raw, underlying dbapi connection instance:

db.connection().get_connection()

Sometimes, you may need to reconnect to a given database:

db.reconnect('foo')

If you need to disconnect from the given database, use the disconnect method:

db.disconnect('foo')
Issues
  • MemoryError: stack overflow when I try to query a relation

    MemoryError: stack overflow when I try to query a relation

    I am running orator on windows/ python 2.7, and my model looks like:

    class Area(Model):
        pass
    
    class Restaurant(Model):
    
        @has_one()
        def area(self):
            return Area
    

    when i try to run r = Restaurant.find(1) r.id # works as normal r.area # throws "MemoryError: stack overflow" error

    bug 
    opened by tantita 26
  • If mysql begin a transaction and lost connection, reconnect

    If mysql begin a transaction and lost connection, reconnect

    Fix the following problems:

    Traceback (most recent call last):
      File "orator_db.py", line 97, in <module>
        with db.connection('yh_edu').transaction():
      File "/usr/lib/python3.6/contextlib.py", line 81, in __enter__
        return next(self.gen)
      File "/usr/lib/python3.6/site-packages/orator/connections/connection.py", line 288, in transaction
        self.begin_transaction()
      File "/usr/lib/python3.6/site-packages/orator/connections/mysql_connection.py", line 42, in begin_transaction
        self._connection.autocommit(False)
      File "/usr/lib/python3.6/site-packages/pymysql/connections.py", line 753, in autocommit
        self._send_autocommit_mode()
      File "/usr/lib/python3.6/site-packages/pymysql/connections.py", line 771, in _send_autocommit_mode
        self._read_ok_packet()
      File "/usr/lib/python3.6/site-packages/pymysql/connections.py", line 760, in _read_ok_packet
        pkt = self._read_packet()
      File "/usr/lib/python3.6/site-packages/pymysql/connections.py", line 991, in _read_packet
        packet_header = self._read_bytes(4)
      File "/usr/lib/python3.6/site-packages/pymysql/connections.py", line 1037, in _read_bytes
        CR.CR_SERVER_LOST, "Lost connection to MySQL server during query")
    pymysql.err.OperationalError: (2013, 'Lost connection to MySQL server during query')
    ```
    opened by zhanghaofei 17
  • ColumnDoesNotExist but not actually being referenced

    ColumnDoesNotExist but not actually being referenced

    Hi guys, have a strange issue I'm hoping for some assistance on.

    I have a migration: https://gist.github.com/ninjrdevelop/dd5f2715eeef30aca3308042880729a3

    When I run it, I get the error:

    [ColumnDoesNotExist]
      Column "permission_id" does not exist on table "users".
    

    migrate -vvv: https://gist.github.com/ninjrdevelop/6bb795742e1f736ff38360a3c89faa8a migrate --pretend: https://gist.github.com/ninjrdevelop/b1915ac62ae66f2f3d17052b3e04d864

    Image of table structure attached.

    Any thoughts? There's no reference to permission_id anywhere in this project. vivaldi_jdRDBd1nMR

    opened by ninjrdevelop 13
  • Keys_fix not defined

    Keys_fix not defined

    When I run your start up tutorial I get.

    Traceback (most recent call last): File "test.py", line 19, in <module> print(User.all()) File "/Library/Python/2.7/site-packages/orator/orm/model.py", line 544, in all instance = cls() File "/Library/Python/2.7/site-packages/orator/orm/model.py", line 121, in __init__ self._boot_if_not_booted() File "/Library/Python/2.7/site-packages/orator/orm/model.py", line 148, in _boot_if_not_booted klass._boot() File "/Library/Python/2.7/site-packages/orator/orm/model.py", line 167, in _boot cls._boot_columns() File "/Library/Python/2.7/site-packages/orator/orm/model.py", line 171, in _boot_columns connection = cls.resolve_connection() File "/Library/Python/2.7/site-packages/orator/orm/model.py", line 2758, in resolve_connection return cls.__resolver.connection(connection) File "/Library/Python/2.7/site-packages/orator/database_manager.py", line 43, in connection connection = self._make_connection(name) File "/Library/Python/2.7/site-packages/orator/database_manager.py", line 129, in _make_connection return self._factory.make(config, name) File "/Library/Python/2.7/site-packages/orator/connectors/connection_factory.py", line 36, in make return self._create_single_connection(config) File "/Library/Python/2.7/site-packages/orator/connectors/connection_factory.py", line 39, in _create_single_connection conn = self.create_connector(config).connect(config) File "/Library/Python/2.7/site-packages/orator/connectors/mysql_connector.py", line 31, in connect for key, value in keys_fix.items(): NameError: global name 'keys_fix' is not defined

    Seems like I'm following it correctly. Sorry if this is more tech support than an issue but I couldn't find any similar errors online elsewhere.

    opened by jamesh38 12
  • Incorrect datetime value: '2016-10-13 18:59:57.892599+00:00' for column 'created_at' at row 1

    Incorrect datetime value: '2016-10-13 18:59:57.892599+00:00' for column 'created_at' at row 1")

    Orator-ORM: 0.9.1 Python: 2.7.10 MySQL: 5.7.15

    It happened when I save a model.

    orator.exceptions.query.QueryException: (1292, "Incorrect datetime value: '2016-10-13 18:59:57.892599+00:00' for column 'created_at' at row 1") (SQL: INSERT INTOadmins(created_at,email,updated_at) VALUES (%s, %s, %s) ([<Pendulum [2016-10-13T18:59:57.892599+00:00]>, '[email protected]', <Pendulum [2016-10-13T18:59:57.892599+00:00]>]))

    The migration script:

    from orator.migrations import Migration
    
    
    class CreateAdminsTable(Migration):
    
        def up(self):
            """
            Run the migrations.
            """
            with self.schema.create('admins') as table:
                table.increments('id')
                table.string('email', 64).unique()
                table.json('profile').nullable()
                table.json('token').nullable()
                table.small_integer('status').default(0)
                table.timestamps()
    
        def down(self):
            """
            Revert the migrations.
            """
            self.schema.drop('admins')
    
    

    I guess the question is timezone info (+00:00).

    opened by damozhang 12
  • orator cli doesn't work on windows

    orator cli doesn't work on windows

    I tryed to execute orator make:migration create_example_table on my freshly created virtualenv, but it says that this command can't be executed. I also tryed .\Scripts\orator. The same thing. Steps that I followed:

    1. Created vitual enviroment
    2. Installed orator
    3. Activated my virtual env
    4. Created orator.xml
    5. Run orator make:migration create_example_table
    opened by yos-virtus 12
  • I keep getting orator.exceptions.query.QueryException

    I keep getting orator.exceptions.query.QueryException

    I have started using your awesome ORM on production and I get this error like once everyday which causes that endpoint to return a 502 until I refresh it like several times. Here is the stack-trace:

    Traceback (most recent call last):
      File "/var/www/grub_flask/venv/local/lib/python2.7/site-packages/flask/app.py", line 1836, in __call__
        return self.wsgi_app(environ, start_response)
      File "/var/www/grub_flask/venv/local/lib/python2.7/site-packages/flask/app.py", line 1820, in wsgi_app
        response = self.make_response(self.handle_exception(e))
      File "/var/www/grub_flask/venv/local/lib/python2.7/site-packages/flask/app.py", line 1403, in handle_exception
        reraise(exc_type, exc_value, tb)
      File "/var/www/grub_flask/venv/local/lib/python2.7/site-packages/flask/app.py", line 1817, in wsgi_app
        response = self.full_dispatch_request()
      File "/var/www/grub_flask/venv/local/lib/python2.7/site-packages/flask/app.py", line 1477, in full_dispatch_request
        rv = self.handle_user_exception(e)
      File "/var/www/grub_flask/venv/local/lib/python2.7/site-packages/flask/app.py", line 1381, in handle_user_exception
        reraise(exc_type, exc_value, tb)
      File "/var/www/grub_flask/venv/local/lib/python2.7/site-packages/flask/app.py", line 1475, in full_dispatch_request
        rv = self.dispatch_request()
      File "/var/www/grub_flask/venv/local/lib/python2.7/site-packages/flask/app.py", line 1461, in dispatch_request
        return self.view_functions[rule.endpoint](**req.view_args)
      File "./theapp/controllers.py", line 28, in restaurants
        ress = Restaurant.with_('area', 'foods').get().to_dict()
      File "/var/www/grub_flask/venv/local/lib/python2.7/site-packages/orator/orm/builder.py", line 198, in get
        models = self.get_models(columns)
      File "/var/www/grub_flask/venv/local/lib/python2.7/site-packages/orator/orm/builder.py", line 439, in get_models
        results = self.apply_scopes().get_query().get(columns)
      File "/var/www/grub_flask/venv/local/lib/python2.7/site-packages/orator/query/builder.py", line 1049, in get
        return self.get_fresh(columns)
      File "/var/www/grub_flask/venv/local/lib/python2.7/site-packages/orator/query/builder.py", line 1067, in get_fresh
        return self._processor.process_select(self, self._run_select())
      File "/var/www/grub_flask/venv/local/lib/python2.7/site-packages/orator/query/builder.py", line 1079, in _run_select
        not self._use_write_connection
      File "/var/www/grub_flask/venv/local/lib/python2.7/site-packages/orator/connections/connection.py", line 34, in _run
        e, query, bindings, wrapped
      File "/var/www/grub_flask/venv/local/lib/python2.7/site-packages/orator/connections/connection.py", line 318, in _try_again_if_caused_by_lost_connection
        raise QueryException(query, bindings, e)
    orator.exceptions.query.QueryException: (0, '') (SQL: SELECT * FROM `restaurants` ([]))
    [pid: 13958|app: 0|req: 185/967] 41.184.34.126 () {46 vars in 989 bytes} [Mon Mar 21 18:08:02 2016] GET /api/v1/restaurants/ => generated 0 bytes in 4 msecs (HTTP/1.1 500) 0 headers in 0 bytes (0 switches on core 0)
    

    Here is what my query looks like:

    Restaurant.with_('area', 'foods').get().to_dict()
    
    opened by tantita 10
  • AttributeError: 'Car' object has no attribute 'name'

    AttributeError: 'Car' object has no attribute 'name'

    c = Car() print c.name

    AttributeError: 'Car' object has no attribute 'name'

    opened by sreenadh 9
  • Is this still being maintained?

    Is this still being maintained?

    We're re-evaluating our decision to use orator. Is this still being maintained?

    question 
    opened by mattcl 9
  • Weird error when loading Model classes, [Possible Bug?]

    Weird error when loading Model classes, [Possible Bug?]

    Hi, I'm trying to access an attribute of another Model (Say Model b) within my current Model with the relationship setting. But it gives me an error saying that the class b was not found, the reality is that they are at the same module and I imported the whole Model class. I tried in some way to find the root cause but not succeed, is that a possible bug? Or I'm using it in a wrong way? It's very much appreciated if anyone of you could provide any clue on this. Thank you! Below attaching the error message and code.

    - the code here:

    from orator import Model,SoftDeletes,accessor
    from orator.orm import belongs_to
    from Models.lottery_config import *
    
    class LotteryPlay(Model, SoftDeletes):
    
        __table__ = 'wcl_platform_lottery_play'
        __primary_key__ = 'plt_lotplay_id'
        __appends__ = ["lottery_name"]
    
        @belongs_to('plt_lotconf_id')
        def lottery_config(self):
            return LotteryConfig
    
        @accessor
        def lottery_name(self):
            return self.lottery_config.plt_lotconf_name
    

    - error message here:

    NameError
    name 'LotteryConfig' is not defined
    
    Traceback (most recent call last):
    
    File /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sanic/app.py, line 556, in handle_request
    
    response = await response
    
    File /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/asyncio/coroutines.py, line 110, in __next__
    
    return self.gen.send(None)
    
    File /Users/tzhang/Documents/workspace/wclbackend/Blueprints/lottery_play.py, line 16, in get_all
    
    plays = get_all_plays(int(page))
    
    File /Users/tzhang/Documents/workspace/wclbackend/Services/services_lottery_play.py, line 12, in get_all_plays
    
    return make_operation_result(data=plays.serialize(),last_page=last_page)
    File /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/orator/pagination/length_aware_paginator.py, line 94, in serialize
    return self._items.serialize()
    File /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/backpack/collections/base_collection.py, line 771, in serialize
    return list(map(_serialize, self.items))
    File /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/backpack/collections/base_collection.py, line 765, in _serialize
    return value.serialize()
    File /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/orator/orm/model.py, line 2168, in serialize `
    ```
    question 
    opened by tonyzhangcn 8
  • rename_column in Migration

    rename_column in Migration

    If a column with a not null constraint is renamed the column will successfully be renamed but the not null constraint will remain attached to the old column name.

    Observed using a sqlite DB

    opened by Gordo5556 0
  • Want to use orator orm in aws lambda..

    Want to use orator orm in aws lambda..

    Hi I want use orator orm in aws lambda for mysql queriesI have followed the steps of uploading it in layers of aws lambda as my other layers are working perfectly but when I am trying to use orator I am getting this error Unable to import module 'lambda_function': No module named 'orator' But in my local machine it is working perfectly fine.

    can you help ?

    opened by saptarshi-techspro 0
  • How to use db pool

    How to use db pool

    How to use db pool with orator?

    opened by kxg3030 0
  • Fix has_many_through select error

    Fix has_many_through select error

    opened by alfonsocv12 0
  • has_many_through select not working properly

    has_many_through select not working properly

    Using the same example as the documentation where we have 3 tables countries, users and posts

    countries
        id - integer
        name - string
    
    users
        id - integer
        country_id - integer
        name - string
    
    posts
        id - integer
        user_id - integer
        title - string
    

    And the same model as the documentation

    from orator.orm import has_many_through
    
    
    class Country(Model):
    
        @has_many_through(User)
        def posts(self):
            return Post
    

    If we make a query like this

    Country.with('posts').find(1).to_json()
    

    The result will be a json with the country and post like this

    {
        id: 1,
        name: foo,
        posts: [
            {
                id: foo,
                user_id: foo,
                title: foo
            }
        ]
    }
    

    For this to happend without bringing the users table columns has_many_through has this two functions that returns the columns to select living out all the parent table columns.

    The problem with the get function is that add_select overriding our self._query selects or get(columns=[]) because the get is on a Post model query, so we don't have access to filter the results.

        def _get_select_columns(self, columns=None):
            """
            Set the select clause for the relation query.
    
            :param columns: The columns
            :type columns: list
    
            :rtype: list
            """
            if columns == ["*"] or columns is None:
                columns = ["%s.*" % self._related.get_table()]
            return columns + ["%s.%s" % (self._parent.get_table(), self._first_key)]
    
        def get(self, columns=None):
            """
            Execute the query as a "select" statement.
    
            :type columns: list
    
            :rtype: orator.Collection
            """
            if columns is None:
                columns = ["*"]
    
            select = self._get_select_columns(columns)
    
            models = self._query.add_select(*select).get_models()
    
            if len(models) > 0:
                models = self._query.eager_load_relations(models)
    
            return self._related.new_collection(models)
    
    
    opened by alfonsocv12 5
  • get_attribute to consider fillable attributes

    get_attribute to consider fillable attributes

    Hey @sdispater, not sure whether you accept outside PRs, but would you consider attributes defined in the __fillable__ array to be honored by the get_attribute method?

    class Person(Model):
      __fillable__ = ['name']
    
    person = Person()
    
    print(person.name) # expect None, get AttributeError
    

    (I am fairly new to Python and was not able to run the test suite, so while it looks like my tests should work, I wasn't actually able to run them)

    opened by pmn4 0
  • Missing

    Missing "connection already closed" retry error?

    Hello and thank you for Orator.

    I received a QueryException with "connection already closed", and it looks like the connection isn't being retried.

    Could it be that the "connection already closed" string just needs to be added to: https://github.com/sdispater/orator/blob/34e774ea760d6a41a28b39d5f302008d982e5d50/orator/connections/connection.py#L359

    opened by alextyrsmith 1
  • Fix #387

    Fix #387

    opened by djfurman 1
  • PyYaml load function used in orator is subject to CVSS 9.8 risk

    PyYaml load function used in orator is subject to CVSS 9.8 risk

    PyYaml dependency on yaml.load() function is subject to CVE-2020-14343

    This function is only used once in orator. Recommending that this be replaced with the safe load method Refe: https://github.com/sdispater/orator/blob/0d82258a16c27b2d5f73f2e518e3af433145c0b5/orator/commands/command.py#L121

    PR to follow.

    opened by djfurman 0
  • don't give up this project

    don't give up this project

    this project is very good, it is useful for me, but, this project is don't update for long time, why?

    opened by mouday 2
Releases(0.9.10)
  • 0.9.10(Sep 28, 2020)

  • 0.9.9(Jul 15, 2019)

    Fixed

    • Fixed missing relationships when eager loading multiple nested relationships.
    • Fixed a possible AttributeError when starting a transaction.
    • Fixed an infinite recursion when using where_exists() on a soft-deletable model.
    • Fixed some cases where a reconnection would not occur for PostgreSQL.
    Source code(tar.gz)
    Source code(zip)
  • 0.9.8(Oct 10, 2018)

    Fixed

    • Fixed the morphed_by_many() decorator.
    • Fixed decoding errors for MySQL.
    • Fixed connection errors check.
    • Fixed the touches() method.
    • Fixed has_many not showing DISTINCT.
    • Fixed save_many() for Python 3.
    • Fixed an error when listing columns for recent MySQL versions.
    Source code(tar.gz)
    Source code(zip)
  • 0.9.7(May 17, 2017)

  • 0.9.6(May 16, 2017)

    Added

    • Added support for DATE types in models.
    • Added support for fractional seconds for the TIMESTAMP type in MySQL 5.6.4+.
    • Added support for fractional seconds for the TIMESTAMP and TIME types in PostreSQL.

    Changed

    • Improved implementation of the chunk method.

    Fixed

    • Fixed timezone offset errors when inserting datetime aware objects into PostgreSQL.
    • Fixed a bug occurring when using __touches__ with an optional relationship.
    • Fixed collections serialization when using the query builder
    Source code(tar.gz)
    Source code(zip)
  • 0.9.5(Feb 12, 2017)

  • 0.9.4(Jan 12, 2017)

  • 0.9.3(Nov 10, 2016)

  • 0.9.2(Oct 18, 2016)

  • 0.9.1(Sep 29, 2016)

    Changed

    • Improves migrate command output when pretending.

    Fixed

    • Fixes errors when using PyMySQL.
    • Fixes use_qmark information not being passed to schema grammars.
    Source code(tar.gz)
    Source code(zip)
  • 0.9.0(Sep 15, 2016)

    Changed

    ORM
    Connection
    • Improved connectors.
    Schema
    • Makes the use_current=True the default for timestamps().
    Query
    • Allow usage of qmark syntax for all backends.
    • Made QueryBuilder return Collections.
    • Merging queries also merges columns.
    • Made query builder results accessible by attributes.
    DBAL
    • Improved connectors and dbal to detect platform versions.
    Collections
    • Removed Collection code and uses backpack package instead.

    Fixed

    ORM
    • Fixed the update of pivots.
    • Fixed behavior for dates accessor.
    • Fixed connection not being properly set when specifying the connection with on()
    Commands
    • Made the -P/--pretend command option work.
    Schema
    • Fixed schema grammars.
    • Fixed an error when modify a table with an enum column in MySQL.
    DBAL
    • Fixed behavior for enum columns.
    Source code(tar.gz)
    Source code(zip)
  • 0.8.2(Jul 25, 2016)

    Fixes

    ORM

    • Fixing a possible Memory Error: stack overflow error when accessing relations.
    • Fixing builder copying process to avoir issues with PyMySQL(thanks to ihumanable).

    Commands

    • Fixing the -n/--no-interaction option not automatically confirming questions.
    • Removing the check character in migration commands output to avoid errors on Windows.

    Connection

    • Updating connectors to raise an exception when backend packages are missing.
    • Adding standard name resolution to the purge method (thanks to ihumanable).

    DBAL

    • Fixing setting foreign key constraint name for MySQL.
    • Handling missing constraint_name for sqlite (thanks to ihumanable).
    Source code(tar.gz)
    Source code(zip)
  • 0.8.1(Jul 25, 2016)

    Fixes

    ORM

    • Removing call to Model._boot_columns() to avoid errors for column types not supported by the dbal.

    Schema Builder

    • Fixing Blueprint.char() method (thanks to ihumanable).
    • Fixing Fluent behavior.

    Commands

    • Fixing orator command not working on Windows.
    • Fixing migrate:status command not switching databases.

    Connection

    • Fixing a bug when calling Connnection.disconnect() after a reconnection when not using read/write connections.
    • Fixing MySQLConnection.get_server_version() method to be compatible with PyMySQL (thanks to gdraynz).
    Source code(tar.gz)
    Source code(zip)
  • 0.8.0(Jul 25, 2016)

    Improvements

    ORM

    • #30 Support for default values
    • #29 Supporting only one timetamp column on models
    • #26 Adding support for extra conditions on relationships
    • Adding @scope decorator to define query scopes.
    • Improving global scopes

    Schema builder

    • Adding support for a use_current() on timestamps
    • Improving dbal to support SQLite fully.
    • Improving fluents

    Query Builder

    • #28 Making where_in() method accept Collection instances

    Commands

    • Adding a make:model command

    Connection

    • Using unicode by default for mysql and postgres.
    • Improves how queries are run in Connection class

    Collections

    • Adds flatten() method to Collection class

    Fixes

    ORM

    • Fixes Model.get_foreign_key() method
    • Fixes soft deletes
    • Avoid going through setattr method when setting timestamps

    Schema Builder

    • #33 [SQLite] Renaming or dropping columns loses NULL constraint
    • #32 [SQLite] Renaming or dropping columns fails when columns' name is a keyword
    • #31 [SQLite] Changing columns loses default column values.

    Query Builder

    • Fixes query grammar default columns value

    Connection

    • Fixing Connection._try_again_if_caused_by_lost_connection() not being called
    • Preventing default connection being set to None
    • Fixing json type behavior for Postgres

    Migrations

    • Fixing migration stubs
    Source code(tar.gz)
    Source code(zip)
Owner
Sébastien Eustace
Software engineer, proud pythonista, open source lover. Creator of the Poetry package manager and the datetime library Pendulum.
Sébastien Eustace
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.4k Oct 11, 2021
Piccolo - A fast, user friendly ORM and query builder which supports asyncio.

A fast, user friendly ORM and query builder which supports asyncio.

null 566 Oct 20, 2021
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 8.7k Oct 25, 2021
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.3k Oct 21, 2021
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

null 674 Oct 23, 2021
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

null 2.7k Oct 25, 2021
A Python Object-Document-Mapper for working with MongoDB

MongoEngine Info: MongoEngine is an ORM-like layer on top of PyMongo. Repository: https://github.com/MongoEngine/mongoengine Author: Harry Marr (http:

MongoEngine 3.6k Oct 24, 2021
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 12 Oct 22, 2021
Pydantic model support for Django ORM

Pydantic model support for Django ORM

Jordan Eremieff 189 Oct 23, 2021
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

null 1.7k Oct 23, 2021
A Python Library for Simple Models and Containers Persisted in Redis

Redisco Python Containers and Simple Models for Redis Description Redisco allows you to store objects in Redis. It is inspired by the Ruby library Ohm

sebastien requiem 434 Oct 1, 2021
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 273 Sep 2, 2021
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.6k Oct 22, 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 788 Oct 14, 2021
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.1k Oct 18, 2021