Flask-alchemy many to many relationship between separate packages - flask

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?

Related

Why is my SQLalchemy database having attribute errors [duplicate]

I have two very simple models. In my Post model there are supposed to be two relationships into the User table. One is for the owner of the post and one is for the last editor of the post. They can be different values, but both refer to the same User table.
My models are set up like this
class Post(Base):
last_editor_id = Column(BigInteger, ForeignKey('users.id'), nullable=True)
last_editor = relationship('User', backref='posts', foreign_keys=[last_editor_id])
owner_id = Column(BigInteger, ForeignKey('users.id'), nullable=False, index=True)
owner = relationship('User', backref='posts', foreign_keys=[owner_id])
class User(Base):
'''This represents a user on the site'''
__tablename__ = 'users'
id = Column(BigInteger, primary_key=True, unique=True)
name = Column(BigInteger, nullable=False)
When I attempt to create these models though, I get the following error
sqlalchemy.exc.ArgumentError: Error creating backref 'posts' on relationship 'Post.owner': property of that name exists on mapper 'Mapper|User|users'
How do I correct this so that I can maintain both forgeign keys in the Post model?
The error is telling you that you've used post as a name more then once for your backrefs, all you need to do is give the backref's unique names. Here's a complete example-- I've added a id primary key to the Post class, and also some __repr__s so we get some readable output.
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, BigInteger, ForeignKey, Integer
from sqlalchemy.orm import relationship, sessionmaker
Base = declarative_base()
engine = create_engine('sqlite://') ## In Memory.
Session = sessionmaker()
Session.configure(bind=engine)
session = Session()
class Post(Base):
__tablename__ = 'post'
id = Column(Integer, primary_key=True)
last_editor_id = Column(BigInteger, ForeignKey('users.id'), nullable=True)
last_editor = relationship('User', backref='editor_posts', foreign_keys=[last_editor_id])
owner_id = Column(BigInteger, ForeignKey('users.id'), nullable=False, index=True)
owner = relationship('User', backref='owner_posts', foreign_keys=[owner_id])
def __repr__(self):
return '<Post: {}>'.format(self.id)
class User(Base):
'''This represents a user on the site'''
__tablename__ = 'users'
id = Column(BigInteger, primary_key=True, unique=True)
name = Column(BigInteger, nullable=False)
def __repr__(self):
return '<User: {}>'.format(self.name)
Base.metadata.create_all(engine)
bob = User(name='Bob', id=1)
alice = User(name='Alice', id=2)
post = Post(owner=alice, last_editor=bob, id=1)
session.add(post)
session.commit()
bob = session.query(User).get(1)
print bob
# <User: Bob>
print bob.editor_posts
# [<Post: 1>]
print bob.owner_posts
# []
post = session.query(Post).get(1)
print post.owner
# <User: Alice>
print post.last_editor
# <User: Bob>
Now when you query a user, you can ask that object user.owner_posts or user.editor_posts.
In general it's a naming Problem of the backref.
Since 1:n relationships are sometimes a bit confusing, I set the relationship attribute
always on the singular site, to avoid confusion.
then the backref name is always singular. and the relationship attribute is always in the Class where the foreignkey is referencing to.
Now to my suggestion for the fixed code:
class Post(Base):
last_editor_id = Column(BigInteger, ForeignKey('users.id'), nullable=True)
owner_id = Column(BigInteger, ForeignKey('users.id'), nullable=False, index=True)
class User(Base):
'''This represents a user on the site'''
__tablename__ = 'users'
id = Column(BigInteger, primary_key=True, unique=True)
name = Column(BigInteger, nullable=False)
owned_posts = relationship('Post', backref='owner')
edited_posts = relationship('Post', backref='last_editor')
Now you can get all the owned posts of a User with User.owned_posts and all owners of a post with Post.owner. Same with the last_edited attribute.
For additional info you could read the docs how to set up relationships

Can't get Association table to eager load with Movie for particlular User, to show if the User who added the movie is being followed or not Flask

class Movie(db.Model):
__searchable__ = ['genre']
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(200))
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
username = db.Column(db.String(255))
description = db.Column(db.String(100))
class User(db.Model,UserMixin):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String, nullable=False)
password = db.Column(db.String, nullable=False)
movies = db.relationship('Movie', backref='author', lazy='joined')
followed = db.relationship('User', secondary=followers,
primaryjoin=(followers.c.follower_id==id),
secondaryjoin=(followers.c.followed_id==id),
backref=db.backref('followers', lazy='joined'), lazy='joined')
followers = db.Table('followers',
db.Column('follower_id', db.Integer, db.ForeignKey('user.id'), primary_key = True),
db.Column('followed_id', db.Integer, db.ForeignKey('user.id'), primary_key = True) )
def get_all_movies_follow():
qry = Movie.query.order_by(Movie.id.desc()).all()
movies_schema = MovieSchema(many=True)
return movies_schema.dump(qry)
How to get back an collection (array) where I have a property that shows whether
When I eager_load followers table it's still won't show up in the query.
``` #movi = Movie.query.outerjoin(followers, followers.c.followed_id == Movie.user_id).options(contains_eager( Movie.us).contains_eager( 'followers')).order_by(Movie.id.desc()).all()
Also when I try to use follow unfollow function I get Select statement 'SELECT *
FROM followers, movie AS movie_1
WHERE followers.followed_id = movie_1.user_id' returned no FROM clauses due to auto-correlation; specify correlate(<tables>) to control correlation manually.
which in short is def unfollow(id):
...
current_user.unfollow(user)
db.session.commit()
def follow(self, user):
if not self.is_following(user):
self.followed.append(user)
def unfollow(self, user):
if self.is_following(user):
self.followed.remove(user)
def is_following(self, user):
return self.query.filter(followers.c.followed_id==user.id).count()>0
So I tried adding this to Movie class: ```following = column_property( exists().where(followers.c.followed_id==user_id)) but it has to be also restricted on current_user.id=followers.c.follower_id
I am thinking maybe statement that will be included when I query for the Movie
ok, so this works, granted that I included following in the schema to dump on MovieSchema.
But it has a problem. If there are no matches to (followers.c.follower_id) so user is not following anyone. then I get an empty result and no movie gets loaded at all.
class Movie(db.Model)
...
following = column_property( exists().where(followers.c.followed_id==user_id).correlate_except(followers))
and in query
qry = db.session.query(Movie).filter(followers.c.follower_id == current_user.id).all()

Flask: Querying inherited classes from SQLAlchemy brings different results in two similar contexts

I am working on a project with two different users: client, employee. Clients post and Employees confirm the posts or not. To do this I have on User class which deals mostly with authentication and basic procedures, and both the Client and Employee class inherit from it.
Practically:
class User(UserMixin, db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(120), index=True, unique=True)
password_hash = db.Column(db.String(128))
user_type = db.Column(db.String(32), nullable=False)
__mapper_args__ = {'polymorphic_identity': user_type}
class Client(User):
__tablename__ = 'client'
__mapper_args__ = {'polymorphic_identity':'client'}
id = db.Column(db.Integer, db.ForeignKey('user.id'), primary_key=True)
telephone = db.Column(db.String(120))
birthday = db.Column(db.String(120))
posts = db.relationship('Post', backref='author', lazy='dynamic')
class Employee(User):
__tablename__ = 'employee'
__mapper_args__ = {'polymorphic_identity':'employee'}
id = db.Column(db.Integer, db.ForeignKey('user.id'), primary_key=True)
employee_extra = db.Column(db.String(140))
When I want to get all the clients in one of my routes which is something like:
#bp.route('/posts', methods=['GET', 'POST'])
#login_required
def posts():
clients = Client.query.all()
print(clients[0].birthday)
I get results like: AttributeError: 'User' object has no attribute 'birthday'. because the result is [<User 2>].
On the other hand, when I set up an environment for testing, and I run the same query.
db_app.setUp()
# self.app = create_app(Config)
# self.app_context = self.app.app_context()
# self.app_context.push()
# db.create_all()
# self.client = self.app.test_client()
# db.session.commit()
db_app.create_user('11123123123','user#gmail.com','123123123','client')
print(Client.query.all())
It brings the necessary results: [<Client 2>]
I have read on different sources, and main documentation of SQLAlchemy, but cant really see where this difference comes from. What am I missing?

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

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