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()
Related
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 a example about one-to-many relationship in Flask-SQLAlchemy:
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80))
email = db.Column(db.String(120), unique=True)
posts = db.relationship('Post', backref='user')
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
content = db.Column(db.Text)
user_id = db.Column(db.Integer, db.ForeignKey('user.id')
According to Declaring Models, I can get list of posts of User U by U.posts. But in constrast, how can I get author's name of Post P as an attribute like P.author_name, instead of P.user.name?
try P.user, according to what you defined in your backref
I can modify the Post class by:
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
content = db.Column(db.Text)
user_id = db.Column(db.Integer, db.ForeignKey('user.id')
#cached_property
def author_name(self):
return self.user.name
then, it will see P.author_name as P.user.name.
I've got three tables: User, Role, Department
I want to query all the user with selected following filters:
Department.name , Role.name and User.degree
I can't find a solution to this problem, any suggestion would be great!
Here's my models simplified:
class User(UserMixin, db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
password_hash = db.Column(db.String(128))
degree = db.Column(db.String(20),default=None)
department_id = db.Column(db.Integer, db.ForeignKey('departments.id'))
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
class Department(db.Model):
__tablename__ = "departments"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(sqlalchemy.types.NVARCHAR(100), unique=True)
user = db.relationship('User', backref='department',
lazy='dynamic')
class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(sqlalchemy.types.NVARCHAR(100), unique=True)
users = db.relationship('User', backref='role',
lazy='dynamic')
session.query(User).filter(
User.department.has(name='some_name)).filter(
User.role.has(name='some_role')).filter(
User.degree == 'some_degree')
It's a simple query with joins. You can modify "department" with your department filter and "role" with the same. You should modify the select part (session.query(User.id)) with the fields you want.
users = (session.query(User.id).join(Department, Department.user == User.id).join(Role, Role.user == User.id).filter(Department.name=="department").filter(Role.name=="role").group_by(User.id))
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 need to have a post associated to two users. The author and the moderator. I am trying without success this code
class User(UserMixin, db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
...
post = db.relationship('Post', foreign_keys=['posts.id'], backref='post_user', lazy='dynamic')
post_blame = db.relationship('Post', foreign_keys=['posts.moderated_by'], backref='post_blame', lazy='dynamic')
class Post(db.Model):
__tablename__ = 'posts'
id = db.Column(db.Integer, primary_key=True)
...
author_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
moderated_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
Error:
ArgumentError: Column-based expression object expected for argument 'foreign_keys'; got: 'posts.id', type <class 'str'>
One of the issues here is that you are currently trying to use table columns in the relationship foreign_keys, rather than class attributes.
That is, instead of using posts.id, you should be using Post.id. (In fact, to refer to a table column, you would need to use posts.c.id).
So, it is possible that your original code will work if you correct it to:
class User(UserMixin, db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
...
post = db.relationship('Post', foreign_keys='Post.id', backref='post_user', lazy='dynamic')
post_blame = db.relationship('Post', foreign_keys='Post.moderated_by', backref='post_blame', lazy='dynamic')
If it does not, then there several other options. First, you could establish these relationships in the Post class, where it is less ambiguous for sqlalchemy to find the foreign key relationship. Something like
class Post(db.Model):
__tablename__ = 'posts'
id = db.Column(db.Integer, primary_key=True)
...
author_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
moderated_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
post_user = db.relationship(User, foreign_keys=author_id, backref='post', lazy='dynamic')
post_blame = db.relationship(User, foreign_keys=moderated_by, backref='post_blame', lazy='dynamic')
Note in this version, we don't need to pass the foreign_keys value as a string, we can just refer directly to the column in scope.
Alternatively, if you wish to establish these relationships within User, you may you need to give sqlalchemy more information, by using primaryjoin ... perhaps something like:
class User(UserMixin, db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
...
post = db.relationship('Post', primaryjoin='User.id == Post.id', backref='post_user', lazy='dynamic')
post_blame = db.relationship('Post', foreign_keys='User.id == Post.moderated_by', backref='post_blame', lazy='dynamic')