How to set ManyToOne relationship in Flask- SQL alchemy? - flask

I am new to Flask and SQLalchmey. I am trying to crate two tables and establish a manytoone relationship between them.
Here is what I did.
table_hub
class Hub(db.Model):
__tablename__ = 'hub'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(),nullable=False,unique=True)
tbl_vehicle
class vehileMaster(db.Model):
__tablename__ = 'res.vehicle'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, nullable=False)
hub_id = db.Column(db.Integer,db.ForeignKey('hub.id'))
hub = db.relationship('Hub')
Here is what I need to achieve,
while creating a new vehicle in table res.vehicle I need to choose a hub from the list of hubs.
I don't know whether what i already did is correct or not.

The standard way of implementing a foreign key relationship would be:
class Hub(db.Model):
__tablename__ = 'hub'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(), nullable=False,unique=True)
vehicles = db.relationship('res.vehicle', backref='hub', lazy=True)
class vehicleMaster(db.Model):
__tablename__ = 'res.vehicle'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, nullable=False)
hub_id = db.Column(db.Integer, db.ForeignKey('hub.id'))

Related

How to add relationship instances of a model on sqlalchemy's before_insert event?

I want to add instances of a model's relationship when an instance of this model is created.
While before_insert sqlalchemy event allows to set simple attribute, it does not seem to work with relationship.
Is it possible to do that with this event ? What is the standard way to achieve that ?
audio_project_rel = db.Table(
'audio_project_rel',
db.Column('project_id', db.Integer, db.ForeignKey('project.id'), primary_key=True),
db.Column('audio_id', db.Integer, db.ForeignKey('audio.id'), primary_key=True)
)
class Audio(db.Model):
id = db.Column(db.Integer, primary_key=True)
path = db.Column(db.String, unique=True, nullable=False)
class Project(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, unique=True, nullable=False)
audiolist_filename = db.Column(db.String, nullable=False)
audios = db.relationship('Audio',
secondary=audio_project_rel,
lazy=True,
backref=db.backref('projects', lazy=True))
#event.listens_for(Project, 'before_insert')
def get_audiolist_from_file(mapper, connection, project):
with open(project.audiolist_filename, 'r') as audiolist_file:
for line in audiolist_file:
_path = line.strip()
audio = Audio.query.filter(Audio.path==_path).first()
if not audio:
audio = Audio()
audio.path = _path
project.audios.append(audio) # not added
project.name = 'somename' # added
It seems that it is not possible:
https://docs.sqlalchemy.org/en/latest/orm/session_events.html#session-persistence-mapper

flask-sqlalchemy db.create_all() not creating helper/auxiliary table

I am using flask-sqlalchemy in my application and have created below models. I am creating many to many relationship for Users and Subscription table, and tried to create helper table as User_Subscription.
After creating this model, when i run db.create_all() in command line, it is creating only 3 tables in database (tables having db.Model) but not creating helper table (User_Subscription) at all. It is not giving any error either.
Could someone please advise what's going wrong here?
I already searched a lot on google and stackoverflow, but I can't find answer where anyone has faced this problem where helper table (via db.table()) is not being created.
There was one issue from someone where they had somewhat similar problem, but he was facing as he wanted to do across multiple database.
I am doing on same database.
class Subscription(db.Model):
__tablename__ = "subscription"
id = db.Column(db.Integer, primary_key=True)
subscription_name = db.Column(db.String(100), unique=True, nullable=False)
User_Subscription = db.table('user_subscription',
db.Column('user_id', db.Integer, db.ForeignKey('users.id')),
db.Column('subscription_id', db.Integer, db.ForeignKey('subscription.id')),
db.Column('subscription_status', db.String(20), nullable=False, default='Active'))
class Users(db.Model, UserMixin):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
firstname = db.Column(db.String(50), unique=False, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password = db.Column(db.String(60), nullable=False)
date_created = db.Column(db.DateTime, default=datetime.datetime.utcnow)
date_updated = db.Column(db.DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)
user_status_id = db.Column(db.String(2), db.ForeignKey('user_status.id'))
subscriptions = db.relationship('Subscription', secondary=User_Subscription, backref= db.backref('subscriptions', lazy=True))
def __repr__(self):
return f"Users('{self.firstname}', '{self.email}')"
class User_Status(db.Model):
__tablename__ = "user_status"
id = db.Column(db.String(2), primary_key=True)
status_desc = db.Column(db.String(20), unique=True, nullable=False)
users_status = db.relationship('Users', backref='usersofstatus')
Expected Result - 4 tables to be created in database.
Actual Result - Only 3 tables are being created. User_Subscription (Helper table for many to many relationship) is not being created.
After few hours of frustration, i found that i had a typo, i was using db.table instead of db.Table. So posting this answer in case it can help someone.
New to SQLAlchemy myself but it appears you are not inheriting from db.Model when creating User_Subscription. Why not make it a class?
class User_Subscription(db.Model):

Flask-SQLAlchemy wont create tables and Foreign Key

I have a model that wont create the table in the database as shown below
class Person(db.Model):
__tablename__ = 'persons'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(20))
pets = db.relationship('Pet', backref='person')
class Pet(db.Model):
__tablename__ = 'pets'
id = db.Column(db.Integer, primary_key=True)
name=db.Column(db.String(20))
owner_id = db.Column(db.Integer, db.ForeignKey('person.id'),nullable=False)
Might the issue be because the class name and the table name do not match since using the command
db.create_all()
throws an error??
Change this
owner_id = db.Column(db.Integer, db.ForeignKey('person.id'),nullable=False)
to
owner_id = db.Column(db.Integer, db.ForeignKey('persons.id'),nullable=False)
since your table is persons
And give this relationship a different name since you already have a table called pets
pets = db.relationship('Pet', backref='person')
you could for example do this
persons_pets = db.relationship('Pet', backref='person')

Selecting an item by its associated tables

I'm working on building a recipe database. I'm trying to build a query wehere I get all recipies that include a certain ingredient (such as onions, carrots), but I'm not how build my query. Essentally I'm trying to get a list of recipies that (given the proper amount of joins) have an Ingredient.name = 'onion'. My models are as follows:
ingredients = db.Table('ingredients',
db.Column('modified_ingredient', db.Integer, db.ForeignKey('modified_ingredient.id')),
db.Column('ingredient', db.Integer, db.ForeignKey('ingredient.id'))
)
modifiers = db.Table('modifiers',
db.Column('modified_ingredient', db.Integer, db.ForeignKey('modified_ingredient.id')),
db.Column('modifier', db.Integer, db.ForeignKey('modifier.id'))
)
modified_ingredients = db.Table('modified_ingredients',
db.Column('recipe', db.Integer, db.ForeignKey('recipe.id')),
db.Column('modified_ingredient', db.Integer, db.ForeignKey('modified_ingredient.id'))
)
class Recipe(db.Model):
__tablename__ = 'recipe'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(256))
description = db.Column(db.Text)
directions = db.Column(db.Text)
prep_time = db.Column(db.Integer)
cook_time = db.Column(db.Integer)
image = db.Column(db.LargeBinary())
ingredients = db.relationship('ModifiedIngredient', secondary=modified_ingredients)
class Ingredient(db.Model):
__tablename__ = 'ingredient'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(30), index=True, unique=True)
class Modifier(db.Model):
__tablename__ = 'modifier'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(30), index=True, unique=True)
class ModifiedIngredient(db.Model):
__tablename__ = 'modified_ingredient'
id = db.Column(db.Integer, primary_key=True)
amount = db.Column(db.Integer)
unit = db.Column(db.String(20))
ingredients = db.relationship('Ingredient', secondary=ingredients,
backref=db.backref('ingredients', lazy='dynamic'), lazy='dynamic')
modifiers = db.relationship('Modifier', secondary=modifiers,
backref=db.backref('modifiers', lazy='dynamic'), lazy='dynamic')
It's mostly my inexperience with SQL and SQLAlchemy that is stumping me. I know that I'm joining something, but I'm not exactly sure how to phrase it in a way that works.
Option-1: very tidy, but might not be the most efficient due to nested EXISTS clause:
q = (db.session.query(Recipe)
.filter(Recipe.ingredients.any(
ModifiedIngredient.ingredients.any(
Ingredient.name == 'onion')
)))
Option-2: should be faster, but if you query only certain columns (use query(Recipe.name, ..) instead of whole objects as below), you will end with with multiple results per each Recipe row because of JOINs:
q = (db.session.query(Recipe)
.join(Recipe.ingredients)
.join(Ingredient, ModifiedIngredient.ingredients)
.filter(Ingredient.name == 'onion')
)

Mapper Errors while creating Rows

I'm working on building a recipe database. In this, there are ingredients (like onions, carrots, etc) and modifiers (like diced, peeled, etc) which are in part of ModifiedIngredients which is part of a recipe. My models.py is as follows:
from app import db
modifiers = db.Table('modifiers',
db.Column('modified_ingredient', db.Integer, db.ForeignKey('modified_ingredient.id')),
db.Column('modifier', db.Integer, db.ForeignKey('modifier.id'))
)
modified_ingredients = db.Table('modified_ingredients',
db.Column('recipe', db.Integer, db.ForeignKey('recipe.id')),
db.Column('modified_ingredient', db.Integer, db.ForeignKey('modified_ingredient.id'))
)
class Recipe(db.Model):
__tablename__ = 'recipe'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(256))
description = db.Column(db.Text)
directions = db.Column(db.Text)
prep_time = db.Column(db.Integer)
cook_time = db.Column(db.Integer)
image = db.Column(db.LargeBinary())
ingredients = db.relationship('modified_ingredient', secondary=modified_ingredients)
class Ingredient(db.Model):
__tablename__ = 'ingredient'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(30), index=True, unique=True)
class Modifier(db.Model):
__tablename__ = 'modifier'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(30), index=True, unique=True)
class ModifiedIngredient(db.Model):
__tablename__ = 'modified_ingredient'
id = db.Column(db.Integer, primary_key=True)
amount = db.Column(db.Integer)
unit = db.Column(db.String(20))
ingredients = db.relationship('ingredient', backref='ingredient', lazy='dynamic')
modifiers = db.relationship('Modifier', secondary=modifiers,
backref=db.backref('modifiers', lazy='dynamic'), lazy='dynamic')
recipe = db.Column(db.Integer, db.ForeignKey('recipe.id'))
However, if I open up a shell and enter in something like "onion = models.Ingredient(name='Onion')" I get the error:
sqlalchemy.exc.InvalidRequestError: One or more mappers failed to initialize - can't proceed with initialization of other mappers. Original exception was: relationship 'ingredients' expects a class or a mapper argument (received: <class 'sqlalchemy.sql.schema.Table'>)
I'm not exactly sure where my setup is going wrong.
Thanks.
if i'm reading the error message correctly then
ingredients = db.relationship('modified_ingredient', secondary=modified_ingredients)
should become
ingredients = db.relationship('ModifiedIngredient', secondary=modified_ingredients)
Note in the error message:
Original exception was: relationship 'ingredients' expects a class or a mapper argument (received: <class 'sqlalchemy.sql.schema.Table'>)
If it expects a class, give it a class :)