Flask-Sqlalchemy multiple databases (binds) with the same model (class DB.Model) - python

I'm a beginner with python/Flask/SQLAlchemy so sorry if my questions are dumb.
I want to create an API with Flask using Flask-SQLAlchemy as following:
one sqlite database for users/passwords
SQLALCHEMY_DATABASE_URI = 'sqlite:////path/to/users.db'
class User(DB.Model):
__tablename__ = 'users'
id = DB.Column(DB.Integer, primary_key=True)
username = DB.Column(DB.String(64), index=True)
password = DB.Column(DB.String(128))
Lets say I have multiple "customers" witch a user can create using
$ http POST http://localhost:5000/api/customers/ name=customer1
class Customer(DB.Model):
__tablename__ = 'customer'
customer_id = DB.Column(DB.Integer, primary_key=True)
customer_name = DB.Column(DB.String, unique=True, index=True)
I need to create a separate sqlite file for each "customers" :
SQLALCHEMY_BINDS = {
'customer1' = 'sqlite:////path/customer1.db',
'customer2' = 'sqlite:////path/customer2.db',
...
}
My questions are:
I do not have fixed number of "customers" so I cannot create a model class for each and specify the "bind_key" for each. Is it possible to do this with Flask-SQLAlchemy or I need to use plain SQLAlchemy?
I have 3 "customers" in data/ as customer1.db, customer2.db and customer3.db.
I would start the application and create SQLALCHEMY_BINDS dictionary listing the files in data/ and then DB.create_all() on a request for a specific "customer" .
how can I bind to the right .db file using the Flask-SQLAlchemy
DB.session?
I've read Using different binds in the same class in Flask-SQLAlchemy

Why exactly do you want entirely separate DB files for each customer?
In any case this is easier with straight SQLAlchemy. You can create a getter function which returns a session pointing to your db file.
def get_session(customer_id):
sqlite_url = 'sqlite:////path/customer%s.db' % customer_id
engine = create_engine(sqlite_url)
# initialize the db if it hasn't yet been initialized
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
return session
You can then use and close that session.
But without knowing your specific use case, it is difficult to understand why you would want to do this instead of just using a single SQLite database.

Related

Why use "db.*" in SqlAlchemy?

I am working on a project using Flask and SqlAlchemy. Me and my colleagues found two ways to define a table. Both work, but what is the different?
Possibility I
base = declarative_base()
class Story(base):
__tablename__ = 'stories'
user_id = Column(Integer, primary_key=True)
email = Column(String(100), unique=True)
password = Column(String(100), unique=True)
Possibility II
db = SQLAlchemy()
class Story(db.Model):
__tablename__ = 'stories'
user_id = db.Column(Integer, primary_key=True)
email = db.Column(String(100), unique=True)
password = db.Column(String(100), unique=True)
We want to choose one option, but which one?
It is obvious that both classes inherit from a different parent class, but for what are these two possibilities used for?
Possibility 1 is raw SQLAlchemy declarative mapping.
Possibility 2 is Flask-SQLAlchemy.
Both map a class to SQL table (or something more exotic in SQL) in a declarative style, i.e. the class is mapped to an automatically generated table.
Choosing which one to use however is a matter of opinion.
I'll say that using Flask-SQLAlchemy is obviously locking the application to Flask, but that's basically a non-problem since switching frameworks is very uncommon.
NB. __tablename__ is optional with Flask-SQLAlchemy.

How create Postgres database using SQLAlchemy

I want write project using database library (SQLAlchemy). When I use SQLite, all works good, but when I deploy project on server (Heroku with Postgres plugin), It doesn't work. This is code for my database:
class UserBase(Base):
__tablename__ = 'users'
id = Column(Integer, Sequence('user_id_seq'), primary_key=True)
nickname = Column(String(50), unique=True, nullable=False)
server = Column(Integer)
language = Column(String(2))
def __repr__(self):
return "<UserBase(nickname='%s', server='%s', language='%s')>" % \
(self.nickname, self.server, self.language)
I have engine & Base variable:
engine = create_engine(os.environ.get('DATABASE_URL'))
Base = declarative_base()
In DATABASE_URL link to base on Heroku(Postgres). For create database I write in Interactive Python Console:
from user_base import Base, engine
Base.metadata.create_all(engine)
After these manipulations in the server logs it gives this:
sqlalchemy.exc.DataError: (psycopg2.errors.NumericValueOutOfRange) integer out of range
How to fix problem? If you needs in info, asking.
I set type of server as String, indicated that the server is a string and this is helped me.

Pre-populate a Flask SQLAlchemy database

I have two models, one is Identification which contains two IDs (first_id and second_id) and the second is User. The idea is that only authorised users will be given their first_id and second_id pair of values. They go to the site and login by entering the two id's plus a username and password (which they generate there and then).
I am trying to achieve two things here:
Pre-populate the Identification table with many (let's say 100) first_id/second_id values that will serve as the correct value pairs for logging in.
Set up the User class in such a way that only if the user enters a correct first_id/second_id pair in the login form can they log in (presumable this involves checking the form data with the Identification table somehow).
Here are the model classes:
class Identification(db.Model):
id = db.Column(db.Integer, primary_key=True)
first_id= db.Column(db.Text, unique=True)
second_id= db.Column(db.Text, unique=True)
def __init__(self, first_id, second_id):
self.first_id= first_id
self.second_id= second_id
def __repr__(self):
return f"ID: {self.id}, first_id: {self.first_id}, second_id: {self.second_id}"
class User(db.Model, UserMixin):
__tablename__ = 'user'
first_id= db.relationship('Identification', backref = 'identificationFID', uselist=False)
second_id = db.relationship('Identification', backref = 'identificationSID', uselist=False)
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.Text, unique=True, index=True)
password_hash = db.Column(db.Text(128))
identification_id = db.Column(db.Integer, db.ForeignKey('identification.id'), unique=True)
first_id = db.Column(db.Text, unique=True)
second_id = db.Column(db.Text, unique=True)
I would appreciate any help on this as I'm struggling and this is really above my understanding of python/Flask. Thanks all!
The answer above didn't work for me, because the create_tables() function since being part of the User class, requested that I pass an Instance of that class.
The solution I came up with, was to call the function after db.create_all(). This seemed like a good place to put the call, because of the #app.before_first_request decorator.
init.py
#app.before_first_request
def create_tables():
"""Create Tables and populate certain ones"""
db.create_all()
from app.models.init_defaults import init_defaults
init_defaults()
init_defaults.py
def init_defaults():
"""Pre-Populate Role Table"""
if Role.query.first() is None:
roles = ['Admin', 'Master', 'Apprentice']
for role in roles:
user_role = Role(access_level=role)
if role != 'Apprentice':
user_role.set_password('Passw0rd!')
db.session.add(user_role)
db.session.commit()
pass
Due to the decorator the function is now only called once per instance. Another solution I could imagine working, would be to use events:
https://dzone.com/articles/how-to-initialize-database-with-default-values-in
Note: This is a development solution not fit for production.
You can use mock data to populate these tables.
create a function in this py file where you can add objects to DB using ORM
and then call the function in __init__.py, which will populate data once your flask server starts.
Update:-
here is a code for your reference.
Model.py
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import sessionmaker
Base = declarative_base()
engine = create_engine('sqlite:///:memory:', echo=True)
Session = sessionmaker(bind=engine)
session = Session()
class User(Base):
\__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
fullname = Column(String)
#Create getter setters
def create_tables():
Base.metadata.create_all(engine)
user = User()
user.id=1
user.name="ABC"
user.fullname="ABCDEF"
session.add(user)
# similarly create more user objects with mock data and add it using session
__init__.py
from model import User
User.create_tables()
Reference

Linking databases for a management system using Flask SQLAlchemy

I'm trying to build a very basic management system website for a hypothetical insurance agency and i just cant wrap my head around how i should organize the database to make it so i can assign users to specific policies and have the ability to update/replace the user in case there are re-arrangements within the agency so policies can be reassigned to the proper agents. This would be used to display data based on login as well. There's 3 layers that i think i need. A User table for user data, a client data/policy table to store client and policy info, and then a table for tasks that would be assigned to policies. I need multiple users to have access to a policy and then the policy should have access to 1 row in the task table. Would it just be better to have a user table and large client table with the task columns inside rather than a separate table for the tasks? I've been banging my head with this for days so if anyone can help, i greatly appreciate it.
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SECRET_KEY'] = ''
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
username = db.Column(db.String(50), unique=True)
password = db.Column(db.String(50))
email = db.Column(db.String(50), unique=True)
#Multiple assigned users can access
class Client(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
policy_number = db.Column(db.String(50), unique = True)
expiration_date = db.Column(db.Datetime)
#Single "client" assigned to single row of tasks based on policy number
class PolicyTasks(db.Model):
id = db.Column(db.Integer, primary_key=True)
step1 = db.Column(db.String(50))
step1_completed = db.Column(db.Boolean)
step2 = db.Column(db.String(50))
step2_completed = db.Column(db.Boolean)
step3 = db.Column(db.String(50))
step3_completed = db.Column(db.Boolean)
step4 = db.Column(db.String(50))
step4_completed = db.Column(db.Boolean)
step5 = db.Column(db.String(50))
step5_completed = db.Column(db.Boolean)
I removed the code i used to attempt to create the relationships because it might honestly be more helpful to look at the base layout
To wrap your head around how you should organize the database to make it so you can assign users to specific policies, take a look at the docs on the Flask website. I think the second example under One-to-Many is very similar to what you're looking to do http://flask-sqlalchemy.pocoo.org/2.3/models/.
I would recommend keeping the tables separate and not jamming it all into one. Usually that leads to a slippy slope (in my experience) with tables with too many attributes to manage.
It can also mean slower query times because Client.query.filter_by(username='peter').first() would then always query the client data, policy data and whatever else you later on end up throwing in that table, when you may've only needed the policy data for that specific view/api route.
This other stackoverflow post might help too:
database design - when to split tables?

How to ignore some models to migrate? [duplicate]

I am using Flask-SQLAlchemy to define my models, and then using Flask-Migrate to auto-generate migration scripts for deployment onto a PostgreSQL database. I have defined a number of SQL Views on the database that I use in my application like below.
However, Flask-Migrate now generates a migration file for the view as it thinks it's a table. How do I correctly get Flask-Migrate / Alembic to ignore the view during autogenerate?
SQL View name: vw_SampleView with two columns: id and rowcount.
class ViewSampleView(db.Model):
__tablename__ = 'vw_report_high_level_count'
info = dict(is_view=True)
id = db.Column(db.String(), primary_key=True)
rowcount = db.Column(db.Integer(), nullable=False)
Which means I can now do queries like so:
ViewSampleView.query.all()
I tried following instructions on http://alembic.zzzcomputing.com/en/latest/cookbook.html and added the info = dict(is_view=True) portion to my model and the following bits to my env.py file, but don't know where to go from here.
def include_object(object, name, type_, reflected, compare_to):
"""
Exclude views from Alembic's consideration.
"""
return not object.info.get('is_view', False)
...
context.configure(url=url,include_object = include_object)
I think (though haven't tested) that you can mark your Table as a view with the __table_args__ attribute:
class ViewSampleView(db.Model):
__tablename__ = 'vw_report_high_level_count'
__table_args__ = {'info': dict(is_view=True)}
id = db.Column(db.String(), primary_key=True)
rowcount = db.Column(db.Integer(), nullable=False)

Categories

Resources