This question already has answers here:
Passing HTML to template using Flask/Jinja2
(7 answers)
Closed 9 months ago.
I'm building a web page to show articles.
In my database, I have to attributes, one of which is used to put Markdown code and the other to save HTML code converted from the Markdown code. I want to get the HTML and add it to my base HTML.
I use Flask framework and SQLAlchemy and the data is saved in sqlite database.
My model:
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String)
body = db.Column(db.String)
timestamp = db.Column(db.String)
tag = db.Column(db.String)
info = db.Column(db.String)
body_markdown = db.Column(db.String)
and my view function:
def post(post_id):
post = db.session.query(Post).filter(Post.id == post_id).first()
return render_template('post.html',
post = post,)
Firstly, I tried:
<div class="post-body">
{{post.body}}
</div>
It shows the whole string with HTML tag.
post.body.decode("utf-8") and post.body.decode("ascii") didn't work, either.
When I debug the program, I found that the database returns u'string'.
How can I get pure HTML code and apply it in my base HTML file?
By default Jinja2 (the template engine for Flask) assumes input inside of {{ }} is unsafe and will not allow js or html inside them. You can solve this by using the safe filter inside your template. This will tell Jinja2 that the content is safe to render as is, and not to escape the html/javascript.
{{ post.body | safe }}
Related
This question already has answers here:
Passing HTML to template using Flask/Jinja2
(7 answers)
Closed 4 years ago.
I'm new to flask, but want to integrate some HTML capabilities from other libraries together with the templating power of flask. In playing around with variables, I'm not able to understand the different behavior for rendering HTML. Sometimes I can get the HTML rendered if I pass it directly to a view as a variable:
body = a bunch of HTML
#app.route('/')
def index():
return '''
{}
'''.format(body)
However, if I try to pass this to a template using the {{ body }} variable, I don't get the HTML rendered. Instead, I will see the raw HTML on the page.
#app.route('/')
def index():
return render_template("index.html", b = body)
In the "index.html" file, I call this with the {{ b }} template syntax. Here, I get the raw HTML though. I feel there's just one little piece I'm missing. Here's what I see for each approach respectively.
Jinja will escape all strings that it parses to avoid accidentally injecting HTML into your templates. To mark variables as "safe to use as-is", you need to use the safe template filter.
{{ b|safe }}
>>> b = 'Example'
>>> render_template_string('{{ b }}', b=b)
'<a href="#">Example</a>'
>>> render_template_string('{{ b|safe }}')
'Example'
I have jquery send out a request to the api endpoint. Code below:
$.get("http://localhost:5000/api/update/"+ elm2, function(data){});
The endpoint is defined like this:
#app.route('/api/update/<id>', methods=['GET'])
def check_new_entries(id):
result = Trades.query.filter_by(id=id).first_or_404()
new_entries = Trades.query.filter(Trades.time_recorded > result.time_recorded).all()
The table being queried from is:
class Trades(db.Model):
id = db.Column(db.String,default=lambda: str(uuid4().hex), primary_key=True)
amount = db.Column(db.Integer, unique=False)
time_recorded = db.Column(db.DateTime, unique=False)
The problem:
Jquery is successfully sending the correct request, with the variable being of type string but when the first query is executed, it fails to return anything.
I have a similar endpoint in another application and it works fine.. why is this one an exception? What could be missing? I am sure that the record that I am querying is in the database so it should return it.
#app.route('/api/update/<id>', methods=['GET',])
def check_new_entries(id):
result = Trades.query.filter_by(id=id).first()
if result:
new_entries = Trades.query.filter(Trades.time_recorded >= result.time_recorded).all()
The problem is that some unexpected space was before the rest of the string. The error originated from jquery and jinja where jquery was getting the html contents of an element and sending them as the variable. The jinja syntax was something like:
<p> {{ variable }}</p>
When jquery fetches the html contents, it takes the space btwn the paragraph opening tag and the opening curly braces as well. The variable transmitted had a leading space which caused the error.
I'm using flask and sqlalchemy and I'm having issues regarding file uploading.
I'm uploading the file with a form and storing the URL to the DB, so I can display it inside on the page.
Here's the form's code:
boxart = request.files['boxart']
if boxart:
filename = secure_filename(boxart.filename)
boxart_path = os.path.join(app.config['UPLOAD_FOLDER']+"boxart/", filename)
boxart.save(boxart_path)
game_son.boxart = boxart_path
db.session.add(game_son)
db.session.commit()
So I go to my template and I can print the content of game_son.boxart and there is the full path to the file. If I click on the link, I can access, but the image will not display inside tag...
I tried getting it on the template by using:
<img src="{{ game_son.boxart_url() }}" />
Inside the model, I defined a boxart_url method, in case I need to parse the string before sending it
class Game_son(db.Model):
__searchable__ = ['version_name']
son_id = db.Column(db.Integer, primary_key=True)
version_name = db.Column(db.String(128), index=True, unique=False)
son_timestamp = db.Column(db.DateTime)
dad = db.Column(db.Integer, db.ForeignKey('game_dad.id'))
console = db.Column(db.Integer, db.ForeignKey('platform.id'))
boxart = db.Column(db.String(256))
thumbnail = db.Column(db.String(256))
genre = db.Column(db.String(32))
subgenre = db.Column(db.String(32))
def boxart_url(self):
return self.boxart
def __repr__(self):
return '<Game %r>: %r' % (self.version_name, self.dad)
The page's view just send the entire object (game) to the page.
The url that is arriving on the page is:
/home/removed_my_pc_user/app/media/boxart/image.jpg
Edit:
I just remembered that I'm using a virtual environment on the project.
Is it related to chmod?
Thanks in advance!!
Well, It's strange but I will answer my own question.
The solution is simple, I chose to store only the file names inside the database.
Then I created a route to a view that will return the file using the send_from_directory function from flask.ext.uploads
#app.route('/boxart/<filename>')
def uploaded_boxart(filename):
return send_from_directory(app.config['UPLOAD_FOLDER'],filename)
So, inside the template we can use the url_for() function to load the file, sending as a parameter the filename stored inside the database.
<img src="{{ url_for('uploaded_boxart', filename = game_son.boxart)}}" />
Also, I think that one of my mistakes was putting the media/ folder inside the app/ folder, and not inside the root directory. So I also reconfigured the UPLOAD_FOLDER in app.config.
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
I have been looking at form validation in python using Django as this seems to be the default way to do it, other than checking each field, performing some validation and kicking out a specific message for each field that is badly formed. Ideally I want the benefits of form validation, but I do not know how to couple this Django way with the .css way I am displaying by form.
My form is templated HTML with a css behind to handle the display. The code uses data to send back the form if there is an issue and displays the form which was created previously. So in a nutshell, how do we couple validation to a pre formatted HTML form with css without individually validating.
Here is the code I am using:
Looking at those references etc http://www.djangobook.com/en/2.0/chapter07/, I have been unable to come up with a good way to couple everything together. The problem relates to send back the form and contents if the form is not valid. So what I am doing is pulling out each generated form item by item and displaying in the .html file.
So my question is. How do I get this working. Now I can display the form with css style sheet, but I cannot seem to get validation working on the field and I'm always generating an error.
class Quote(db.Model):
email = db.StringProperty(required=True)
class QuoteForm(djangoforms.ModelForm):
class Meta:
model = Quote
exclude = ['entry_time']
class MainPage(webapp.RequestHandler):
def get(self):
form = QuoteForm();
template_values = {}
path = os.path.join(os.path.dirname(__file__), 'index.html')
self.response.out.write(template.render(path, {'form': form}))
def post(self):
data = QuoteForm(data=self.request.POST)
if data.is_valid():
# save here
self.redirect('/Confirm.html')
else:
template_values = {}
path = os.path.join(os.path.dirname(__file__), 'index.html')
self.response.out.write(template.render(path, {'form': data}))
and the part of the .html file is here
<div>
{{ form.email.errors }}
<label for="id_email">Your e-mail address:</label>
{{ form.email }}
</div>
It would nothing that I put into the email field validates correctly. I'm not sure why!? I'm losing the information I have already put into the form. How do I retain this information and actually do proper validation. The model suggests that only a non blank string is required, but nothing ever satisfies the validation.
Thanks
"Customizing Form Design" section of http://www.djangobook.com/en/2.0/chapter07/ explains how to style forms to go with your HTML and CSS.
Looking at those references etc http://www.djangobook.com/en/2.0/chapter07/, I have been unable to come up with a good way to couple everything together. The problem relates to send back the form and contents if the form is not valid. So what I am doing is pulling out each generated form item by item and displaying in the .html file.
So my question is. How do I get this working. Now I can display the form with css style sheet, but I cannot seem to get validation working on the field and I'm always generating an error.
class Quote(db.Model):
email = db.StringProperty(required=True)
class QuoteForm(djangoforms.ModelForm):
class Meta:
model = Quote
exclude = ['entry_time']
class MainPage(webapp.RequestHandler):
def get(self):
form = QuoteForm();
template_values = {}
path = os.path.join(os.path.dirname(__file__), 'index.html')
self.response.out.write(template.render(path, {'form': form}))
def post(self):
data = QuoteForm(data=self.request.POST)
if data.is_valid():
# save here
self.redirect('/Confirm.html')
else:
template_values = {}
path = os.path.join(os.path.dirname(__file__), 'index.html')
self.response.out.write(template.render(path, {'form': data}))
and the part of the .html file is here
<div>
{{ form.email.errors }}
<label for="id_email">Your e-mail address:</label>
{{ form.email }}
</div>
It would nothing that I put into the email field validates correctly. I'm not sure why!? I'm losing the information I have already put into the form. How do I retain this information and actually do proper validation. The model suggests that only a non blank string is required, but nothing ever satisfies the validation.