AttributeError: 'int' object has no attribute '_sa_instance_state' - python

I'm working on forum template using Flask. When I attempt creating a new thread in the browser using forms, SQLAlchemy throws an AttributeError. The problem showed up when I tried implementing a one-to-many relationship with Forum-to-Thread and a one-to-many relationship with Thread-to-User.
models.py
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(32), index=True, unique=True)
password = db.Column(db.String(32), index=True)
email = db.Column(db.String(120), index=True, unique=True)
role = db.Column(db.SmallInteger, default=ROLE_USER)
posts = db.relationship('Post', backref='author', lazy='dynamic')
class Forum(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(128))
description = db.Column(db.Text)
threads = db.relationship('Thread', backref='forum', lazy='dynamic')
class Thread(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(128))
author= db.Column(db.String(32))
timestamp = db.Column(db.DateTime)
forum_id = db.Column(db.Integer, db.ForeignKey('forum.id'))
posts = db.relationship('Post', backref='thread', lazy='dynamic')
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.Text)
timestamp = db.Column(db.DateTime)
thread_id = db.Column(db.Integer, db.ForeignKey('thread.id'))
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
All the new posts/threads and handled within views.py
views.py
#app.route('/forum/id=<id>/submit', methods=['GET','POST'])
#login_required
def new_thread(id):
form = ThreadForm()
forum = Forum.query.filter_by(id=id).first()
if form.validate_on_submit():
thread = Thread(title=form.title.data,
author=g.user.username,
timestamp=datetime.utcnow())
db.session.add(thread)
db.session.flush()
post = Post(body=form.body.data,
timestamp=datetime.utcnow(),
thread=thread.id,
author=g.user.id)
db.session.add(post)
db.session.commit()
flash('Post successful.')
return redirect(url_for('forum_index', id=id))
return render_template('forum/thread_submit.html', title=forum.title, form=form)

the problem is this:
post = Post(body=form.body.data,
timestamp=datetime.utcnow(),
thread=thread.id,
author=g.user.id)
you want to work with ORM objects, not primary key columns:
post = Post(body=form.body.data,
timestamp=datetime.utcnow(),
thread=thread,
author=g.user)
the error means that an integer is being interpreted as an ORM object.

Related

Python Error 'str' object has no attribute '_sa_instance_state' [duplicate]

I'm working on forum template using Flask. When I attempt creating a new thread in the browser using forms, SQLAlchemy throws an AttributeError. The problem showed up when I tried implementing a one-to-many relationship with Forum-to-Thread and a one-to-many relationship with Thread-to-User.
models.py
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(32), index=True, unique=True)
password = db.Column(db.String(32), index=True)
email = db.Column(db.String(120), index=True, unique=True)
role = db.Column(db.SmallInteger, default=ROLE_USER)
posts = db.relationship('Post', backref='author', lazy='dynamic')
class Forum(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(128))
description = db.Column(db.Text)
threads = db.relationship('Thread', backref='forum', lazy='dynamic')
class Thread(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(128))
author= db.Column(db.String(32))
timestamp = db.Column(db.DateTime)
forum_id = db.Column(db.Integer, db.ForeignKey('forum.id'))
posts = db.relationship('Post', backref='thread', lazy='dynamic')
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.Text)
timestamp = db.Column(db.DateTime)
thread_id = db.Column(db.Integer, db.ForeignKey('thread.id'))
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
All the new posts/threads and handled within views.py
views.py
#app.route('/forum/id=<id>/submit', methods=['GET','POST'])
#login_required
def new_thread(id):
form = ThreadForm()
forum = Forum.query.filter_by(id=id).first()
if form.validate_on_submit():
thread = Thread(title=form.title.data,
author=g.user.username,
timestamp=datetime.utcnow())
db.session.add(thread)
db.session.flush()
post = Post(body=form.body.data,
timestamp=datetime.utcnow(),
thread=thread.id,
author=g.user.id)
db.session.add(post)
db.session.commit()
flash('Post successful.')
return redirect(url_for('forum_index', id=id))
return render_template('forum/thread_submit.html', title=forum.title, form=form)
the problem is this:
post = Post(body=form.body.data,
timestamp=datetime.utcnow(),
thread=thread.id,
author=g.user.id)
you want to work with ORM objects, not primary key columns:
post = Post(body=form.body.data,
timestamp=datetime.utcnow(),
thread=thread,
author=g.user)
the error means that an integer is being interpreted as an ORM object.

How to get rid of this error sqlalchemy.exc.ArgumentError

Hello i would like to know how to get rid of this error. Here are my my tables
#login_menager.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)
email = db.Column(db.String(120), unique=True, nullable=False)
password = db.Column(db.String(60),nullable=False)
posts = db.relationship('Post', backref='author', lazy=True)
user_comments = db.relationship('Comment', backref='author_comment', lazy=True)
def __repr__(self):
return f"User('{self.username}', '{self.email}')"
class Comment(db.Model):
id = db.Column(db.Integer, primary_key = True)
body = db.Column(db.Text)
timestamp = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
#post_id = db.Column(db.Integer, db.ForeignKey('post.id'), nullable=False)
comment_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
def __repr__(self):
return f"Comment('{self.body}')"
When i add these lines:
if comment_form.validate_on_submit():
reply = Comment(body=comment_form.comment_on_form.data, author_comment=current_user)
db.session.add(reply)
db.session.commit()
replies = Comment.query.filter_by(author_comment=current_user).order_by(Comment.timestamp.desc()).all()
return render_template('home.html', posts=posts, title='Home', form=form, comment_form=comment_form, replies=replies)
I get this error:
sqlalchemy.exc.ArgumentError: Mapped instance expected
for relationship comparison to object.
Classes, queries and other SQL elements are not accepted in this context;
for comparison with a subquery, use Comment.author_comment.has(**criteria).
With trackback:
https://raw.githubusercontent.com/RealdoBeja98/endi-s_project/main/README.md
I would appreciate any help. Thank you

I am getting "AttributeError: 'int' object has no attribute 'filter'

I want this to be able to filter post by the CURRENT USER and delete it. Someone told me to use .WHERE instead of .FILTER but neither of them are working and I get an Attribute Error. How can I get this working?
By the way I am able to delete ALL user post with just db.session.query(Post).delete() but I want it so it only deletes posts for CURRENT_USER.ID (the user that is currently logged into my session)
I added my current models:
#login_required
def delete_all_post():
if current_user.is_authenticated:
db.session.query(Post).delete().filter(Post.user_id == current_user.id)
db.session.commit()
flash('All of your posts has been deleted!', 'success')
return redirect(url_for('main.home'))```
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
date_posted = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
content = db.Column(db.Text, nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
def __repr__(self):
return f"Post('{self.title}', {self.date_posted}')"
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(16), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
image_file = db.Column(db.String(20), nullable=False, default='default.jpg')
password = db.Column(db.String(60), nullable=False)
posts = db.relationship('Post', backref='author', lazy=True)
delete() return True/ False which is 0 or 1 which is int so you cannot do filter over it
try:
db.session.query(Post).filter(Post.user_id == current_user.id).delete()

How to display list of users who have liked a post in Flask app?

I am new to Flask and I have created a social media blog using Flask by following videos of Corey Schafer and Miguel Grinberg, I have incorporated additional functionalities such as Liking a post and Commenting on a post. All of the functionalities that I have incorporated are functioning properly but I cannot view the users who have liked a post.
here is my routes.py file that has the like and ViewLikers route
#app.route('/like/<int:post_id>/<action>')
#login_required
def like_action(post_id, action):
post = Post.query.filter_by(id=post_id).first_or_404()
if action == 'like':
current_user.like_post(post)
db.session.commit()
if action == 'unlike':
current_user.unlike_post(post)
db.session.commit()
return redirect(request.referrer)
#app.route('/like/<int:post_id>/viewLikes')
#login_required
def viewLikers(post_id):
post = Post.query.get_or_404(post_id)
return render_template('viewLikes.html', likers=post.get_likers())
Here is my models.py file
class PostLike(db.Model):
__tablename__ = 'PostLike'
id = db.Column(db.Integer, primary_key=True)
users_id = db.Column(db.Integer, db.ForeignKey('user.id'))
post_id = db.Column(db.Integer, db.ForeignKey('post.id'))
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
image_file = db.Column(db.String(20), nullable=False, default='default.jpg')
password = db.Column(db.String(60), nullable=False)
posts = db.relationship('Post', backref='author', lazy=True)
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')
liked = db.relationship(
'PostLike',
foreign_keys='PostLike.users_id',
backref='user', lazy='dynamic')
def like_post(self, post):
if not self.has_liked_post(post):
like = PostLike(users_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(
users_id=self.id,
post_id=post.id).delete()
def has_liked_post(self, post):
return PostLike.query.filter(
PostLike.users_id == self.id,
PostLike.post_id == post.id).count() > 0
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), unique=True, nullable=False)
date_posted = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
content = db.Column(db.Text, nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
likes = db.relationship('PostLike', backref='post', lazy='dynamic')
comments = db.relationship('Comment', backref='title', lazy='dynamic')
def get_likers(self):
return PostLike.query.filter_by(users_id =User.id, post_id=self.id)
But when I click on the viewLikes button in the template it gives me an error stating Function doesn't return any view
Simple error. In the last method you haven't returned the render_template. return render_template('')
Also call .first() on your post object

Post object with many recipients

I have three models : user, houses and a post.
I am trying to assign a post to one author and mutliple recipients, but I do not know how to do it.
I reckon it may have to do with relationships between tables...
These are my models
lettings = db.Table('lettings',
db.Column('tenant_id', db.Integer, db.ForeignKey('user.id')),
db.Column('property_id', db.Integer, db.ForeignKey('house.id'))
)
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))
lettings = db.relationship("Houses",secondary=lettings,lazy="dynamic", backref=db.backref("tenants",lazy="dynamic"))
posts_sent = db.relationship('Post',
foreign_keys='Post.sender_id',
backref='author', lazy='dynamic')
posts_received = db.relationship('Post',
foreign_keys='Post.recipient_id',
backref='recipient', lazy='dynamic')
last_post_read_time = db.Column(db.DateTime)
def haslived(self,house):
if not self.isliving(house):
self.lettings.append(house)
def unlived(self, house):
if self.isliving(house):
self.lettings.remove(house)
def isliving(self, house):
return self.lettings.filter_by(id=house.id).first()
class Post(db.Model):
__tablename__ = 'post'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(32))
body = db.Column(db.String(140))
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
sender_id = db.Column(db.Integer, db.ForeignKey('user.id'))
recipient_id = db.Column(db.Integer, db.ForeignKey('user.id'))
house_id = db.Column(db.Integer, db.ForeignKey('house.id'))
__searchable__= ['body']
def __repr__(self):
return '<Post {}>'.format(self.body)
class Houses(db.Model):
__tablename__ = 'house'
id = db.Column(db.Integer, primary_key=True,index=True)
address = db.Column(db.String(120))
postcode = db.Column(db.String(120),index=True)
licence_holder = db.Column(db.String(140),index=True)
__searchable__=['address']
posts = db.relationship('Post', backref='letting', lazy='dynamic')
def __repr__(self):
return '<House {}>'.format(self.address)
And this is my routes code snippet:
#bp.route('/house/<address>/ask',methods=['GET', 'POST'])
#login_required
def ask(address):
house = Houses.query.filter_by(address=address).first_or_404()
form = PostForm()
if form.submit.data and form.validate_on_submit():
post = Post(body=form.body.data,title=form.title.data,author=current_user,letting=house,recipient={I do not know what to write here})
db.session.add(post)
db.session.commit()
flash('Your post is now live!')
return redirect(url_for('main.house', address=house.address))
return render_template('ask.html', title='Ask a question about {}'.format(house.address),form=form,house=house)
My objective is to create a post object which will have only one sender but multiple recipients.
Thank you for your help.
Yes, as somebody above correctly pointed out, this can be done using many-to-many relationship from SQLalchemy.
I got the answer myself, that's how i modified the code:
My models:
lettings = db.Table('lettings',
db.Column('tenant_id', db.Integer, db.ForeignKey('user.id')),
db.Column('property_id', db.Integer, db.ForeignKey('house.id'))
)
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))
reviews = db.relationship('Review', backref='author', lazy='dynamic')
checklists = db.relationship('Checklist', backref='author', lazy='dynamic')
about_me = db.Column(db.String(140))
lettings = db.relationship("Houses",secondary=lettings,lazy="dynamic", backref=db.backref("tenants",lazy="dynamic"))
posts_sent = db.relationship('Post',
foreign_keys='Post.sender_id',
backref='author', lazy='dynamic')
last_post_read_time = db.Column(db.DateTime)
def new_posts(self):
last_read_time = self.last_post_read_time or datetime(1900, 1, 1)
return Post.query.filter_by(recipients=self).filter(
Post.timestamp > last_read_time).count()
def haslived(self,house):
if not self.isliving(house):
self.lettings.append(house)
def unlived(self, house):
if self.isliving(house):
self.lettings.remove(house)
def isliving(self, house):
return self.lettings.filter_by(id=house.id).first()
def __repr__(self):
return '<User {}>'.format(self.username)
recipients = db.Table('recipients',
db.Column('recipient_id', db.Integer, db.ForeignKey('user.id')),
db.Column('post_id', db.Integer, db.ForeignKey('post.id'))
)
class Post(db.Model):
__tablename__ = 'post'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(32))
body = db.Column(db.String(140))
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
sender_id = db.Column(db.Integer, db.ForeignKey('user.id'))
recipients = db.relationship("User",secondary=recipients,lazy="dynamic", backref=db.backref("posts_received",lazy="dynamic"))
house_id = db.Column(db.Integer, db.ForeignKey('house.id'))
__searchable__= ['body']
def __repr__(self):
return '<Post {}>'.format(self.body)
def add_recipient(self, recipient):
self.recipients.append(recipient)
class Houses(db.Model):
__tablename__ = 'house'
id = db.Column(db.Integer, primary_key=True,index=True)
address = db.Column(db.String(120))
postcode = db.Column(db.String(120),index=True)
licence_holder = db.Column(db.String(140),index=True)
__searchable__=['address']
posts = db.relationship('Post', backref='letting', lazy='dynamic')
reviews = db.relationship('Review', backref='house', lazy='dynamic')
checklists = db.relationship('Checklist', backref='house', lazy='dynamic')
latitude=db.Column(db.Float(precision=32,decimal_return_scale=None),index=True)
longitude=db.Column(db.Float(precision=30,decimal_return_scale=None),index=True)
def __repr__(self):
return '<House {}>'.format(self.address)
And you add all the recipients to a post once its uploaded by a different user using list comprehension:
Post(body=form.body.data,title=form.title.data,author=current_user,letting=house)
[post.add_recipient(recipient) for recipient in house.tenants.all()]
db.session.add(post)
db.session.commit()

Categories