Using SqlAlchemy Models in flask - python

I created some models using SqlAlchemy for setting up the database initially. Initially i parse some XML files and populate the database. This is a one time thing which needs to be done when i setup the app on server.
Base = declarative_base()
class Movie(Base):
__tablename__ = 'Movies'
id = Column(Integer, primary_key=True, autoincrement=True)
title = Column(String(80))
filename = Column(String(80), unique=True)
genre = Column(String(80))
language = Column(String(80))
year = Column(Integer)
description = Column(Text)
poster = Column(String)
def __init__(self, title, filename, genre, language, year, description, poster):
self.title = title
self.filename = filename
self.genre = genre
self.language = language
self.year = year
self.description = description
self.poster = poster
def __repr__(self):
return '<Movie (id=%d, title=%s, filename=%s, genre=%s, year=%s, description=%s, poster=%s)>' % (
self.id, self.title, self.filename, self.genre, self.year, self.description, self.poster )
......
Now i want to use the same models in the flask also for a REST api. But from what i have seen, first i need to create a db instance using the flask app - like this
app = Flask(__name__)
db = SQLAlchemy(app)
class Movie(db.Model):
.......
How does this workout? Since my models are inheriting from the Base class but for flask they need to inherit from the db.Model class.

you can simply use your models as they are, they do not "need" to inherit from db.Model, that is simply a convince to make flask integration easier

You can to create a module with the SQLAlchemy instance and use only the inheritance in the another's modules.
in database.py module:
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
movieModel.py
from database import db
class MovieModel(db.Model):
__tablename__ = 'movie'
id = db.Column(db.Integer, primary_key=True)
imageModel.py
from database import db
class ImageModel(db.Model):
__tablename__ = 'image'
id = db.Column(db.Integer, primary_key=True)

Related

How can i do a relationship with multiple entities?

How can i link both entities with relationship with flask python?
for example i have this entity, here i am trying to link with user = relationship('User'), so i am getting error relation of relationship (btw: Grant, User, Client are in differents files )
from sqlalchemy.orm import relationship
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Grant(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(
db.Integer, db.ForeignKey('user.id', ondelete='CASCADE')
)
user = relationship('User')
this is the error:
sqlalchemy.exc.InvalidRequestError: When initializing mapper mapped class Grant->grant, expression 'User' failed to locate a name ('User'). If this is a class name, consider adding this relationship() to the <class 'model.Grant.Grant'> class after both dependent classes have been defined.
note: those are my anothers entities:
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(40), unique=True, index=True,
nullable=False)
def check_password(self, password):
return True
and this is the Client.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Client(db.Model):
name = db.Column(db.String(40))
client_id = db.Column(db.String(40), primary_key=True)
this is the error user = relationship('User') please helpme

How to map table relationships with flask-sqlalchemy, before tables exist?

I have a problem creating relationships between my tables in flask-sqlalchemy. I have a table with project overview, and from there on out I want to dynamically create new experiment tables with a relationship to my project overview. However, when I try to define the relationship, sqlalchemy throws the following error:
sqlalchemy.exc.InvalidRequestError: When initializing mapper mapped class Projects->projects, expression 'Experiment_Overview' failed to locate a name ('Experiment_Overview'). If this is a c
lass name, consider adding this relationship() to the <class 'app.Projects'> class after both dependent classes have been defined.
This seems to be the case because the class Experiment_Overview(db.Model) does not exist yet, which is correct since it will be dynamically generated later on through user input. How can I mitigate this error?
import os
from flask import Flask, render_template, redirect, request, url_for
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
app = Flask(__name__)
Bootstrap(app)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///DATA/DB.db"
db = SQLAlchemy(app)
def TableCreator(tablename):
class Experiment_Overview(db.Model):
__tablename__ = tablename
creation_date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
experiment_name = db.Column(db.String(30), unique=False, nullable=False, primary_key=True)
projectname = db.Column(db.String(150), db.ForeignKey('projects.projectname'), nullable=False, unique=True)
return MyTable
class Projects(db.Model):
projectname = db.Column(db.String(150), unique=True, nullable=False, primary_key=True)
creation_date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
experiments = db.relationship('Experiment_Overview', backref="experiments", lazy=True, uselist=False)
def __init__(self, owner, projectname, status, created_at):
self.owner=owner
self.projectname=projectname
self.status=status
self.created_at=created_at
db.create_all()
Generally speaking, you don't dynamically create tables; you usually shouldn't create or drop tables while your program is running.
Additionally, I believe it's impossible to create a true relationship that links back to an entire table. Relationships/Foreign Keys are for linking between rows in tables.
Don't worry, there are easier ways to achieve the behavior that you are looking for here.
From your question it sounds like you can have multiple Projects, and each project can have multiple Experiments within the project.
This would make the relationship between a Project and its Experiments a One-To-Many relationship.
If this is the case, you would need one Projects table (which you have already in your code), and you would also have one Experiments table.
Each row in the Projects table represents one Project.
Each row in the Experiments table represents one Experiment. The Experiments table will have a column containing a foreign key linking back to the Project the Experiment is linked to.
I've modified your code according to the One-To-Many example code given in the SQLAlchemy documentation that I linked above.
Note the addition of the back_populates option to the relationship(), this allows bi-directional knowledge of the relationship: the Experiment know what project it belongs to, and the Project know what Experiments it has.
import os
from flask import Flask, render_template, redirect, request, url_for
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
app = Flask(__name__)
Bootstrap(app)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///DATA/DB.db"
db = SQLAlchemy(app)
class Projects(db.Model):
__tablename__ = "projects"
projectname = db.Column(db.String(150), unique=True, nullable=False, primary_key=True)
creation_date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
experiments = db.relationship("Experiment", back_populates="project")
def __init__(self, owner, projectname, status, created_at):
self.owner=owner
self.projectname=projectname
self.status=status
self.created_at=created_at
class Experiment(db.Model):
__tablename__ = "experiments"
creation_date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
experiment_name = db.Column(db.String(30), unique=False, nullable=False, primary_key=True)
projectname = db.Column(db.String(150), db.ForeignKey('projects.projectname'), nullable=False)
project = relationship("Project", back_populates="experiments")
db.create_all()

What is the purpose of declarative_base()

I've been reading through the docs (screenshot) as well as some online questions about declarative_base(). eg https://groups.google.com/forum/#!topic/sqlalchemy/-Zl9T7Q7awY as I try to understand its usage in projects like https://github.com/hack4impact/flask-base/tree/master/app
This project uses flask-sqlalchemy (http://flask-sqlalchemy.pocoo.org/2.1/quickstart/)
From researching I see its possible to create sqlalchemy table models that look like:
class Property(db.Model):
__tablename__ = 'properties'
__searchable__ = ['body']
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(140))
body = db.Column(db.String(1400))
img = db.Column(db.String(140))
or that inherit from base, eg.
Base = declarative_base()
class Property(Base):
__tablename__ = 'properties'
__searchable__ = ['body']
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(140))
body = db.Column(db.String(1400))
img = db.Column(db.String(140))
Why should I use Base here?
PS:
in the project above the app/_init.py initializes the flask app with multiple extensions including sqlalchemy:
mail = Mail()
db = SQLAlchemy()
.....
def create_app(config_name):
....
db.init_app(app)
login_manager.init_app(app)
...
db is then imported into the model files.

sqlalchemy session to create or update records with * to many relationships

I have a fairly simple flask app connected to a postgresql database. I am mainly using the flask app with flask-admin so that I can add records to the database and perhaps build it out into a dashboard later. It's an internal use catalog, basically.
What I am trying to do is also write a script that connects to a third party API to add/update records in the database, so it does not got through the flask app. I am using SQLAlchemy to do this because it's consistent with the app and I just need something to work without fussing over SQL statements.
The flask app's data model is defined as such:
app.py
from flask import Flask, render_template, request
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.dialects import postgresql
from flask_admin import Admin
# ... APPLICATION CONFIGURATION ...
# db Models
## Table for many to many
keywords = db.Table('keywords',
db.Column('keyword_id', db.Integer, db.ForeignKey('keyword.id')),
db.Column('dataset_id', db.String(24), db.ForeignKey('dataset.dataset_id')),
)
## Model classes
class Dataset(db.Model):
title = db.Column(db.String(120))
description = db.Column(db.Text())
dataset_id = db.Column(db.String(24), primary_key=True, unique=True)
#relationships
dataset_documentation = db.relationship('DataDocument', backref='dataset', lazy='dynamic')
keywords = db.relationship('Keyword', secondary=keywords, backref='dataset', lazy='dynamic')
def __str__(self):
return self.title
class Keyword(db.Model):
id = db.Column(db.Integer, primary_key=True)
keyword = db.Column(db.String(80))
def __str__(self):
return self.keyword
class DataDocument(db.Model):
id = db.Column(db.Integer, primary_key=True)
document = db.Column(db.String(120))
dataset_id = db.Column(db.String(24), db.ForeignKey('dataset.dataset_id'))
def __str__(self):
return self.document
# ... APPLICATION VIEWS ...
So we have datasets with some basic metadata and they have a one to many relationship with a filepath to a document and a many to many relationship to any number of keywords.
The separate script is connecting directly to the database, and mapping existing tables to objects that I can use to create a session and modify the database.
script.py
import config #local config only
from sqlalchemy import create_engine, MetaData, Table
from sqlalchemy.orm import mapper, sessionmaker
# Connecting to postgres database and creating a session with database objects, intantiate empty classes to populate
class Dataset(object):
pass
class DataDocument(object):
pass
class Keyword(object):
pass
## How to instantiate the MTM association table?
db_uri = config.SQLALCHEMY_DATABASE_URI
engine = create_engine(db_uri)
meta = MetaData(engine)
dataset_table = Table('dataset', meta, autoload=True) #correct
datadocument_table = Table('dataset', meta, autoload=True) #incorrect?
keyword_table = Table('keyword', meta, autoload=True) #incorrect?
mapper(Dataset, dataset_table) #correct
mapper(DataDocument, datadocument_table, meta, autoload=True) #??
mapper(Keyword, keyword_table, meta, autoload=True) #??
Session = sessionmaker(bind=engine)
session = Session()
# sample update
data_upsert = Dataset()
data_upsert.title = "Some title"
data_upsert.dataset_id = "Uniq_ID-123"
data_upsert.description = "lorem ipsum foo bar foo"
session.merge(data_upsert)
#attempt to add related properties
key1 = Keyword('test1')
key2 = Keyword('test2')
datadoc = DataDocument('path/to/document.txt')
# FAIL.
data_upsert.append(key1)
data_upsert.append(key2)
data_upsert.append(datadoc)
session.flush()
I am a newbie with sqlalchemy and I can just barely wrap my head around creating the Dataset object in the script from the database engine. But I was thinking in loading the Keyword and Datadocument tables as well that it would already understand the relationships based on what it is loading from the database, but this is where my understanding is running thin.
Is there a straightforward way to complete the picture here? I am assuming it doesn't make sense to define my models again explicitly in script.py, but in reviewing documentation and some tutorials, I am not seeing the missing pieces of loading these relationships into the session so that I can ingest all of the data into the database.
Update your model definitions to add constructor functions. In that case, it allows you to pass the parameters to the object upon instantiation.
models.py
## Model classes
class Dataset(db.Model):
title = db.Column(db.String(120))
description = db.Column(db.Text())
dataset_id = db.Column(db.String(24), primary_key=True, unique=True)
#relationships
dataset_documentation = db.relationship('DataDocument', backref='dataset', lazy='dynamic')
keywords = db.relationship('Keyword', secondary=keywords, backref='dataset', lazy='dynamic')
def __init__(self, title=None, desc=None, dataset_id=None):
self.title = title
self.description = desc
self.dataset_id = dataset_id
def __str__(self):
return self.title
class Keyword(db.Model):
id = db.Column(db.Integer, primary_key=True)
keyword = db.Column(db.String(80))
def __init__(self, keyword=None):
self.keyword = keyword
def __str__(self):
return self.keyword
class DataDocument(db.Model):
id = db.Column(db.Integer, primary_key=True)
document = db.Column(db.String(120))
dataset_id = db.Column(db.String(24), db.ForeignKey('dataset.dataset_id'))
def __init__(self, document, dataset_id):
self.document = document
self.dataset_id = dataset_id
def __str__(self):
return self.document
No need to define the model classes again in the script.py. You can simply import the classes you want to use from the models.py. Then you can insert the data object with its related objects altogether into the database in this way:
script.py
import config #local config only
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from models import Dataset, DataDocument, Keyword
def loadSession(engine):
""""""
Session = sessionmaker(bind=engine)
session = Session()
return session
engine = create_engine(config.SQLALCHEMY_DATABASE_URI, echo=False)
Base = declarative_base(engine)
# load session
session = loadSession(engine)
data_upsert = Dataset(title="Some title", dataset_id="Uniq_ID-125", desc="lorem ipsum foo bar foo")
# add related properties here
key1 = Keyword('test1')
key2 = Keyword('test2')
datadoc = DataDocument('path/to/document.txt', dataset_id="Uniq_ID-125")
# append the properties to the object
data_upsert.dataset_documentation.append(datadoc)
data_upsert.keywords.append(key1)
data_upsert.keywords.append(key2)
session.add(data_upsert)
session.commit()
I've tested the code locally and hope it works for you.

Flask, SQLAlchemy, Foreign Keys

Hello I have some Problem with some foreign keys.
I have something like this:
class Foo(db.Model):
"""The foo object."""
__tablename__ = 'foos_foo'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.Text, unique=True)
content = db.Column(db.Text)
bar = db.relationship('Bar', backref='bars_bar')
bar_id = db.Column(db.Integer, db.ForeignKey('bars_bar.id'))
class Bar(db.Model):
"""The bar object."""
__tablename__ = 'bars_bar'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.Text, unique=True)
description = db.Column(db.Text)
status = db.Column(db.SmallInteger, default=NEW)
I use this configuration style: https://github.com/mitsuhiko/flask/wiki/Large-app-how-to
So I have something like this:
from app.foos.views import mod as foosModule
from app.bars.views import mod as barsModule
app.register_blueprint(foosModule)
app.register_blueprint(barsModule)
If I call like in the config style from Mitsuhiko, python shell.py, comes this error:
sqlalchemy.exc.OperationalError: (OperationalError) no such table: bars_bar u'SELECT bars_bar.id AS bars_bar_id, bars_bar.name AS bars_bar_name, bars_bar.description AS bars_bar_description, bars_bar.status AS bars_bar_status \nFROM bars_bar' ()
What goes on there? Yes the table is not there because I will create it? Is there a way to create some tables before some other? Or what do I wrong?
Thanks for your time!
Quoting Flask-SQLalchemy docs:
To create the initial database, just import the db object from a interactive Python shell and run the SQLAlchemy.create_all() method to create the tables and database:
from yourapplication import db
db.create_all()

Categories