upload image for a blog post with flask-sqlalchemy - python-3.x

I'm trying to create a form to enable users to add blog posts with images using flask and I saved the name of the picture in the database using SQLAlchemy like this "<FileStorage: 'IMG_20210415_114702.jpg' ('image/jpeg')>" as the name of the picture instead of this 'IMG_20210415_114702.jpg'. Please how do I correct this?
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(255))
content = db.Column(db.Text)
author = db.Column(db.String(255))
date_posted = db.Column(db.DateTime, default=datetime.utcnow)
slug = db.Column(db.String(255))
poster_id = db.Column(db.Integer, db.ForeignKey("users.id"))
post_pic = db.Column(db.String(200), nullable=True)
#app.route("/add-post", methods=["GET", "POST"])
##login_required
def add_post():
form = PostForm()
if form.validate_on_submit():
poster = current_user.id
post = Post(title=form.title.data, content=form.content.data, poster_id=poster, slug=form.slug.data, post_pic=form.post_pic.data)
form.title.data = ''
form.content.data = ''
#form.author.data = ''
form.slug.data = ''
name = ''
form.post_pic.data = request.files['post_pic']
name = form.post_pic.data.filename
filepath = os.path.join(app.config['UPLOAD_FOLDER'], name)
form.post_pic.data.save(filepath)
#postpic_filename = secure_filename(form.post_pic.data.filename)
#postpic_name = str(uuid.uuid1()) + "_" + postpic_filename
#saver = request.files['post_pic']
#form.post_pic.data = postpic_name
db.session.add(post)
db.session.commit()
#saver.save(os.path.join(app.config['UPLOAD_FOLDER'], postpic_name))
flash("post submitted successfully!")
return render_template("add_post.html", form=form)

Related

Function view throws an exception of UnboundLocalError at /group/share/42/ when a user tries share a post

What could possibly be the course of this exception UnboundLocalError at /group/share/42/ when i try sharing post of another user in a group.The exception trace seems to be on the the first line of the forloop ( new = new_post.video.add(img) ) Here is my view for users to share post
def share_post(request, pk):
original_post = Post.objects.get(pk=pk)
form = ShareForm(request.POST)
if form.is_valid():
new_post = Post(
shared_body = request.POST.get('description'),
description = original_post.description,
username = original_post.username,
date_posted = original_post.date_posted,
shared_on = timezone.now(),
shared_user = request.user)
new_post.save()
for img in original_post.video:
shared = new_post.video.add(img)
shared.save()
return redirect('group:main',original_post.group.pk)
Here is my model Post.
new_post will only be assigned a value if the form is valid, so you should indent the for loop:
def share_post(request, pk):
original_post = Post.objects.get(pk=pk)
form = ShareForm(request.POST)
if form.is_valid():
new_post = Post(
shared_body = request.POST.get('description'),
description = original_post.description,
username = original_post.username,
date_posted = original_post.date_posted,
shared_on = timezone.now(),
shared_user = request.user
)
new_post.save()
for img in original_post.video:
new_post.video.add(img)
return redirect('group:main',original_post.group.pk)

Django Query data optimization

Recently, I watched Django and discovered the teaching videos of select_related and prefetch_related.
So I installed debug_toolbar and took a look at my website
I searched the database too many times on one page.
I must convert it to json and send it back to the front end
Can I still optimize?
Is this the only way to go?
Below is my model .
models.py
def get_upload_path(instance, filename):
return f'register/{instance.owner.job_number}/{filename}'
def image_upload_path(instance, filename):
date = datetime.datetime.strptime(instance.date, '%Y-%m-%d %H:%M:%S')
return f'image/{date.year}/{date.month}/{date.day}/{filename}'
class UserProfile(models.Model):
name = models.CharField(max_length=64)
department = models.CharField(max_length=32)
job_number = models.CharField(max_length=32, unique=True)
card_number = models.CharField(max_length=32, unique=True)
location = models.CharField(max_length=32)
date = models.DateTimeField()
class UserRegister(models.Model):
owner = models.ForeignKey(UserProfile, on_delete=models.CASCADE)
date = models.DateTimeField()
feature = ArrayField(models.FloatField(null=True, blank=True), null=True, blank=True, size=512)
image = models.ImageField(upload_to=get_upload_path)
class Meta:
db_table = 'UserRegister'
class UserImage(models.Model):
owner = models.ForeignKey(UserProfile, on_delete=models.CASCADE)
heat = models.CharField(max_length=8, blank=True, null=True)
date = models.DateTimeField()
image = models.ImageField(upload_to=image_upload_path)
punch_in = models.CharField(max_length=8, blank=True, null=True)
class Meta:
db_table = 'UserImage'
views.py
def get_db_data(db, page, limit, model):
data = []
paginator = Paginator(db, limit)
try:
page_object = paginator.page(page)
all_page = paginator.num_pages
for db_data in page_object:
images = []
for image in db_data.userregister_set.all():
try:
url = f'/static/uploads{image.image.url}'
except ValueError:
url = '/static/imgs/assets/none.jpg'
images.append({'id': image.id, 'url': url})
_ = {
'id': db_data.id,
'name': db_data.name,
'department': db_data.department,
'job_number': db_data.job_number,
'card_number': db_data.card_number,
'location': db_data.location,
'date': db_data.date.strftime('%Y-%m-%d %H:%M:%S'),
'images': images,
}
data.append(_)
result = {
'data': data,
'all_page': all_page,
'page': int(page)
}
return result
except EmptyPage as error:
# 該頁數沒資料。
return {'msg': 'Error'}
class Test(Views):
def get(self, request):
name = request.GET.get('name')
department = request.GET.get('department')
job_number = request.GET.get('job_number')
card_number = request.GET.get('card_number')
location = request.GET.get('location')
start_date = request.GET.get('start_date')
end_date = request.GET.get('end_date')
order = request.GET.get('order', default='DESC')
page = request.GET.get('page', default=1)
limit = request.GET.get('limit', default=25)
user_profile = UserProfile.objects.all().select_related()
if name:
user_profile = user_profile.filter(name=name)
if department:
user_profile = user_profile.filter(department=department_table.department)
if job_number:
user_profile = user_profile.filter(job_number=job_number)
if card_number:
user_profile = user_profile.filter(card_number=card_number)
if location:
user_profile = user_profile.filter(location=locations_table.location)
if start_date and end_date:
user_profile = user_profile.filter(date__range=[start_date, end_date])
if order == 'DESC':
data = get_db_data(db=user_profile.order_by('-date'), page=page, limit=limit, model='api')
else:
data = get_db_data(db=user_profile.order_by('date'), page=page, limit=limit, model='api')
response = JsonResponse(data)
# return response # This is the response I want to return, but now I need to use `debug_toolbar`, so I return an empty page to facilitate my reading `debug_toolbar`.
return render(request, 'User/test1.html')
return JsonResponse(data) is what I want to return, but now I need to use debug_toolbar, so I return to an empty page to facilitate reading debug_toolbar.
Use select_related() or prefetch_related().
Put the associated parameters into.
can speed up the ORM.

how can i do a feed of posts with flask and Jinja2?

I'm using the flask-SQLAchemy to manage my DB and i have a table named post with the columns content, title, date, likes and profile_id, in the feed i use a simple jinja2 template and i send a variable with all posts in db, when i render the posts i make a for loop to render it but i can't take the name of the person who make the post (i have a variable with his id); So my question is how can i take the name of user who make the post in a jinja template?
Is the column 'profile_id' the primary key? If so, then your post might be actually showing the ID of the post (i.e. the number that corresponds the primary key or the main column to identify a certain post) instead of what you want.
class Post(db.Model):
profile_id = db.Column(db.Integer, primary_key=True)
likes = db.Column(db.Integer, nullable=False)
title = db.Column(db.String(250))
date_posted = db.Column(db.DateTime)
content = db.Column(db.Text)
Try analyzing the code above, I assume that's your code, what what you need to do is add another column which in this case the column known as the 'author', so do the following:
class Post(db.Model):
profile_id = db.Column(db.Integer, primary_key=True)
likes = db.Column(db.Integer, nullable=False)
title = db.Column(db.String(250))
author = db.Column(db.String(250))
date_posted = db.Column(db.DateTime)
content = db.Column(db.Text)
Done? Try opening a terminal and build your new model by doing the following:
python name-of-your-app.py
from name-of-your-app import *
db.create_all()
exit()
By doing this, you are making sure that your new model is actually being used instead of the old one. The next thing for you to do is add the 'author' column to your add function and edit function, then make sure to add a new input for the 'author' column in your jinja2 template and also replace the 'profile_id' with 'author'. If you are still confused, please refer to the code I've used while I was still learning flask. Also, consult to the provided link which I've also used to make the provided code.
https://www.youtube.com/watch?v=XHGpPCYmPvI
from flask import Flask, render_template, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
from flask_simplelogin import SimpleLogin, is_logged_in
from datetime import datetime
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///blog.db'
app.config['SECRET_KEY'] = 'something-secret'
app.config['SIMPLELOGIN_USERNAME'] = 'admin'
app.config['SIMPLELOGIN_PASSWORD'] = 'admin'
db = SQLAlchemy(app)
SimpleLogin(app)
class Blogpost(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(50))
subtitle = db.Column(db.String(50))
author = db.Column(db.String(20))
date_posted = db.Column(db.DateTime)
content = db.Column(db.Text)
#app.route('/')
def index():
posts = Blogpost.query.order_by(Blogpost.date_posted.desc()).all()
return render_template('index.html', posts=posts)
#app.route('/about')
def about():
return render_template('about.html')
#app.route('/add')
def add():
if is_logged_in():
return render_template('add.html')
else:
return redirect('/')
#app.route('/post/<int:post_id>')
def post(post_id):
post = Blogpost.query.filter_by(id=post_id).one()
return render_template('post.html', post=post)
#app.route('/edit/<int:post_id>')
def edit(post_id):
post = Blogpost.query.filter_by(id=post_id).one()
if is_logged_in():
return render_template('edit.html', post=post)
else:
return redirect('/')
#app.route('/addpost', methods=['POST'])
def addpost():
title = request.form['title']
subtitle = request.form['subtitle']
author = request.form['author']
content = request.form['content']
post = Blogpost(title=title, subtitle=subtitle, author=author, content=content, date_posted=datetime.now())
db.session.add(post)
db.session.commit()
return redirect(url_for('index'))
#app.route('/deletepost/<int:post_id>', methods=['POST'])
def deletepost(post_id):
post = Blogpost.query.get_or_404(post_id)
db.session.delete(post)
db.session.commit()
return redirect(url_for('index'))
#app.route('/editpost/<int:post_id>', methods=['POST'])
def editpost(post_id):
post = Blogpost.query.get_or_404(post_id)
post.title = request.form['title']
post.subtitle = request.form['subtitle']
post.author = request.form['author']
post.content = request.form['content']
db.session.commit()
return redirect('/')
if __name__ == "__main__":
app.run(debug=True)

Sqlalchemy get data from relationship

I have this models
class Post(Base):
__tablename__ = 'posts'
id = Column(Integer, primary_key=True)
content = Column(Text)
author = Column(Integer, ForeignKey('users.id'))
to_topic = Column(Integer, ForeignKey('topics.id'))
def __init__(self, content: str, author: int, to_topic: int) -> None:
self.content = content
self.author = author
self.to_topic = to_topic
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
username = Column(String(30))
email = Column(String(40))
password = Column(String(255))
registred_at = Column(DateTime, default=datetime.datetime.now)
last_logged_in = Column(DateTime)
last_login_attempt = Column(DateTime)
avatar = Column(Integer, ForeignKey('files.id'))
role = Column(String(20))
email_confirm = Column(Boolean, default=False)
children_forum = relationship('Forum')
children_topic = relationship('Topic')
children_post = relationship('Post')
And I am trying to get query that will contain Post.content, Post.author.username but how do i do this in sessions?
I tried
posts = db_session.query(Post, User).filter(
Post.to_topic == topic_id).with_entities(Post.content, Post.author.username)
but that doesnt work because author is just integer (id) so I expect that I need somehow get the author object and post object in one query, but I dont know how. In sql that would be easy just 2 queries but here I dont know how it´s done.
query_results = db_session.query(Post, User).\
join(User, Post.author == User.id).\
filter(
Post.to_topic == topic_id
).all()
I've never used with_entities, but I know this would give you a list of tuples where query_results[0] would be your Post instance, and query_results[1] would be your User instance.
EDIT: I believe you don't have to include the Post.author == User.id bit, but it's more legible if you're explicit with your joins.

How can a check on foreign key be enforced before insert a new value in Flask SQLAlchemy?

I am building the following DB scheme in Flask-SQLAlchemy:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
app = Flask(__name__)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
db = SQLAlchemy(app)
class User(db.Model):
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))
posts = db.relationship('Post', backref='author', lazy='dynamic')
def __repr__(self):
return '<User {}>'.format(self.username)
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.String(140))
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
def __repr__(self):
return '<Post {}>'.format(self.body)
db.drop_all()
db.create_all()
u = User(username='susan', email='susan#example.com')
db.session.add(u)
p = Post(body='this is my first post!', user_id=1)
db.session.add(p)
# Now I create a new post for a non-existing user and I'd expect an error...
p = Post(body='this is my second post!', user_id=2)
db.session.add(p)
db.session.commit()
As you can see I manually enter a new user and then enter two posts.
I would like that on the second post insert an error is thrown because user_id=2 is not existing in the database.
But instead the insert is successful and I also see the data into the DB:
sqlite> select * from post;
1|this is my post!|2018-07-09 16:13:16.947856|1
2|this is my post!|2018-07-09 16:13:16.948996|2
sqlite>
How can I enforce the constraint in order to make it throw an error?
Thanks for your help.
To resolve this situation, instead of assigning an id to a post object, it would be better and more ORM-like to associate a User with a Post. Also, by enforcing the NOT NULLability of the foreign key column, invalid numbers cannot be added.
For example:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
app = Flask(__name__)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
db = SQLAlchemy(app)
class User(db.Model):
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))
posts = db.relationship('Post', lazy='dynamic', back_populates='author')
def __repr__(self):
return '<User {}>'.format(self.username)
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.String(140))
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
author_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
author = db.relationship('User')
def __repr__(self):
return '<Post {}>'.format(self.body)
db.drop_all()
db.create_all()
# Susan will be both created and added to the session
u1 = User(username='susan', email='susan#example.com')
db.session.add(u1)
# John will be created, but not added
u2 = User(username='john', email='john#example.com')
# Create a post by Susan
p1 = Post(body='this is my post!', author=u1)
# Add susan's post to the session
db.session.add(p1)
# Create a post by john, since john does not yet exist as a user, he is created automatically
p2 = Post(body='this is my post!', author=u2)
# Add john's post to the session
db.session.add(p2)
# After the session has everything defined, commit it to the database
db.session.commit()

Resources