Post object with many recipients - python

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

Related

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

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

Flask database realtionship issue

I have the following database model:
#login_manager.user_loader
def load_user(id):
return User.query.get(int(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(30), nullable=False)
email = db.Column(db.String(100), nullable=False, unique=True)
adverts = db.relationship('Advert', backref='autor', lazy=True)
messages_sent = db.relationship('Message',foreign_keys='Message.sender_id', backref='author', lazy='dynamic')
messages_received = db.relationship('Message',foreign_keys='Message.recipient_id', backref='recipient', lazy='dynamic')
telephone = db.Column(db.String(15))
def __repr__(self):
return f"User('{self.username}', '{self.email}'"
class Advert(db.Model):
id = db.Column(db.Integer, primary_key=True)
date = db.Column(db.DateTime, nullable=False, default = datetime.utcnow)
title = db.Column(db.String(100), nullable=False)
content = db.Column(db.Text, nullable = False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
category = db.Column(db.String(50), nullable=False)
price = db.Column(db.Integer)
city = db.Column(db.String(), nullable=False)
messages = db.relationship('Message', backref='messages', lazy=True)
def __repr__(self):
return f"Advert('{self.title}', '{self.date}', '{self.category}')"
class Message(db.Model):
id = db.Column(db.Integer, primary_key=True)
sender_id = db.Column(db.Integer, db.ForeignKey('user.id'))
recipient_id = db.Column(db.Integer, db.ForeignKey('user.id'))
title = db.Column(db.String(100), nullable=False)
body = db.Column(db.String(300), nullable=False)
timestamp = db.Column(db.DateTime, default=datetime.utcnow)
ad_title = db.relationship('Advert', foreign_keys='Advert.title', backref='adtitle', lazy='dynamic')
def __repr__(self):
return f"Message('{self.body}')"
but the relationship between Message and Advert tables doesn't work. I want to do that the every Message have refer to Advert title. Anyone know the solution for this problem?

AttributeError: 'InstrumentedList' object has no attribute 'get'

In rendering an html template, the minutes_id is not being passed through correctly. It's throwing the error: "AttributeError: 'InstrumentedList' object has no attribute 'get'". I'm not sure how to fix this.
This is the line of code tripping me up.
minutes = club.minutes.get(minutes_id) #GET THE Minutes for specific DAY
routes.py:
#clubs.route("/view_minutes/<int:user_id>/<int:club_id>", methods=['GET', 'POST'])
#login_required
def view_minutes(user_id, club_id):
club = Club.query.get_or_404(club_id)
user = User.query.get_or_404(user_id)
minutes = club.minutes
#if click "export" -->
#return render_pdf(url_for('minutes_pdf', user_id=user_id, club_id=club_id))
return render_template('view_minutes.html', title='View', user=user, club=club, minutes=minutes)
#clubs.route("/minutes_pdf/<int:user_id>/<int:club_id>/<int:minutes_id>.pdf", methods=['GET', 'POST'])
#login_required
def minutes_pdf(user_id, club_id, minutes_id):
club = Club.query.get_or_404(club_id)
user = User.query.get_or_404(user_id)
minutes = club.minutes.get(minutes_id) #GET THE Minutes for specific DAY
return render_template('view_minutes_pdf.html', club=club, minutes=minutes)
models.py
user_club_assoc_table = db.Table('user_club_assoc_table',
db.Column('user_id', db.Integer, db.ForeignKey('user.id')),
db.Column('club_id', db.Integer, db.ForeignKey('club.id')))
roles = db.relationship('Role', secondary='user_roles',
backref=db.backref('users', lazy='dynamic'))
#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)
firstname = db.Column(db.String(15), nullable=False)
lastname = db.Column(db.String(15), nullable=False)
email = db.Column(db.String(60), unique=True, nullable=False)
password = db.Column(db.String(60), nullable=False)
role = db.Column(db.Integer(), nullable=False, default=ROLES['student'])
clubs = db.relationship('Club', secondary=user_club_assoc_table)
def __repr__(self):
return f'{self.firstname} {self.lastname}' #return f'User(firstname={self.firstname!r}, lastname={self.lastname!r})'
class Club(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), unique=True, nullable=False)
members = db.relationship('User', secondary=user_club_assoc_table)
minutes = db.relationship('Minutes', backref='club')
def __repr__(self):
return f'{self.name}'#Club(name={self.name!r})
class Minutes(db.Model):
id = db.Column(db.Integer, primary_key=True)
club_id = db.Column(db.Integer, db.ForeignKey('club.id'))
date = db.Column(db.Date, nullable=False) #0000-00-00
time = db.Column(db.Time) #00:00:00
location = db.Column(db.String(100), nullable=False)
attendance = db.relationship('Attendance', backref='minutes', lazy=True) #check code
purchase = db.Column(db.Text)
purchasemotion = db.Column(db.Text)
fundraiser = db.Column(db.Text)
fundmotion = db.Column(db.Text)
minute = db.Column(db.Text, nullable=False) #notes
def __repr__(self):
return f'{self.club_id} {self.date}'
class Attendance(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
student_name = db.Column(db.String(35), nullable=False)
present = db.Column(db.Boolean, default=False) #set correctly
minutes_id = db.Column(db.Integer, db.ForeignKey('minutes.id'))
In the url you are already capturing minute_id why don't you query directly
Minutes.query.get_or_404(minute_id)

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

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.

Categories