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.
Related
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
I'm working with flask and trying to learn sqlalchemy, I have a database where I added a third table to make a new feature, but the entire project stopped working, it says:
sqlalchemy.exc.NoReferencedTableError: Foreign key associated with column 'url_reference.folder_rel' could not find table 'reffolders' with which to generate a foreign key to target column 'id'
this is the dbs.py
from . import db
from flask_login import UserMixin
from sqlalchemy.sql import func
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(200), unique=True)
user_password = db.Column(db.String(150))
user_name = db.Column(db.String(400))
user_refs = db.relationship("RefFolders")
class RefFolders(db.Model):
id = db.Column(db.Integer, primary_key=True)
folder_title = db.Column(db.String(1000))
date_added = db.Column(db.DateTime(timezone=True), default=func.now())
user_rel = db.Column(db.Integer, db.ForeignKey("user.id"))
url_rel = db.relationship("UrlReference")
class UrlReference(db.Model):
id = db.Column(db.Integer, primary_key=True)
ref_title = db.Column(db.String(1000))
ref_url = db.Column(db.String(30000))
date_added = db.Column(db.DateTime(timezone=True), default=func.now())
folder_rel = db.Column(db.Integer, db.ForeignKey("reffolders.id"))
this is where I make the database in the __init__.py:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from os import path
from flask_login import LoginManager
db = SQLAlchemy()
DB_NAME = "database.db"
def app():
app = Flask(__name__)
app.config['SECRET_KEY'] = '<here is the key in my code>'
app.config['SQLALCHEMY_DATABASE_URI'] = f"sqlite:///{DB_NAME}"
db.init_app(app)
# making the blueprints usable
from .views import views
from .auth import auth
app.register_blueprint(views, url_prefix="/")
app.register_blueprint(auth, url_prefix="/")
from .dbs import User
from .dbs import UrlReference
from .dbs import RefFolders
db_create(app)
# Login manager
login_manager= LoginManager()
login_manager.login_view = 'views.home'
login_manager.init_app(app)
# Searching and loading user
#login_manager.user_loader
def load_user(id):
return User.query.get(int(id))
return app
def db_create(app):
if not path.exists("project/" + DB_NAME):
db.create_all(app=app)
I don't know what happened, maybe a problem with having a double foreign key?
I think flask or its extension guesses the table name you want, like CamelCase --> camel_case.
So in this case the tablename is probably ref_folders. You might want to just use tablename to set the table name yourself although I think their default is a good convention.
As a side note I would recommend using _id for your ids otherwise things might get pretty confusing between object relations and actual ids, ie. use user_id instead of user_rel in RefFolders.
SEE:
https://flask-sqlalchemy.palletsprojects.com/en/2.x/models/#declaring-models
Some parts that are required in SQLAlchemy are optional in
Flask-SQLAlchemy. For instance the table name is automatically set for
you unless overridden. It’s derived from the class name converted to
lowercase and with “CamelCase” converted to “camel_case”. To override
the table name, set the __tablename__ class attribute.
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.
I have a model being used by my flask server as well as a command-line tool. I want both the script and flask app to be able to create tables programmatically using this model.
In the flask app, I do this:
from models import db
db.app = app
db.init_app(app)
db.create_all()
However I can't use this code in the command-line tool as there is no 'app', and without the app I get
RuntimeError: application not registered on db instance and no application bound to current context
Which makes sense. So how do I make it work with the command-line tool without creating another version of my model file?
My model file (roughly):
class User(db.Model):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
account_id = Column(BigInteger, nullable=False)
team_name = Column(String, nullable=False)
external_id = Column(String(36), nullable=False)
# should not be filled with web app, but for misc notes we take later
notes = Column(String)
def __str__(self):
return "User (account_id={}, team_name='{}', external_id='{}')".format(
self.account_id, self.team_name, self.external_id)
__table_args__ = (
UniqueConstraint("account_id", name="unique_account_id"),
UniqueConstraint("external_id", name="unique_external_id"),
UniqueConstraint("team_name", name="unique_team_name")
)
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)