HTML shows Flask-SQLAlchemy syntax? - python

When trying to display replies of users on my page it displays them with SQL syntax, like so: ('reply',) I've tried str() when returning from my route.
html:
<ul style="float:left;" class="list-group list-group-flush" id="anons">
{% for reply in replies %}
<li class="list-group-item" align="left">
{{ reply }}
</li>
{% endfor %}
model:
class Reply(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(30))
data = db.Column(db.String(10000))
date = db.Column(db.DateTime(timezone=True), default=func.now())
route:
#views.route('/', methods=["GET", "POST"])
def home():
if request.method == "POST":
name = request.form.get("name")
data = request.form.get("reply")
if len(data) >= 1:
new_reply = Reply(name=name, data=data)
db.session.add(new_reply)
db.session.commit()
replies = db.session.query(Reply.data)
return render_template('home.html', replies=replies)
return render_template('home.html')

Try modifying your replies definition to this:
replies = Reply.query.all()
This essentially creates a list of all the 'Reply' objects, which you then pass to the template. If you were wanting to only show the .data attribute of each 'Reply', you would access that with {{ reply.data }} in the for loop.

Related

Why do I get a null row in my sqlite database in flask?

Every time I upload a post, it will post a row with null values inside all of the columns except the date and id, and then add a second row with the correct information put in each column. The SQLite data looks like this. Using SQL-alchemy, flask-login, wtf-forms, flask-bootstrap,werkzeug.
id title content posting_user date_posted
1 null null jack 2021-11-01
2 adad test jack 2021-11-01
Post Model
class Posts(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(50))
content = db.Column(db.Text)
posting_user = db.Column(db.String(255))
date_posted = db.Column(db.String(50))
class Postform(FlaskForm):
title = StringField('Title', validators=[InputRequired(), Length(min = 3, max = 50)])
content = StringField('Content', validators=[InputRequired()], widget=TextArea())
HTML block
<div class="container">
<div class="row">
<div class="col-md-4 offset-md-4">
<div class="login-form bg-light mt-4 p-4">
<form action="" method="POST" class="row g-3">
<h4>Add a Post</h4>
{{ form.hidden_tag()}}
{{ form.title(placeholder = "Title") }}
{{ form.content(cols="30", rows="15", placeholder = "Write your review here") }}
<div class="col-12">
<button type="submit" name = "post_submit" class="btn btn-dark float-end">Post</button>
</div>
</form>
</div>
</div>
</div>
</div>
app.py
#app.route('/dashboard/post', methods = ["POST", "GET"])
#login_required
def post():
form = Postform()
posting = Posts(title = form.title.data, content = form.content.data, posting_user = current_user.username, date_posted = datetime.datetime.now().date())
if posting.title != None and posting.content != None:
db.session.add(posting)
db.session.commit()
flash('Your post has been added', 'posted')
return render_template("post.html", form = form, loggedin = current_user.is_active)
It is possible that you send a GET request before your POST request in which the form is still empty.
You can differentiate between the request methods to control the behavior.
#app.route('/dashboard/post', methods = ["POST", "GET"])
#login_required
def post():
form = Postform()
# Validate whether the request method is post and the entries are correct!
if form.validate_on_submit():
posting = Posts(title = form.title.data, content = form.content.data, posting_user = current_user.username, date_posted = datetime.datetime.now().date())
db.session.add(posting)
db.session.commit()
flash('Your post has been added', 'posted')
return render_template("post.html", form = form, loggedin = current_user.is_active)

How to organise placed orders

So when I place an order it creates a new order for every item in the cart. Lets say that I have 3 phones in my cart, then It would go to the database as as 3 orders. How would I go about it if I want to make it so these 3 phones would display as 1 order in "My orders"?
Here's my models:
class Order(TimestampMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), unique=False )
phone_id = db.Column(db.Integer, db.ForeignKey('phone.id'),
unique=False, nullable=True)
accessory_id = db.Column(db.Integer, db.ForeignKey('accessory.id'),
unique=False, nullable=True)
That's code for displaying:
#phones.route("/myorders", methods=['GET', 'POST'])
#login_required
def myorder():
orders = Order.query.filter_by(user_id=current_user.get_id())
return render_template('phones/myorder.html', orders=orders)
and HTML:
{% extends "base.html" %}
{% block app_content %}
<div class="row">
<div class="container">
<div class="col-md-5">
<div class="content-section">
<table class="table table-striped">
{% for order in orders %}
<tr><th>{{order.id}} {{order.created_at}}</th>
<tr>
{% if order.phone %}
<td>{{order.phone.brand.name}} {{order.phone.model}} </td>
{% endif %}
{% if order.accessory %}
<td>{{order.accessory.brand}} {{order.accessory.compability.platform}} {{order.accessory.type.type_of_accessory}} </td>
{% endif %}
{% endfor %}
</table>
</div>
</div>
</div>
</div>
{% endblock %}
How it looks on the page:
Code for creating an order:
#phones.route("/order/", methods=['GET', 'POST'])
#login_required
def order():
list_of_phones = session['cart']
list_of_accessories = session['cart2']
for pid in list_of_phones:
phone = Phone.query.get(pid)
phone.stock = phone.stock - 1
order = Order(
user_id = current_user.get_id(),
phone_id = phone.id)
db.session.add(order)
db.session.commit()
for aid in list_of_accessories:
accessory = Accessory.query.get(aid)
accessory.stock = accessory.stock - 1
order = Order(
user_id = current_user.get_id(),
accessory_id = accessory.id)
db.session.add(order)
db.session.commit()
session.pop('cart')
session.pop('cart2')
flash('Order was added successfully', 'success')
return redirect(url_for('phones.shopping_cart'))
Difference after changes:
I modified code for displaying all orders:
#phones.route("/myorders", methods=['GET', 'POST'])
#login_required
def myorder():
# orders = Order.query.filter_by(user_id=current_user.get_id())
orders = db.session.query(Order).filter_by(user_id = current_user.id).group_by(Order.order_id).all()
print(orders)
return render_template('phones/myorder.html', orders=orders)
And it kind of works, because it only display one order_id, but on the other side it only displays one item, see below:
I tested this code, it works and groups order by randomly generated order_id:
#phones.route("/order/", methods=['GET', 'POST'])
#login_required
def order():
list_of_phones = session['cart']
random_string = random_generator()
for pid in list_of_phones:
phone = Phone.query.get(pid)
phone.stock = phone.stock - 1
order = Order(
user_id = current_user.get_id(),
phone_id = phone.id)
order_id = random_string
db.session.add(order)
db.session.commit()
session.pop('cart')
flash('Order was added successfully', 'success')
return redirect(url_for('phones.shopping_cart'))
def random_generator():
size = 16
chars = string.ascii_letters + string.digits
return ''.join(random.choice(chars) for _ in range(size))
in your app route:
#phones.route("/myorders", methods=['GET', 'POST'])
#login_required
def myorder():
orders = db.session.query(Order).filter_by(
user_id = current_user.id).all()
return render_template('phones/myorder.html',
orders=orders)
In your template, you can use something like the following:
{% for order_id, order in orders | groupby('order_id') %}
{{ order_id }}
{% for i in order %}
<ul>
{{ i.id }}
{{ i.phone_name }}
</ul>
{% endfor %}
<br>
{% endfor %}
with some test data I added to database, I get the following
abc
1 phone1
2 phone2
3 phone3
xyz
4 phone4
5 phone5
6 phone6

search function (query in Flask, SQLAlchemy)

I'm new to programming and Flask and I am stuck on this problem.
I am trying to implement a search function in a web application that will take data from a form and compare it to a value in the database and list results.
This is what I have so far:
views.py
#app.route('/search', methods=['GET', 'POST'])
def search():
searchForm = searchForm()
courses = models.Course.query.order_by(models.Course.name).all()
if searchForm.validate_on_submit():
for i in courses:
if searchForm.courseName.data == i.name:
searchResult = models.Course.filter(Course.name.like('%searchForm.courseName.data%'))
return render_template('courselist.html', courses = courses, searchResult = searchResult)
form.py
class searchForm(Form):
courseName = StringField('Search course', validators=[DataRequired(), Length(max=60)])
database models.py
class Course(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(40), unique=True)
courseCode = db.Column(db.String(10), unique=True)
duration = db.Column(db.Integer)
maxStudents = db.Column(db.Integer)
startDate = db.Column(db.DateTime)
prerequisites = db.Column(db.String(500))
trainerID = db.Column(db.Integer, db.ForeignKey('trainer.id'))
venueID = db.Column(db.Integer, db.ForeignKey('venue.id'))
sessions = db.relationship('Session', backref='course', lazy='dynamic')
bookings = db.relationship('Booking', backref='course', lazy='dynamic')
html file
{% extends "index.html" %}
{% block content %}
<h3>Courses:</h3>
<ul>
{% for course in courses %}
<li>
<h4>{{course.name}}
<a class="btn btn-success" href="/editcourse?id={{course.id}}">Book</a>
<a class="btn btn-info" href="/editcourse?id={{course.id}}">Edit</a>
<a class="btn btn-danger" href="/deletecourse?id={{course.id}}">Delete</a></h4>
</li>
{% endfor %}
</ul>
{% endblock %}
I think the general logic is right but I need some help adjusting it.
Your logic in views.py seems a bit off. You're retrieving all Course objects from the database and looping through them. Then you check if the course name exactly matches the search input - and if so, try to find matching courses. I think it would be better constructed like this:
#app.route('/search', methods=['GET', 'POST'])
def search():
searchForm = searchForm()
courses = models.Course.query
if searchForm.validate_on_submit():
courses = courses.filter(models.Course.name.like('%' + searchForm.courseName.data + '%'))
courses = courses.order_by(models.Course.name).all()
return render_template('courselist.html', courses = courses)
This is this is the simplest answer :
#app.route("/search", methods=['GET'])
def search():
query = request.args.get("query") # here query will be the search inputs name
allVideos = Videos.query.filter(Videos.title.like("%"+query+"%")).all()
return render_template("search.html", query=query, allVideos=allVideos)

reading from joined query in flask-sqlalchemy

After successfully joining two db tables, I'm trying to read the data from the 2nd table by addressing the first. I'm addressing opinion.topic_name from my jinja2 template, but nothing is returned.
How can I access the Topic.topic_name value from the joined query?
view
#main.route('/', methods=['GET', 'POST'])
def index():
form = IndexForm()
opinions = []
if form.validate_on_submit():
opinions = Opinion.query
.filter_by(party_id=form.party.data)
.filter_by(topic_id=form.topic.data)
.join('topic')
.all()
return render_template('index.html', form=form, opinions=opinions)
model
class Opinion(db.Model):
id = db.Column(db.Integer, primary_key=True)
text = db.Column(db.String(2000))
topic_id = db.Column(db.Integer, db.ForeignKey('topic.id'))
party_id = db.Column(db.Integer, db.ForeignKey('party.id'))
class Topic(db.Model):
id = db.Column(db.Integer, primary_key=True)
topic_name = db.Column(db.String(64))
opinions = db.relationship(Opinion, backref='topic')
template (jinja2)
<div>
{{ wtf.quick_form(form) }}
</div>
<div>
{% for opinion in opinions %}
<div class='jumbotron'>
<h1>{{ opinion.topic_name }}</h1>
<p>{{ opinion.text }}</p>
</div>
{% endfor %}
</div>
This query:
opinions = Opinion.query
.filter_by(party_id=form.party.data)
.filter_by(topic_id=form.topic.data)
.join(Topic)
.all()
will return a list of Opinion models and since in your relationship in Topic model, you defined it as:
opinions = db.relationship(Opinion, backref='topic')
Then, in your jinja2 template, to access Topic.topic_name, you should do:
<h1>{{ opinion.topic.topic_name }}</h1>

SelectMultipleField default value is not being selected on HTML

c.ingredientsI am creating a Flask sample app for learning purposes and I have a Form called CocktailForm that contains a SelectMultipleField. This form should be used for creating new cocktails objects as well as for updating them:
class CocktailForm(Form):
name = StringField('What is the coktail\'s name?', validators=[Required()])
ingredients = SelectMultipleField('Ingredients',
coerce=int,
)
submit = SubmitField('Submit')
For editing I want to load the same form with the cocktail data. It works for name, and for loading all choices but I also want to select those choices that are ingredients of that cocktail:
#main.route('/edit/<int:id>', methods=['GET', 'POST'])
def edit(id):
try:
c = Cocktail.query.filter(Cocktail.id == id).one()
form = CocktailForm()
form.ingredients.choices = [(i.id, i.name) for i in Ingredient.query.all()]
if form.validate_on_submit():
c.name = form.name.data
cocktail_ingredients = Ingredient.query.filter(Ingredient.id.in_(form.ingredients.data)).all()
c.ingredients.extend(cocktail_ingredients)
db.session.commit()
else:
form.name.data = c.name
form.ingredients.default = [(i.id, i.name) for i in c.ingredients]
return render_template('new.html', form=form)
except Exception, e:
print e
return redirect(url_for('.index'))
I get unexpected results since on the HTML those choices are not displayed but when I submit the form it seems like those choices are always selected even if you select a new ones.
The template is quite simple:
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Cocktails{% endblock %}
{% block page_content %}
<div class="page-header">
<h1>New Cocktail</h1>
{{ wtf.quick_form(form) }}
</div>
{% endblock %}
This is what you can see in in the browser. I am editing Gintonic cocktail which is composed of gin and tonic. However they are not displayed as selected:
Browser
Thanks in advance, any tip will be appreciated
The line form.ingredients.default = [(i.id, i.name) for i in Ingredient.query.all()] does not set the selected values. You want to change it to be form.ingredients.data = [i.id for i in c.ingredients].
You should have a redirect/render_template in the validate block, but that is optional.

Categories

Resources