Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to manage django and flask application sharing one database model?

I have two repository written in flask and django.
These projects sharing the database model which is written in SQLAlchemy in flask and written in Django ORM.
When I write migration script in flask as alembic, How can django project migrates with that script?

I also think about Django with SQLAlchemy. But I can't find out Django projects using SQLAlchemy. Is that bad idea?
Thanks.

like image 588
Jony cruse Avatar asked Apr 27 '15 08:04

Jony cruse


1 Answers

At our workplace, we use both django and flask on the same website. The reason for this is django's admin and model system are very quick to write code for, and it serves as our backend (staff-only) admin interface. On the other hand, it's far easier to write the frontend in flask, as each page can be as simple as one python function and a Jinja2 template. Flask forms require an additional form model class (it's easy to write) that specifies all the fields in the form, and a second SQLAlchemy model if the form responses are to be saved to an SQL database. If you were to write the same form in django, in the simplest case you don't write the form model, as the django framework implicitly generates it from the sql model. It's still faster to write "regular" pages in flask, though.

As for making the two play nice together, how it ends up working is we write the django model first, as database management and sql schema migrations are baked into django. Then we use a feature of SQLAlchemy called "reflection" that reads the sql database and generates the sql model from the schema. That way, when we make a change to the django model, we run manage.py migrate (which deploys the schema changes to the sql database), and the SQLAlchemy models automatically reflect the new schema without us having to touch the Flask-SQLAlchemy model. Here is an example that includes some foreign key stuff and some timestamps that are automatically updated whenever you use the ORM to save the model. I've included those because they're both common use cases and both require additional code.

Django model:

from django.db import models

class DjangoModel(models.Model):
    field1 = models.CharField(max_length=10)
    field2 = models.IntegerField()
    field3 = models.DateTimeField()
    # We frequently use timestamps like this that the ORM updates automatically on creation and modification respectively.
    created_timestamp = models.DateTimeField(auto_now_add=True)
    modified_timestamp = models.DateTimeField(auto_now=True)
    related = models.ForeignKey(SomeOtherDjangoModel, related_name='related_backref')
    class Meta:
        db_table = 'sql_table_name'

Flask-SQLAlchemy model:

from flask import current_app
from flask_sqlalchemy import SQLAlchemy
import datetime

# The next three lines should be in your application.py ideally.
db = SQLAlchemy()
db.init_app(current_app)
db.Model.metadata.reflect(db.engine)

class FlaskSQLAlchemyModel(db.Model):
    # All 6 fields from the django model and the implicit 'id' field django
    # adds to its models will get reflected automatically the way this model
    # is coded. The created_timestamp and modified_timestamp fields require
    # special code for Flask-SQLAlchemy to update them automatically when
    # you save a model instance. Relationships must be defined manually in
    # Flask-SQLAlchemy, so there's code for that, too.
    __table__ = db.Table('sql_table_name', db.Model.metadata,
            # You'll need to "override" the timestamp fields to get flask-sqlalchemy to update them automatically on creation and modification respectively.
            db.Column('created_timestamp', db.DateTime, default=datetime.datetime.now),
            db.Column('modified_timestamp', db.DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now),
            autoload=True,
            extend_existing=True,
            autoload_with=db.engine,
        )
    # You need to do model relationships manually in SQLAlchemy...
    related = db.relationship(SomeOtherFlaskSQLAlchemyModel, backref=db.backref('related_backref', lazy='dynamic'))

The caveat to defining the Flask-SQLAlchemy models this way is that they depend on the the SQL database. Flask-SQLAlchemy and its db object need to be properly configured before you try to even import the .py file that model is in, otherwise you'll be rewarded with some nasty errors upon import. If you put the definition of db in the same file as the model, and it gets initialized like such:

from flask import current_app
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()
db.init_app(current_app)

Then you'll need to do the import like such:

with app.app_context():
    import module_with_flask_sqlalchemy_model

Of course, app needs to have loaded settings with configuration for Flask-SQLAlchemy, or that won't work. It's usually better to just have the model file do from application import db, and have application.py do from my_models import * after db has been set up.

Once properly configured, the Flask-SQLAlchemy model will be equivalent to a Flask-SQLAlchemy model defined like this:

from flask import current_app
from flask_sqlalchemy import SQLAlchemy
import datetime

db = SQLAlchemy()
db.init_app(current_app)

class FlaskSQLAlchemyModel(db.Model):
    __tablename__ = 'sql_table_name'
    id = db.Column(db.Integer, primary_key=True)
    field1 = db.Column(db.String(10))
    field2 = db.Column(db.Integer)
    field3 = db.Column(db.DateTime)
    created_timestamp = db.Column(db.DateTime, default=datetime.datetime.now)
    modified_timestamp = db.Column(db.DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)
    related_id = db.Column(db.Integer, db.ForeignKey('some_other_sql_table_name.id'))
    related = db.relationship(SomeOtherFlaskSQLAlchemyModel, backref=db.backref('related_backref', lazy='dynamic'))

Once you've got the django models and the reflected flask-sqlalchemy models down, then you can use django's awesome migrate command to create your tables with the correct schema. Then the flask-sqlalchemy models will notice they've been created and automatically populate themselves. This way, you only have to worry about the django model (minus the timestamp fields and model relationships), and just let flask-sqlalchemy reflect the rest for you.

The one last thing you need to do is configure Flask-SQLAlchemy and Django to point at the same SQL database. I assume you know how to do that, though. One hint I'll give you is that the settings.py for Flask and Django can be the same file without any conflict.

like image 146
MasterGeek Avatar answered Nov 09 '22 22:11

MasterGeek