Mapper Errors while creating Rows - flask

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 :)

Related

Flask-alchemy many to many relationship between separate packages

I have two tables Member and Posts. Respective classes are declared into separate models.py modules, that belong to separate packages members and posts.
post package - models .py
class Post(db.Model):
__tablename__ = 'posts'
id = db.Column(db.Integer(), primary_key=True)
subject = db.Column(db.Text, nullable=False, unique=True, index=True)
body = db.Column(db.Text)
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
category_id = db.Column(db.Integer(), db.ForeignKey('categories.id'))
tags = db.relationship('Tag', secondary=post_tags, lazy='subquery', backref=db.backref('post'))
authors = db.relationship('Member', secondary=member_posts, lazy='subquery', backref=db.backref('post'))
def __init__ (self,**kwargs):
super(Post, self).__init__(**kwargs)
members package - models .py
class Member(db.Model):
__tablename__ = 'members'
id = db.Column(db.Integer, primary_key=True)
first_name = db.Column(db.String(50, collation='NOCASE'), nullable=False)
last_name = db.Column(db.String(50, collation='NOCASE'), nullable=False)
phone = db.Column(db.String(15))
twitter = db.Column(db.String(15))
avatar = db.Column(db.String(255), default='avatar.png')
# Relationships
field = db.relationship('Field', secondary=members_fields, lazy='subquery', backref=db.backref('member'))
position = db.relationship('Position', secondary=members_positions, lazy='subquery', backref=db.backref('member'))
def __init__ (self,**kwargs):
super(Member, self).__init__(**kwargs)
Those tables are connected with Many to Many relationship, and associative table member_posts is located along with posts class.
posts package - models .py
member_posts = db.Table('member_posts',
db.Column('member_id', db.Integer, db.ForeignKey('members.id'), primary_key=True),
db.Column('post_id', db.Integer, db.ForeignKey('posts.id'), primary_key=True)
)
When I send send request, debugger throws the following error:
sqlalchemy.exc.InvalidRequestError: When initializing mapper mapped class Post->posts, expression 'Member' failed to locate a name ('Member'). If this is a class name, consider adding this relationship() to the <class 'eierc.posts.models.Post'> class after both dependent classes have been defined.
Can anyone explain, hat is the problem and how to fix it?

How to set ManyToOne relationship in Flask- SQL alchemy?

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'))

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

Select many to many using function count()

I have many to many relationships and i try to find User which has a minimum requests im my subs table but i can't understand how i can do it.
Could you please clarify how i can do it
my Models are:
subs = db.Table('subs',
db.Column('user_id', db.Integer, db.ForeignKey('user.id')),
db.Column('request_id', db.Integer, db.ForeignKey('request.id'))
)
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(120))
role = db.Column(db.String(120))
password_hash = db.Column(db.String(120))
requests = db.relationship('Request', secondary=subs,
backref=db.backref('users', lazy='dynamic'))
post = db.relationship('Posts', backref = 'user', lazy = 'dynamic')
request = db.relationship('Request', backref='user', lazy = 'dynamic')
is_active = db.Column(db.String(120))
class Request(db.Model):
__tablename__ = 'request'
id = db.Column(db.Integer, primary_key=True)
org = db.Column(db.String(120))
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
cost = db.Column(db.Integer)
created = db.Column(db.DateTime, default= datetime.utcnow)
cost_time = db.Column(db.Integer)
update_time = db.Column(db.DateTime, default = datetime.utcnow())
diff_time = db.Column(db.DateTime)
feedback = db.Column(db.Text, default=update_time)
comment = db.relationship('Posts', backref = 'request', lazy='dynamic')
rate_idea = db.Column(db.Integer)
new = db.Column(db.Text)
cost_buyer = db.relationship('Costs', backref = 'request', lazy='dynamic')
status = db.Column(db.String(120), db.ForeignKey('status.id'))
For example:
User1.requests = [Request_1, 'Request_2, Request_3]
User2.requests = [Request_2, Request_3]
When somebody do a new Request i need to clarify firstly which user has a minimum requests from all of users and then put this request to him.
New_request = Request(org = 'TEST')
In this case User2 must add this New_request to his own User.requests so the final result must be
User1.requests = [Request_1, 'Request_2, Request_3]
User2.requests = [Request_2, Request_3, New_request]
i want to do query something like this, but what is the right and simple solution for this i don't know and i want to know:
db.query.filter(min(len(User.requests))
Something like this should work. But I suggest you to check the docs.
from sqlalchemy import func
db.query(User.id, func.count())
.outerjoin(User.requests)
.group_by(User.id)
.order_by(func.count())
.limit(1)

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')
)