How can I get database's content data? - python

How can I get database's content data? I wrote in index.py,
def index():
user_data = UserData.query.filter_by(id=1)
return render_template('index.html', user_data=user_data)
in index.html
<div>
<p>{{ user_data.name }}</p>
</div>
in models.py
class UserData(db.Model):
id = db.Column(db.Integer)
name = db.Column(db.Text)
When I show index.html in browser,jinja2.exceptions.UndefinedError: 'name' is undefined error happens.I rewrote in index.py,
def index():
user_data = UserData.query.filter_by(id=1).all()
return render_template('index.html', user_data=user_data)
but same error happens.How can i get& show name value whose id=1 in index.html?How should I fix this code?

Basically, your query returns a list of objects, not a single object.
Eric Wilson's comment is correct. .all() will return a list while .one() (I prefer .first()) will return a single object with .name as a property.

Related

Relationship between models and filter_by errors in Python

I am currently learning Python. I have an app with two classes - customer and order. I would like to display a page that shows all orders associated with one customer (many to one relationship). I believe that I have defined the relationship correctly, but I'm getting some errors.
models.py
from app import db, ma
class customer(db.model):
__tablename__="customer"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(120))
orders = relationship("order", back_populates="customer")
def __repr__(self):
return '<customer %r' % self.id
class order(db.Model):
__tablename_="order"
id = db.Column(db.Integer, primary_key=True)
customer_id = db.Column(db.Integer, db.ForeignKey("customer.id"))
content = db.Column(db.String(120))
customer = relationship("customer")
def __repr__(self):
return '<order %r' % self.id`
routes.py
from app import app
from flask import Flask, render_template, request, redirect
from app.models import *
#app.route('/orders/<customer>')
def show_orders(name):
orders = order.query.join(customer, customer.id==order.customer_id).filter_by(customer.name==name).all()
return render_template('orders.html', name=name)
So for example, if I typed 'orders/bob_smith', this page should display all the orders made by bob_smith in the database.
But when I try to run this, I get the following error:
TypeError: filter_by() takes 1 positional argument but 2 were given.
Am I doing something wrong with the models? Or am I not assigning the right variables in filter_by? Would appreciate any help!
Edit: After some advice, I made the following change to routes.py
#app.route('/orders/<customer>')
def show_orders(name):
orders = order.query.join(customer, customer.id==order.customer_id).filter_by(customer.name==customer.name).all()
return render_template('orders.html', name=name)
I was able to see orders/bob_smith, but instead of showing me only bob_smith's orders, I saw all orders in the database.
orders.html
{{ name }} Orders
{% for order in orders %}
{{ order.customer }} # should all be bob_smith
{{ order.content }}
But instead, I'm seeing a list of all the orders. Am I not limiting the query sufficiently?
Just some small updates:
#app.route('/orders/<customer_name>')
def show_orders(customer_name):
orders = order.query.join(customer, customer.id==order.customer_id).filter(customer.name==customer_name).all()
return render_template('orders.html', orders=orders)
order.html will then need to have:
{% for order in orders %}
{{ order.customer }}
{{ order.content }}
{% endfor %}

Flask: Site reload prior check with database

I try to build a flask site with a very simple dictionary. A user can save words which are stored in a simple sqlite database. All that works fine, but if I try to implement a simple test on the vocabulary I can´t get consistent data from the database. The site is called, and after validate_on_submit it is relaoded so my choice from the database is a new one. Sorry for the bad description, may be it is gettinger clearer if I show you the code. I followed the CoreySchaefer Tutorials and the structur, so
# forms.py
# ...
class VocTestForm(FlaskForm):
german = StringField('Deutsch', validators=[DataRequired()])
submit = SubmitField('Check')
# models.py
# ...
class Dictionary(db.Model):
id = db.Column(db.Integer, primary_key=True)
engl = db.Column(db.String(120), unique=True, nullable=False)
german = db.Column(db.String(120), unique=True, nullable=False)
def __repr__(self):
return f"{self.engl} {self.german}"
# voc_test.html
{% extends "base_layout.html" %}
{% import "bootstrap/wtf.html" as wtf%}
{% block content %}
<h1>{{check_word.engl}}</h1>
{{ wtf.quick_form(form) }}
{% endblock %}
# routes.py
# ...
#app.route('/voc_test', methods=['GET', 'POST'])
def voc_test():
form = VocTestForm()
content = Dictionary.query.all()
check_word = random.choice(content)
if form.validate_on_submit():
if str(form.german.data) == str(check_word.german):
flash(f'Correct', 'success')
return redirect(url_for('voc_test'))
else:
flash(f'wrong', 'danger')
form.german = " "
return render_template('voc_test.html', form=form, check_word=check_word
The last function is not working. If I compare the String from the form with the String from the database the random.choice has already chosen a new word. How can I prevent this?
Thanks for the help
Steffen
If I've read what you're trying to do right, you could use the random library to generate a number. Then pull the item at that index from the dictionary.

Why is the return value of a model method in Django not showing up to the template?

I have this function inside my model that is not appearing when I try to run the server. I think I am accessing the method correctly but when I tried writing print("ENTER") inside the total_balance() function, nothing showed up which makes me think that it's not even entering the method at all. Oddly, the function works if I take out the search functionality.
model.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
def total_balance():
transaction_list = Transaction.objects.filter(user=User)
total_balance_amount = 0
for transaction in transaction_list:
if transaction.category=='Income':
total_balance_amount += transaction.amount
elif transaction.category=='Expense':
total_balance_amount -= transaction.amount
return total_balance_amount
views.py
def profile(request):
if request.method == 'GET':
query = request.GET.get('q')
if query and query!="":
results = Transaction.objects.filter(Q(tag__icontains=query))
else:
results = Transaction.objects.all()
transactions = {
'transactions' : results,
}
profile = {
'profile' : Profile.objects.all()
}
return render(request, 'users/profile.html', transactions, profile)
template.py
<h5 class="card-title">Total Balance</h5>
<p class="card-text">₱{{ profile.total_balance }}</p>
Can someone please help me identify the reason this is not working and how I might be able to fix it? Thank you.
There are at least four things wrong here.
Firstly, for some reason you are passing two separate dictionaries to render. That doesn't work; you need a single dictionary with multiple entries.
context = {
'transactions' : results,
'profile' : Profile.objects.all()
}
return render(request, 'users/profile.html', context )
Secondly, profile - despite the singular name - is a queryset of all profiles. You would need to iterate through it in your template:
{% for prof in profile %}
<p class="card-text">₱{{ prof.total_balance }}</p>
{% endfor %}
Ideally, you would use a more appropriate name for the context variable, ie profiles.
Next, your total_balance method itself has two issues. Firstly, any method in Python needs to take the self parameter. And secondly, you need to use that parameter to access the value of the user field, not the class User. So:
def total_balance(self):
transaction_list = Transaction.objects.filter(user=self.user)
although note that that second line could be more easily written:
transaction_list = self.user.transaction_set.all()

What should I use to get the title and post to show?

I'm working on an assignment using python, flask-env, and my sql and I am stuck on trying to get the blog title and blog post to show. I can only get the name of the person to show(which I need but the other stuff is important as well).
If I leave the template with just "indiv_post" for both fields, it shows "None", but if I put anything other that or anything with it, nothing but the author name shows.
in my main.py:
#app.route('/blog', methods=['POST', 'GET'])
def blog():
owner = User.query.filter_by(username=session['username']).first()
if request.method == 'POST':
blog_name = request.form('blog')
new_blog = Blog(blog_name, owner)
#post_title = request.form('blog_title')
#post_entry = request.form('your_thoughts')
db.session.add(new_blog)
db.session.commit()
post_id = request.args.get('id')
indiv_post = Blog.query.get('post_id')
user_id = request.args.get('owner_id')
posts = Blog.query.filter_by(owner_id=user_id)
blog = Blog.query.filter_by(owner_id=user_id)
return render_template('indiv_post.html', owner_id=owner, blog=blog, post=post_id, indiv_post=indiv_post)
in my template(indiv_post.html):
<h1>My Posts</h1>
<h2>{{posts}}</h2>
<p>{{indiv_post}}</p>
<p class="byline">Written by {{session['username']}}</p>
<hr>
I expect the output to be show:
Blog Title
Blog post
written by: user
But I'm getting:
None
None
written by: user

WTForms: setting default value from an SQLAlchemy field with relationship

There are many questions here on SO with titles that sound similar to what I'm about to describe but as far as I can tell from literally hours of research, this question is unique. So here goes!
I'm writing my first Flask app. I'm using SQLAlchemy for the model layer and WTForms to handle forms. The app is going to be a lightweight personal finance manager that I probably will not actually use for for serious biz. I have one table for a list of all transactions and another for all expense categories (groceries, clothing, etc). The transaction table has a column ("category") which references the Category table. In the view, I represent the list of categories with a element.
My issue is that when editing a transaction, I can't figure out how to tell WTForms to set the element to a specific pre-defined value. (Yes, I know that you can set a default value at the time that the form is defined, this is not what I am asking.)
The model looks like this (with irrelevant fields removed):
class Category(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80), nullable=False, unique=True)
# ...
class Trans(db.Model):
# ...
category_id = db.Column(db.Integer, db.ForeignKey('category.id'))
category = db.relationship('Category',
backref=db.backref('trans', lazy='dynamic'))
# ...
forms.py:
def category_choices():
return [('0', '')] + [(c.id, c.name) for c in Category.query.all()]
class TransactionForm(Form):
# ...
category = SelectField('Category', coerce=int, validators=[InputRequired()])
# ...
The route (POST not yet implemented):
#app.route('/transactions/edit/<trans_id>', methods=['GET', 'POST'])
def trans_edit(trans_id):
transaction = Trans.query.get(trans_id)
form = forms.TransactionForm(obj=transaction)
form.category.choices = forms.category_choices()
form.category.default = str(transaction.category.id)
#raise Exception # for debugging
return render_template('trans.html',
title='Edit Transaction',
form=form)
And finally, the template (Jinja2):
{{ form.category(class='trans-category input-medium') }}
As you can see in the route, I set form.category.default from the transaction.category.id, but this doesn't work. I think my issue is that I'm setting "default" after the form has been created. Which I'm rather forced to because the model comes from the database via SQLAlchemy. The root cause seems to be that form.category is an object (due to the relationship), which WTForms can't seem to handle easily. I can't have been the first one to come across this... Do I need to rework the model to be more WTForms compatible? What are my options?
Thanks!
I alluded to this in my comment. It sounds like you might benefit from using WTForm's SQLAlchemy extension. This will create a dropdown list for categories in your trans form.
My example's use case is slightly different. I am relating blog post's to topic. That is, many posts share one topic. I image in your case, many transactions share one category.
Form
from wtforms.ext.sqlalchemy.fields import QuerySelectField #import the ext.
def enabled_topics(): # query the topics (a.k.a categories)
return Topic.query.all()
class PostForm(Form): # create your form
title = StringField(u'title', validators=[DataRequired()])
body = StringField(u'Text', widget=TextArea())
topic = QuerySelectField(query_factory=enabled_topics, allow_blank=True)
models
The important part here is a) making sure you have the relationship correctly defined, and b.) adding topic to your init since you use it to create a new entry.
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(80))
body = db.Column(db.Text)
# one-to-many with Topic
topic = db.relationship('Topic', backref=db.backref('post', lazy='dynamic'))
def __init__(self, title, body, topic):
self.title = title
self.body = body
self.topic = topic
class Topic(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
def __init__(self, name):
self.name = name
view
Nothing special here. Just a regular view that generates a form and processes submitted results.
#app.route('/create', methods=['GET', 'POST'])
#login_required
def create_post():
form = PostForm()
if form.validate_on_submit():
post = Post(title=form.title.data, body=form.body.data,
topic=form.topic.data)
db.session.add(post)
db.session.commit()
Topic.update_counts()
flash('Your post has been published.')
return redirect(url_for('display_post', url=url))
posts = Post.query.all()
return render_template('create_post.html', form=form, posts=posts)
template
Nothing fancy here either. Just be sure to render the field in the template like you would a basic field. No fancy loop required since WTForms Sqlalchemy extensions does all that for you.
{% extends "base.html" %}
{% block title %}Create/Edit New Post{% endblock %}
{% block content %}
<H3>Create/Edit Post</H3>
<form action="" method=post>
{{form.hidden_tag()}}
<dl>
<dt>Title:
<dd>{{ form.title }}
<dt>Post:
<dd>{{ form.body(cols="35", rows="20") }}
<dt>Topic:
<dd>{{ form.topic }}
</dl>
<p>
<input type=submit value="Publish">
</form>
{% endblock %}
That's It! Now my post form has a topic dropdown list. To use your terminology, when you load a transaction the default category for that transaction will be highlighted in the dropdown list. The correct way to state this is to say that the category associated with the transaction is loaded via the relationship defined in the trans model.
Also note, there is also a multisellect SQLAlchemy extension in case one transaction has many 'default' categories.
Now, my issue is how to deal with many-to-many relationships.... I'm trying to pass a string of tags that are stored in a many-to-many table to a TextArea field. No SQLAlchemy extension for that!
I posted that question here

Categories