What is the difference between the declarative_base() and db.Model? - python

The quickstart tutorial for the Flask-SQLAlchemy plugin instructs users to create table models inheriting the db.Model class, e.g.
app = Flask(__main__)
db = SQLAlchemy(app)
class Users(db.Model):
__tablename__ = 'users'
...
However, the SQLAlchemy tutorial and the bottle-SQLAlchemy README both suggest that table models inherit a Base instantiated from declarative_base().
Base = declarative_base()
class Users(Base):
__tablename__ = 'users'
...
What is the difference between these two approaches?

Looking in the Flask-SQLAlchemy source code the db.Model class is initialized as follows:
self.Model = self.make_declarative_base()
And here is the make_declarative_base() method:
def make_declarative_base(self):
"""Creates the declarative base."""
base = declarative_base(cls=Model, name='Model',
metaclass=_BoundDeclarativeMeta)
base.query = _QueryProperty(self)
return base
The _BoundDeclarativeMeta metaclass is a subclass of SQLAlchemy's DeclarativeMeta, it simply adds support for computing a default value for __tablename__ (the table name) and also to handle binds.
The base.query property enables Flask-SQLAlchemy based models to access a query object as Model.query instead of SQLAlchemy's session.query(Model).
The _QueryProperty query class is also subclassed from SQLAlchemy's query. The Flask-SQLAlchemy subclass adds three additional query methods that do not exist in SQLAlchemy: get_or_404(), first_or_404() and paginate().
I believe these are the only differences.

Related

Create a base model class

I'm using flask-sqlalchemy.
I currently declare my models using:
class MyModel(db.Model):
__tablename__ = 'my_table'
id = db.Column(db.Integer, primary_key=True)
...
I want to create a base model class so I will be able to declare my models like this:
class MyBase(db.Model):
pass
class MyModel(MyBase):
__tablename__ = 'my_table'
id = db.Column(db.Integer, primary_key=True)
...
Is it possible?
I'm getting the following error:
InvalidRequestError: Class <class 'api.models.base.base_model.BaseModel'> does not have a __table__ or __tablename__ specified and does not inherit from an existing table-mapped class.
I would want to be able to put the tablename and all the column attributes inside my model and not inside my base class.
Since your custom base model is not a real model, you need to tell SQLAlchemy that it is abstract by setting __abstract__ = True on the class.
class MyBase(db.Model):
__abstract__ = True
Unless you are adding common functionality to this custom base, there's no point in doing this. The empty custom base is basically equivalent to just inheriting from db.Model directly.

Independent SQLAlchemy models [duplicate]

I have some standard SQLAlchemy models that I reuse across projects. Something like this:
from sqlalchemy import Column, Integer, String, Unicode
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Category(Base):
__tablename__ = 'category'
id = Column(Integer, primary_key=True)
slug = Column(String(250), nullable=False, unique=True)
title = Column(Unicode(250), nullable=False)
def __call__(self):
return self.title
I'd like to put this in a shared library and import it into each new project instead of cutting and pasting it, but I can't, because the declarative_base instance is defined separately in the project. If there's more than one, they won't share sessions. How do I work around this?
Here's another question that suggests using mixin classes. Could that work? Will SQLAlchemy accurately import foreign keys from mixin classes?
When you call
Base = declarative_base()
SA create new metadata for this Base.
To reuse your models you must bind metadata of main models to reusable models, but before any import of your reusable models by:
Base.metadata = my_main_app.db.metadata
MixIn classes useful for repeating column declarations, and extending class methods.
For connecting reusable apps based on MixIns you must define concrete class in code manualy for each model.
Will SQLAlchemy accurately import
foreign keys from mixin classes?
MixIn class with foreign key and constraint
from sqlalchemy.schema import UniqueConstraint
from sqlalchemy.ext.declarative import declared_attr
class MessageMixIn(object):
ttime = Column(DateTime)
#declared_attr
def sometable_id(cls):
return Column(Integer, ForeignKey('sometable.id'))
#declared_attr
def __table_args__(cls):
return (UniqueConstraint('sometable_id', 'ttime'), {})

Using same name of tables with different binds in Flask

I have two tables sharing the same name but located in different databases:
class Lcn(db.Model):
__tablename__ = 'lcn'
class LcnRemote(db.Model):
__bind_key__ = 'remote'
__tablename__ = 'lcn'
It seems SQLAlchemy doesn't like that. It says:
sqlalchemy.exc.InvalidRequestError: Table 'lcn' is already defined for this MetaData instance. Specify 'extend_existing=True' to redefine options and columns on an existing Table object.
Is there a way to solve this without having to rename one of my tables?
Use separate declarative base classes for different databases with the same name, to prevent sharing of SQLAlchemy metadata. You'll have to create two flask_sqlalchemy.SQLAlchemy() instances:
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/database1.db'
app.config['SQLALCHEMY_BINDS'] = {'remote': 'sqlite:////tmp/database1.db'}
db1 = SQLAlchemy(app)
class Lcn(db1.Model):
__tablename__ = 'lcn'
db2 = SQLAlchemy(app)
class LcnRemote(db2.Model):
__bind_key__ = 'remote'
__tablename__ = 'lcn'
This is a limitation of Flask-SQLAlchemy, it really should allow you to create declarative bases per bind. The way the SQLAlchemy() class currently is designed limits it to just one such base; it proxies various SQLAlchemy metadata calls through the db.Model class it generates at the start. By creating two instances of flask_sqlalchemy.SQLAlchemy() you work around this issue.

How to inherit the declarative in SQLAlchemy without setting the __tablename__?

Using flask-sqlalchemy, I want to create some class to inherit the declarative class and add the __bind_key__. So that I can create some tables and inherit these binded class.
from flask.ext.sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Model1(db.Model):
__bind_key__ = 'db2'
class Table1(Model1):
__tablename__ = 'table1'
name = db.Column(db.String(100))
But I got some troubles:
sqlalchemy.exc.InvalidRequestError: Class <class '__main__.Model1'>
does not have a __table__ or __tablename__ specified
and does not inherit from an existing table-mapped class.
How could I solve it?
You can also use the __abstract__ flag:
from flask.ext.sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Model1(db.Model):
__abstract__ = True
__bind_key__ = 'db2'
class Table1(Model1):
__tablename__ = 'table1'
name = db.Column(db.String(100))
Classes with __abstract__ set to True are ignored by SQLAlchemy declarative (docs here). As a bonus, this enables you to add SQLAlchemy specific attributes (for example columns) to your Model1.

Reusing SQLAlchemy models across projects

I have some standard SQLAlchemy models that I reuse across projects. Something like this:
from sqlalchemy import Column, Integer, String, Unicode
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Category(Base):
__tablename__ = 'category'
id = Column(Integer, primary_key=True)
slug = Column(String(250), nullable=False, unique=True)
title = Column(Unicode(250), nullable=False)
def __call__(self):
return self.title
I'd like to put this in a shared library and import it into each new project instead of cutting and pasting it, but I can't, because the declarative_base instance is defined separately in the project. If there's more than one, they won't share sessions. How do I work around this?
Here's another question that suggests using mixin classes. Could that work? Will SQLAlchemy accurately import foreign keys from mixin classes?
When you call
Base = declarative_base()
SA create new metadata for this Base.
To reuse your models you must bind metadata of main models to reusable models, but before any import of your reusable models by:
Base.metadata = my_main_app.db.metadata
MixIn classes useful for repeating column declarations, and extending class methods.
For connecting reusable apps based on MixIns you must define concrete class in code manualy for each model.
Will SQLAlchemy accurately import
foreign keys from mixin classes?
MixIn class with foreign key and constraint
from sqlalchemy.schema import UniqueConstraint
from sqlalchemy.ext.declarative import declared_attr
class MessageMixIn(object):
ttime = Column(DateTime)
#declared_attr
def sometable_id(cls):
return Column(Integer, ForeignKey('sometable.id'))
#declared_attr
def __table_args__(cls):
return (UniqueConstraint('sometable_id', 'ttime'), {})

Categories

Resources