I'm using flask-sqlalchemy, this is not the first relations that i've built, but for some reason it gives me an error when i start flask:
sqlalchemy.exc.InvalidRequestError: One or more mappers failed to initialize - can't proceed with initialization of other mappers. Triggering mapper: 'mapped class User->users'. Original exception was: 'Table' object has no attribute 'sender_id'
These are two models that i'm trying to connect via ForeignKeys:
User:
class User(UserMixin, db.Model):
__tablename__ = 'users'
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))
about_me = db.Column(db.String(140))
last_seen = db.Column(db.DateTime, default=datetime.utcnow)
#relations
posts = db.relationship('Post', back_populates='author', lazy='dynamic')
messages_sent = db.relationship('Message', foreign_keys='messages.sender_id',
back_populates='author', lazy='dynamic')
messages_received = db.relationship('Message', foreign_keys='messages.recipient_id',
back_populates='recipient', lazy='dynamic')
last_message_read_time = db.Column(db.DateTime)
followed = db.relationship(
'User', secondary=followers,
primaryjoin=(followers.c.follower_id == id),
secondaryjoin=(followers.c.followed_id == id),
backref=db.backref('followers', lazy='dynamic'), lazy='dynamic')
def __repr__(self):
return '<User {}>'.format(self.username)
And Messsage:
class Message(db.Model):
__tablename__ = 'messages'
id = db.Column(db.Integer, primary_key=True)
sender_id = db.Column(db.Integer, db.ForeignKey('users.id'))
recipient_id = db.Column(db.Integer, db.ForeignKey('users.id'))
body = db.Column(db.String(140))
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
#relations
author = db.relationship('User', back_populates='messages_sent', lazy='dynamic')
recipient = db.relationship('User', back_populates='messages_received', lazy='dynamic')
def __repr__(self):
return '<Message {}>'.format(self.body)
I've checked the database itself and it certainly has sender_id in the correct table, tried to change "foreign_keys" parameter to directly name of model Message.sender... Tried to change parameters of lazy on those relations. It still gives me same error.
This is subtle but messages in this case is a table so columns are referenced off of c, like messages.c.sender_id. To use the column of the model class (the mapped class) you would do Message.sender_id.
So...
messages_sent = db.relationship('Message', foreign_keys='messages.c.sender_id',
back_populates='author', lazy='dynamic')
# OR
messages_sent = db.relationship('Message', foreign_keys='Message.sender_id',
back_populates='author', lazy='dynamic')
There is some information here but it doesn't explain the table vs class situation: handling-multiple-join-paths I think I would just use ORM style references until you are more comfortable and then you could use table references if needed.
Related
from yff import db, login_manager, app
from flask_login import UserMixin
import datetime
import jwt
#login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True, nullable=False)
password = db.Column(db.String(120), unique=False, nullable=False)
email = db.Column(db.String(50), unique=False, nullable=False)
profile_pic = db.Column(db.String(30), unique=False, default="default.jpg")
is_moderator = db.Column(db.Boolean, default=False)
posts = db.relationship('Posts', backref='author', lazy=True)
likes = db.relationship('Likes', backref='user', passive_deletes=True)
comments = db.relationship('Comment', backref='user', passive_deletes=True)
def get_reset_token(self):
encoded = jwt.encode({'user_id':self.id, "exp":datetime.datetime.now() + datetime.timedelta(hours = 0.5)}, app.config['SECRET_KEY'], algorithm='HS256')
return encoded
#staticmethod
def verify_secret_token(token):
try:
decoded = jwt.decode(token, options={"verify_signature": False})
user_id = decoded['user_id']
except:
return None
return User.query.get(user_id)
def __repr__(self):
return f'{self.username}, {self.email}'
class Image(db.Model):
id = db.Column(db.Integer, primary_key=True)
title=db.Column(db.String(120), nullable=False)
date_created = db.Column(db.DateTime, nullable=False, default=datetime.datetime.now(tz=datetime.timezone.utc))
img_location = db.Column(db.String(600), nullable=False)
mimetype = db.Column(db.String(10))
post_name = db.Column(db.String(150),nullable=False, unique=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
manga_name = db.Column(db.String(100), unique=False, nullable=False)
class Posts(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
post_id = db.Column(db.Integer, db.ForeignKey('image.id', ondelete="CASCADE"), nullable=False)
likes = db.Column(db.Integer, db.ForeignKey('likes.id', ondelete='CASCADE'))
date_created = db.Column(db.DateTime, nullable=False, default=datetime.datetime.now(tz=datetime.timezone.utc))
comments = db.relationship('Comment', backref='Posts', passive_deletes=True)
class Likes(db.Model):
id = db.Column(db.Integer, primary_key=True)
author = db.Column(db.Integer, db.ForeignKey('user.id', ondelete="CASCADE"), nullable=False)
post_id = db.Column(db.Integer, db.ForeignKey('image.id', ondelete="CASCADE"), nullable=False)
liked_post = db.relationship('Posts', backref='post')
class Comment(db.Model): #import it in the __init__ file
id = db.Column(db.Integer, primary_key=True)
text = db.Column(db.String(1000), nullable=False)
date_created = db.Column(db.DateTime, nullable=False, default=datetime.datetime.now(tz=datetime.timezone.utc))
author = db.Column(db.Integer, db.ForeignKey('user.id', ondelete="CASCADE"), nullable=False)
post_id = db.Column(db.Integer, db.ForeignKey('image.id', ondelete="CASCADE"), nullable=False)
The following is the entire error:
sqlalchemy.exc.InvalidRequestError: One or more mappers failed to initialize - can't proceed with initialization of other mappers. Triggering mapper: 'mapped class Posts->posts'. Original exception was: Could not determine join condition between parent/child tables on relationship Posts.comments - there are no foreign keys linking these tables. Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or specify a 'primaryjoin' expression.
I am getting this error in the pythonanywhere error log. I tried changing the positions of the backref statement but I that did not help. (I am new to web dev and I still haven't figured out how backref works)
I know how foreign keys work in SQL but I am still not experienced at OOP and SQLAlchemy, so please help me out.
So i been trying to make a like function for my q&a website. however, i'm stuck on database relations part of the models.py. I'm getting an error that says
"sqlalchemy.exc.InvalidRequestError: One or more mappers failed to initialize - can't proceed with initialization of other mappers. Triggering mapper: 'mapped class User->user'. Original exception was: Could not determine join condition between parent/child tables on relationship User.posts - there are multiple foreign key paths linking the tables. Specify the 'foreign_keys' argument, providing a list of those columns which should be counted as containing a foreign key reference to the parent table."
This is my code for the user and post class
class Post(db.Model):
id = db.Column("id", db.Integer, primary_key=True)
title = db.Column("title", db.String(200))
text = db.Column("text", db.String(100))
date = db.Column("date", db.String(50))
#Create Foreign Key
user_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False)
comments = db.relationship("Comment", backref="post", cascade="all, delete-orphan", lazy=True)
recipient_id = db.Column(db.Integer, db.ForeignKey('user.id'))
likes = db.relationship('PostLike', backref='post', lazy='dynamic')
and my user class
class User(db.Model):
id = db.Column("id", db.Integer, primary_key=True)
first_name = db.Column("first_name", db.String(100))
last_name = db.Column("last_name", db.String(100))
email = db.Column("email", db.String(100))
password = db.Column(db.String(255), nullable=False)
registered_on = db.Column(db.DateTime, nullable=False)
posts = db.relationship("Post", backref="user", lazy=True)
comments = db.relationship("Comment", backref="user", lazy=True)
liked = db.relationship(
'PostLike',
foreign_keys='PostLike.user_id',
backref='user', lazy='dynamic'
)
def like_post(self, post):
if not self.has_liked_post(post):
like = PostLike(user_id=self.id, post_id=post.id)
db.session.add(like)
def unlike_post(self, post):
if self.has_liked_post(post):
PostLike.query.filter_by(
user_id=self.id,
post_id=post.id).delete()
def has_liked_post(self, post):
return PostLike.query.filter(
PostLike.user_id == self.id,
PostLike.post_id == post.id).count() > 0
my postlike class in the models.py
class PostLike(db.Model):
__tablename__ = 'post_like'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
post_id = db.Column(db.Integer, db.ForeignKey('post.id'))
my flask file for like_action function
#app.route('/like/<int:post_id>/<action>')
def like_action(post_id, action):
post = Post.query.filter_by(id=post_id).first_or_404()
if action == 'like':
session['user_id'].like_post(post)
db.session.commit()
if action == 'unlike':
session['user_id'].unlike_post(post)
db.session.commit()
return redirect(request.referrer)
You have two foreign keys pointing to User on Post:
user_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False)
recipient_id = db.Column(db.Integer, db.ForeignKey('user.id'))
so, your User doesn't know where to point
posts = db.relationship("Post", backref="user", lazy=True)
Have something like recipient = db.relationship (..) and author = db.relationship (..) in User model, and make posts = db.relationship("Post", back_populates="author", lazy=True).
I have the following badge (achievement) system database structure:
class Base(db.Model):
__abstract__ = True
id = db.Column(db.Integer, primary_key=True)
date_created = db.Column(db.DateTime, default=db.func.current_timestamp())
date_modified = db.Column(db.DateTime, default=db.func.current_timestamp(),
onupdate=db.func.current_timestamp())
class User(UserMixin, Base):
__tablename__ = 'users'
username = db.Column(db.String(20), nullable=False, unique=True)
email = db.Column(db.String(50), nullable=False, unique=True)
password = db.Column(db.String(200), nullable=False)
name = db.Column(db.String(30), nullable=False)
badges = db.relationship('UserBadge', backref='ubadge',
lazy='dynamic')
class Badge(Base):
__tablename__ = 'badges'
name = db.Column(db.String(35), unique=True)
description = db.Column(db.String(300))
imgfile = db.Column(db.String(80))
badges = db.relationship('UserBadge', backref='badge',
lazy='dynamic')
def __repr__(self):
return '<Achievement: {} - {}>'.format(self.name, self.description)
class UserBadge(Base):
__tablename__ = 'userbadges'
user_id = db.Column(db.Integer(), db.ForeignKey('users.id'))
badge_id = db.Column(db.Integer(), db.ForeignKey('badges.id'))
def __repr__(self):
return '<Achievement: {} - {}>'.format(self.user_id, self.badge_id)
So i can return all the badges by a specific user, using:
ubadges = UserBadge.query.filter_by(user_id=user.id).all()
It returns:
[<Achievement: 1 - 1>]
But instead of 1 (user_id) and 1 (badge_id) i want to show the users.name and badges.name. How can i access those attributes?
In your UserBadge class, just use:
def __repr__(self):
return '<Achievement: {} - {}>'.format(self.ubadge.name, self.badge.name)
It has both properties because you set them up using backref in the other classes.
PS: You might need to change the User backref to user, and then use self.user.name int he function above
I've created to 3 simple model with flask-sqlalchemy with one to many relationship. Here is the code for models:
class UsersModel(BaseModel, UserMixin):
__tablename__ = 'user'
email = db.Column(db.String(255), unique=True)
password = db.Column(db.String(255))
confirmed_at = db.Column(db.DateTime())
info = db.relationship('UserInfoModel', backref="user", cascade="all, delete" , lazy='dynamic')
notes = db.relationship('NotesModel', backref="owner", cascade="all, delete" , lazy='dynamic')
class UserInfoModel(db.Model):
__tablename__ = 'user_info'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
first_name = db.Column(db.String(55))
last_name = db.Column(db.String(55))
age = db.Column(db.Integer)
profession = db.Column(db.String(255))
class NotesModel(BaseModel):
__tablename__ = 'notes'
title = db.Column(db.String(255), nullable=False)
desc = db.Column(db.Text)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
I can insert and retrive relational data without any problem but when I'm trying to delete a user it should also delete notes & info rather it gives error and don't let me delete. Here is the error that I see: http://prntscr.com/ek5cx1
But if I delete notes & info and then try to delete user it works. It's doing the reverse. I tried using 'delete-orphan' but didn't worked. I have read the documentation and read some blog about it but nothing helps. Am I wrong about declaring the relation? If so please help me to implement this or help me to find error within my code.
Appriciate your help, Thanks
Update: After adding delete-orphan I can delete data from session but not form phpmyadmin.
i think you want your relationships defined opposite how you have them, so like this:
class UsersModel(BaseModel, UserMixin):
__tablename__ = 'user'
email = db.Column(db.String(255), unique=True)
password = db.Column(db.String(255))
confirmed_at = db.Column(db.DateTime())
class UserInfoModel(db.Model):
__tablename__ = 'user_info'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
first_name = db.Column(db.String(55))
last_name = db.Column(db.String(55))
age = db.Column(db.Integer)
profession = db.Column(db.String(255))
user = db.relationship('User',uselist=False, cascade='all, delete-orphan',backref=db.backref('info', uselist=False))
class NotesModel(BaseModel):
__tablename__ = 'notes'
title = db.Column(db.String(255), nullable=False)
desc = db.Column(db.Text)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
user = db.relationship('User',uselist=False, cascade='all, delete-orphan',backref=db.backref('notes'.lazy='dynamic'))
use this way
cascade="all,delete"
I have a 3 tables: users, posts and comments. I'm trying to get username of comment author.
This is my models.py:
class User(db.Model, UserMixin):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.Integer, unique=True, index=True)
comments = db.relationship('Comment', backref='author', lazy='dynamic')
#i'm trying to:
comment_author = db.relationship('Comment', backref='comment_author_username', lazy='dynamic')
class Post(db.Model):
__tablename__ = 'posts'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(32), index=True)
comments = db.relationship('Comment', backref='post', lazy='dynamic')
body = db.Column(db.Text)
author_id = db.Column(db.Integer, db.ForeignKey('users.id'))
class Comment(db.Model):
__tablename__ = 'comments'
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.Text)
author_id = db.Column(db.Integer, db.ForeignKey('users.id'))
post_id = db.Column(db.Integer, db.ForeignKey('posts.id'))
#im trying to do:
#comment_author_username = db.Column(db.String(64), db.ForeignKey('users.username'))
but getting an error:
AmbiguousForeignKeysError: Could not determine join condition between parent/child tables on relationship User.comments - there are multiple foreign key paths linking the tables. Specify the 'foreign_keys' argument, providing a list of those columns which should be counted as containing a foreign key reference to the parent table.
If i'm using in template {{comment.author_id}} it works properly(shows comment author id), but {{comment.author_id.username}} shows nothing. How i can get comment author username?
You are making TWO relation from table User (comments, comment_author) to table Comment (That basically they are the same).
The author_id in table Post has a db.ForeignKey('users.id') but there is no refer to table Post in your User table.
Basically what you want is, There are some posts that they have their own author, and for each post there are some comments that they have also their authors. The relation between your Post and User is One-To-Many and the relation between your Post and Comment is also One-To-Many. The Relation between Comment and User is One-To-Many. By sqlalchemy, Your Tables will be like below:
class User(db.Model, UserMixin):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.Integer, unique=True, index=True)
posts = db.relationship('Post', backref='poster', lazy='dynamic')
comments = db.relationship('Comment', backref='commenter', lazy='dynamic')
class Post(db.Model):
__tablename__ = 'posts'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(32), index=True)
body = db.Column(db.Text)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
comments = db.relationship('Comment', backref='comment_on_post', lazy='dynamic')
class Comment(db.Model):
__tablename__ = 'comments'
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.Text)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
post_id = db.Column(db.Integer, db.ForeignKey('posts.id'))
and for adding data, you can do like:
s = db.session
user = User(username='Alex')
post = Post(title='First try', body='This is my first try!')
comment = Comment(body='This is a useful post!')
user.posts.append(post)
user.comments.append(comment)
post.comments.append(comment)
s.add(user)
s.add(post)
s.add(comment)
s.commit()
s.close()
and for retrieve data:
s = db.session
comments = models.Comment.query.all()
for c in comments:
print c.user_id
s.close()