Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you make a re-usable component with Flask-SQLAlchemy?

Lets say I have a part of my Flask app that I want to pull out and use in multiple different projects. It has SQLAlchemy models using Flask-SQLAlchemy, as well as views and templates. I can get the app.route decorators by using a Module, but how can I handle the SQLAlchemy db object?

Since my component needs to be separate, I can't just import the db object. But I don't think I should create the db object twice. How would I mix its metadata in with the metadata in the rest of the project?

like image 243
Nick Retallack Avatar asked Oct 11 '22 05:10

Nick Retallack


2 Answers

At first you must share metadata and do not use flask-sqlalchemy db.Model as base for you re-usable app.

At second you must recreate all classes with extending them new bases from flask-sqlalchemy for supporting signals, short-cuts and other stuff.

Integrate bases (it may be buggy):

def integrate_models(cls, nbase):
        new_bases = list(cls.__bases__) + [nbase]
        new_dict = dict(cls.__dict__)
        new_dict.update(dict(nbase.__dict__))
        return type.__new__(cls.__class__, cls.__name__, tuple(new_bases), new_dict)

Example Flask app with sqlalchemy extension:

# -*- coding: utf-8 -*-
from flask import Flask
from flaskext.sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True)
    email = db.Column(db.String(120), unique=True)

    def __init__(self, username, email):
        self.username = username
        self.email = email

    def __repr__(self):
        return '<User %r>' % self.username

if __name__ == '__main__':
        from some_model_base import init_metadata
        init_metadata(db.Model.metadata)

        from some_model import SomeClass

        SomeClass = integrate_models(SomeClass, db.Model)

        db.create_all()

        print SomeClass.query.all()

Where some_model_base.py:

from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()

def init_metadata(metadata):
    Base.metadata = metadata

And some_model.py:

from sqlalchemy import Column, Integer, String
from some_model_base import Base

class SomeClass(Base):
    __tablename__ = 'some_table'
    id = Column(Integer, primary_key=True)
    name =  Column(String(50))

In this example tested only db.create_all and SomeClass.query shortcut.

Sorry for my poor English.

like image 190
estin Avatar answered Oct 18 '22 03:10

estin


You should use flask.current_app, it is a proxy object to access the current application, and it is what is used in some extensions. So, your code would look something like this:

from flask import current_app
from flaskext.sqlalchemy import SQLAlchemy

db = SQLAlchemy(current_app)

and then use your db object as you would usually do. But i'm not sure if this would work if the user used your module and also needed to use flaskext.sqlalchemy, maybe it would conflict, i'd suggest to test this, to be sure your module will be 100% compatible with any flask application.

like image 35
mdeous Avatar answered Oct 18 '22 01:10

mdeous