Create a base model class - python

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.

Related

Flask + sqlalchemy + marshmallow - Error on relationship - One to Many

Why this code returns an error?
Error: When initializing mapper Mapper|Pessoa|pessoa, expression 'Imovel' failed to locate a name ("name 'Imovel' is not defined").
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
db=SQLAlchemy()
ma=Marshmallow()
class Pessoa(db.Model):
__tablename__ = 'pessoa'
idLocal= db.Column(db.Integer, primary_key=True)
Nome=db.Column(db.String(100), default=u'')
imovelList = db.relationship("Imovel", back_populates="PessoaList")
def get_id(self):
return self.idLocal
class PessoaSchema(ma.ModelSchema):
class Meta: model = Pessoa
class Imovel(db.Model):
__tablename__ = 'imovel'
idLocal= db.Column(db.Integer, primary_key=True)
CodigoImovel=db.Column(db.String(30), default=u'')
idPessoa = db.Column(db.Integer, db.ForeignKey('pessoa.idLocal'))
PessoaList = db.relationship("Pessoa", back_populates="imovelList")
def get_id(self):
return self.idLocal
class ImovelSchema(ma.ModelSchema):
class Meta: model = Imovel
You have an 'order of declaration' issue. Relationships are initialized immediately when their Mappers are constructed when the relationship is defined with a String. But when you define the relationship on "Imovel" you have yet to declare a Mapper called "Imovel". The Imovel Mapper or class is defined a couple lines after that.
So you could move the Imovel Mapper above the Pessoa Mapper, except then you would get the exact same error as you are also building a relationship from Imovel to Pessoa.
So instead you want to declare your relationship using a callable function that will return the "Imovel" Mapper. This function will typically only be called after all Mappers are constructed. Thus by using a lambda function we can ensure the relationship is not invoked until you have had a chance to setup the Imovel class.
In practice, to fix this error, replace this line
imovelList = db.relationship("Imovel", back_populates="PessoaList")
with this
imovelList = db.relationship(lambda: Imovel, back_populates="PessoaList")

Creating second model upon instantiation of the first within sqlachemy

I am currently working with some legacy code that looks as follows:
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Unicode
from sqlalchemy.dialects.postgresql import ARRAY, TEXT
Base = declarative_base()
class Book(Base):
__tablename__ = 'book'
id = Column(Integer, primary_key=True)
title = Column(Unicode)
keywords = Column('keywords', ARRAY(TEXT), primary_key=False)
The keywords are currently being kept as an array, but I'd like to flatten this out and have them be in their own separate model
class Keyword():
__tablename__ = 'keyword'
id = Column(Integer, primary_key=True)
book_id = Column(Integer, ForeignKey('book.id', ondelete='cascade'),
nullable=False)
keyword = Column(Unicode)
How can I make it such that when a Book() is created, it also creates the
accompanying keywords? As an intermediate step for migrating the API, I'd like to keep the current array column, but also have the accompanying Keyword() instances be created.
I could do this within an __init__ method, but would need to know what the current Session() was, in order to run a commit. I could also perhaps use a property attribute, attached to keywords, but am not sure how that would work given that I am working with a class that inherits from SQLAlchemy's base, and not with a regular class that I have defined. What's the correct way to do this?
You can use object_session to find out the session of a given instance.
But if you define relationship between a Book and Keywords, you should not need even bother:
class Book(Base):
# ...
rel_keywords = relationship('Keyword', backref='book')
def init_keyword_relationship(self):
for kw in self.keywords:
self.rel_keywords.add(Keyword(keyword=kw))
sess = # ... get_session...
books = sess.query(Book).all()
for book in books:
book.init_keyword_relationship()
sess.commit()
However, I would do a migration once and get rid of the keywords array in order not to add a logic to keep those in sync.

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

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.

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.

One-to-many relationship to multiple models

I have a model Thing and a model Action. There is a one-to-many relationship between Things and Actions. However, I would like to be able to subclass Action to have (for example) BuildAction, HealAction and BloodyStupidAction. Is it possible using Flask-SQLAlchemy to do this and maintain the single one-to-many relationship?
This problem is described in the SQLAlchemy docs under Inheritance Configuration. If your different subclasses will share the same database table, you should use single table inheritance.
Code example:
class Thing(db.Model):
__tablename__ = 'thing'
id = db.Column(db.Integer, primary_key=True)
actions = db.relationship('Action', backref=db.backref('thing'))
class Action(db.Model):
__tablename__ = 'action'
id = db.Column(db.Integer, primary_key=True)
thing_id = db.Column(db.Integer, db.ForeignKey('thing.id'))
discriminator = db.Column('type', db.String(50))
__mapper_args__ = {'polymorphic_on': discriminator}
class BuildAction(Action):
__mapper_args__ = {'polymorphic_identity': 'build_action'}
time_required = db.Column(db.Integer)
Each subclass of Action should inherit the thing relationship defined in the parent class. The action.type column describes which subclass action each row of the table represents.

Categories

Resources