I'd like to visualize a form in home/admin page that allows users to select a value from a list (values belongs to a db table) and a button to execute a custom python method.
I am not able to understand if it's possibile to show a form without showing data of if it's possible to run code without flask-admin.
P.s. the same (simple) code that I use to create a form (just 2 datepickers) in Flask works but as soon as I put it in /home/admin the html and flask-admin cannot talk (exchange the values in the form) anymore.
Update:
This is part of my Flask-admin code:
class ExampleForm(Form):
dt_start = DateField('DatePicker', format='%Y-%m-%d')
dt_end = DateField('DatePicker', format='%Y-%m-%d')
#app.route('/admin', methods=['POST','GET'])
def index():
form = ExampleForm()
if form.validate_on_submit():
print("Start date: ", form.dt_start.data.strftime('%Y-%m-%d'))
print("End date: ", form.dt_end.data.strftime('%Y-%m-%d'))
return "Form is OK!"
return render_template('admin/index.html', form=form)
HTML CODE:
{% extends "admin/master.html" %}
{% block body %}
<head>
<title>Download form</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<div class="container">
<h1>Please insert the dates</h1>
<br>
<form action="#" method="post">
{{ form.dt_start(class='datepicker') }}
{{ form.hidden_tag() }}
{{ form.dt_end(class='datepicker') }}
{{ form.hidden_tag() }}
<input type="submit"/>
</form>
</div>
{% endblock %}
ERROR:
jinja2.exceptions.UndefinedError: 'form' is undefined
Thanks a lot
Alessandro
Related
I am using Django 3.0 and python 3.8.2 to develop an ads website. To add a post, I used Django formtools wizard. It worked and everything goes nicely. I could save the multiform data. However, I could not retrieve the files from the FileSystemStorage so I can save them. Hence, any help to achieve this or suggestion is much appreciated. I want to retrieve uploaded files, save them to the data base and then delete them from the wizard (from the FileSystemStorage). Note: there is no error and everything is working except that the uploaded files are not saved to the data base even though they are available in the FileSystemStorage. Thus, I want to retrieve them to be able to save them to the data base.
Here is the view class:
TEMPLATES = {"CommonForm": "towns/salehslist/ads_main_form.html",
"JobForm": "towns/salehslist/forms/jobPostForm.html",
}
FORMS = [
("CommonForm", CommonForm),
("JobForm", JobForm, JobImagesForm),
]
class PostWizard(SessionWizardView):
# The form wizard itself; will not be called directly by urls.py,
# but rather wrapped in a function that provide the condition_dictionary
_condition_dict = { # a dictionary with key=step, value=callable function that return True to show step and False to not
"CommonForm": True, # callable function that says to always show this step
"JobForm": select_second_step, # conditional callable for verifying whether to show step two
}
file_storage = FileSystemStorage(
location=os.path.join(settings.MEDIA_ROOT, "photos")
)
def get_template_names(self):
return [TEMPLATES[self.steps.current]]
def done(self, form_list, form_dict, **kwargs):
# form_data = [form.cleaned_data for form in form_list]
# print(form_data)
data = {k: v for form in form_list for k, v in form.cleaned_data.items()}
data["posted_by"] = self.request.user
instance = Job.objects.create(**data)
print("YOU ARE HERE")
print(self.request.FILES.getlist("files"))
for file in self.request.FILES.getlist("files"):
print(file)
img_instance = JobImages.objects.create(job=instance, images=file)
img_instance.save()
return HttpResponse("<h1>Post Page </h1>")
Here is the url:
url(r'^post/$', PostWizard.as_view(FORMS, condition_dict = PostWizard._condition_dict)),
Here is the html template:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
{% load static %}
{% load crispy_forms_tags %}
{% load i18n %}
<script type="text/javascript" src="{% static 'towns/assets/fontawesome-free-5-12-0-we/js/all.js' %}">
</script>
<link rel="stylesheet" type="text/css" href="{% static 'towns/assets/bootstrap-4.4.1/dist/css/bootstrap.min.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'towns/assets/fontawesome-free-5-12-0-we/scc/fontawesome.min.css' %}">
<!-- file uploader font -->
<link type="text/css" rel="stylesheet" media="all" href="{% static 'towns/assets/fileuploader-2.2/dist/font/font-fileuploader.css' %}" >
<link rel="stylesheet" type="text/css" href="{% static 'towns/style/forms/jobPostForm.css' %}">
</head>
<body>
<div class="container">
<div class="row h-100">
<div class="col-lg-6 my-auto">
<div class="breadcrumb">
<div class="ads-form-title">
Job Post
</div>
</div>
<form class="" action="" method="POST" enctype="multipart/form-data" novalidate id="jobPost">
{% csrf_token %}
{{ wizard.management_form }}
{{ wizard.form.media }}
<hr>
<div class="form-group">
<div>
{{ wizard.management_form }}
{% if wizard.form.forms %}
{{ wizard.form.management_form }}
{% for form in wizard.form.forms %}
{{ form|crispy }}
{% endfor %}
{% else %}
{{ wizard.form|crispy }}
{% endif %}
</div>
</div>
<hr>
<!-- upload images -->
<!-- file input -->
<input type="file" name="files" class="files">
<center>
<button type="submit" class="btn btn-primary" style="position:relative; width: 33%; height: 100%;"> Submit </button>
</center>
</form>
</div>
</div>
</div>
<script type="text/javascript" src=" {% static 'towns/assets/jquery-3.5.0.min.js' %}">
</script>
<script type="text/javascript" src=" {% static 'towns/assets/bootstrap-4.4.1/dist/js/bootstrap.min.js' %}">
</script>
<script type="text/javascript" src="{% static 'towns/assets/fileuploader-2.2/dist/jquery.fileuploader.min.js' %}" >
</script>
</body>
</html>
When user hit submit button of the form wizard, def post() method is called. def post() will
validate the form and
save data and files into session.
then if the current page is the last page, it will
render_done, which is def done()
The reason why your request.files is empty is because, the current request does not have files or data associated with it. All your data and files are saved to the session when you hit the submit buttons which are preceding the done() method.
Since I do not know how your form is structured, I am not sure how to definetely solve your problem. But something like below should do:
# iterate over all forms in the form_list
for form in form_list:
# check if current form has files
if bool(self.get_form_step_files(form)):
# if yes, do something
uploadedfiles = form.files
print(uploadedfiles)
for key, value in uploadedfiles.items():
jobimage = JobImage(job=?, image=value)
jobimage.save()
Update
wihtout your model, form structure, and template, it is difficult to come out with complete solution. I am posting a minimum working example.
1. in models.py
class MyPost(models.Model):
field1 = models.CharField(max_length=50)
field2 = models.CharField(max_length=50)
class Photo(models.Model):
mypost = models.ForeignKey(MyPost, on_delete=models.CASCADE)
photo = models.FileField(blank=True, null=True)
2. In forms, you will not include photoform, because you are trying to uplaod more than one images.
from .models import MyPost, Image
from django import forms
class step_first_form(forms.ModelForm):
class Meta:
model = MyPost
fields = ['field1']
class step_second_form(forms.ModelForm):
class Meta:
model = MyPost
fields = ['field2']
in template, you can keep your first template same as whatever you have. For the second one, say, you will have MyPost's field2 and 2 image inputs, you will change it to:
<form action="" method="post" enctype="multipart/form-data">{% csrf_token %}
<table>
{{ wizard.management_form }}
{% if wizard.form.forms %}
{{ wizard.form.management_form }}
{% for form in wizard.form.forms %}
{{ form }}
{% endfor %}
{% else %}
{{wizard.form.field2}}
<input type="file" name ="imagefile1" >
<input type="file" name ="imagefile2" >
{% endif %}
</table>
.......
.....
.....
</form>
make sure you include enctype="multipart/form-data" otherwise your files will not be uploaded.
make sure you have different names for filefield otherwise only one
will be saved to your model.
4. in views.py
def done(self, form_list, form_dict, **kwargs):
data = [form.cleaned_data for form in form_list]
# you can print out and inspect this data, and you will know
# the below code.
# you will need to modify data[0]['field1'] etc ...
print(data)
mypost = MyPost()
mypost.field1 = data[0]['field1']
mypost.field2=data[1]['field2']
mypost.save()
print('mypost')
print(mypost)
# the below part is for saving image files to model
for form in form_list:
# check which form has files
if bool(self.get_form_step_files(form)):
uploadedfiles= form.files
print(form.files)
for key, value in uploadedfiles.items():
photo = Photo(mypost=mypost,photo=value)
photo.save()
else:
print('not bool')
return render ##### whatever template you want to render
This question already has answers here:
How does the 'with' statement work in Flask (Jinja2)?
(2 answers)
Closed 4 years ago.
I was checking the message flashing in flask framework from here.
This is a basic example where a template file (Index.html) is used to provide the initial link and another template file (Login.html) creates the form.
The files are:
Login.html:
<!doctype html>
<html>
<body>
<h1>Login</h1>
{% if error %}
<p><strong>Error:</strong> {{ error }}
{% endif %}
<form action = "" method = post>
<dl>
<dt>Username:</dt>
<dd>
<input type = text name = username
value = "{{request.form.username }}">
</dd>
<dt>Password:</dt>
<dd><input type = password name = password></dd>
</dl>
<p><input type = submit value = Login></p>
</form>
</body>
</html>
Index.html:
<!doctype html>
<html>
<head>
<title>Flask Message flashing</title>
</head>
<body>
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul>
{% for message in messages %}
<li<{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
<h1>Flask Message Flashing Example</h1>
<p>Do you want to <a href = "{{ url_for('login') }}">
<b>log in?</b></a></p>
</body>
</html>
Flash.py:
from flask import Flask, flash, redirect, render_template, request, url_for
app = Flask(__name__)
app.secret_key = 'random string'
#app.route('/')
def index():
return render_template('index.html')
#app.route('/login', methods = ['GET', 'POST'])
def login():
error = None
if request.method == 'POST':
if request.form['username'] != 'admin' or \
request.form['password'] != 'admin':
error = 'Invalid username or password. Please try again!'
else:
flash('You were successfully logged in')
return redirect(url_for('index'))
return render_template('login.html', error = error)
if __name__ == "__main__":
app.run(debug = True)
The part that is confusing me is inside index.html. It's using with messages = get_flashed_messages() to get the messages from the session. I do not fully understand why it's using with? I know with is used for resources, files, streams etc to control the closing procedure (and for not leaving something open when something goes wrong etc). What's the resource it's accessing using with in this context?
I tried removing it (in this context) and an error occurred:
jinja2.exceptions.TemplateSyntaxError: Encountered unknown tag
'messages'.
Also, example use cases from programcreek.com does not use with with get_flashed_messages so what's the case here?
Jinja templates are not Python. with in a template is not a Python context manager, it just introduces a new scope; this code definesa new variable messages that is only visible until the endwith.
See the docs.
try to fix index.html
<!doctype html>
<html>
<head>
<title>Flask Message flashing</title>
</head>
<body>
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
<h1>Flask Message Flashing Example</h1>
<p>Do you want to <a href="{{ url_for('login') }}">
<b>log in?</b></a></p>
</body>
</html>
I have the following flask application that displays a dashboard with various buttons. Each button executes a python function. After the execution of such a function I want the application to return to the dashboard. In order to give the user a simple log I want to output some string on the html page. For that thought about a tag above the buttons on the dashboard that get filled with the respective value. How can I do that?
Flask:
from flask import Flask, render_template
app = Flask(__name__)
#app.route("/")
def main():
return render_template('index.html')
#app.route('/something')
def do_something():
print("Hello")
return render_template('index.html', user="Successfully executed!")
if __name__ == "__main__":
app.run()
HTML:
<!DOCTYPE html>
<html>
<head>
<head>
<meta charset="UTF-8">
</head>
<title>MP Reporting</title>
</head>
<body>
<div value=user></div>
Your button
</body>
</html>
For flask template use "{{kwarg}}" i.e. in your example
<div>{{user}}</div>
will render as
<div>Successfully executed!</div>
In addition to other answers, I suggest using Flask's built-in message flashing which is simpler, and neater instead of passing variables to render_template manually. It's simple as that:
(template)
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
<div>{{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
(flask view)
from flask import flash
flash("Successfully executed!")
You can get more information from here.
You can print variables using Jinja2.
To print out the variable user in your example add
{{ user }} in the html template.
If you send a list of items to the html you can output them by using a simple for:
{% for item in items %}
{{ item }}
{% endfor %}
I'm brushing up on my Flask skills to make a "Jeopardy score keeper" application. What I'm trying to do right now is to take a player's score from a database and display it on the screen. I keep getting a "404 NOT FOUND" error. Here's my code:
JeopardyApp.py
#app.route('/layout', methods=['POST'])
def layout():
db = get_db()
try:
cur1 = db.execute('SELECT score FROM {tn} where name="Tyler"'.format(tn="Score"))
score1 = cur1.fetchone()
except:
flash("This person doesn't exist in our database!")
return render_template('layout.html', score=score1)
layout.html
<!doctype html>
<title>JeopardyApp</title>
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='style.css') }}">
<div class=page>
<h1>Jeopardy Score Keeper</h1>
<div class=metanav>
<p>Currently under construction...</p>
</div>
<div>
<p>Tyler</p>
<tr><td>{{ score.score }}</td></tr>
</div>
{% for message in get_flashed_messages() %}
<div class=flash>{{ message }}</div>
{% endfor %}
{% block body %}{% endblock %}
</div>
Any help is appreciated. Thank you.
The route you've set for the layout function is only reachable via the POST method.
By default, your webbrowser uses the GET method.
Change your route this way:
#app.route('/layout', methods=['GET'])
I use dynamic URLs in django. It works fine for integer values, and works for strings if the dynamic part is the end if the URL. When there is some other component in the URL after the dynamic variable, say:
url(r'companies/(?P<comp_id>.+)/buy/?$',views.buy)
Now in the views.buy function,
print comp_id
gives me id1/buy. So, it takes the whole of the remaining URL as the comp_id variable. How do I stop it?
company.html:
<html>
<head>
<title>{{ company.name }}</title>
</head>
<body>
Name:{{ company.name }}<br>
Worth: {{ company.company_worth }}<br>
<form action="/companies/{{ company.comp_id }}/buy/" method = "post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Buy">
</form>
</body>
url(r'companies/(?P<comp_id>.+?)/buy(/|)$', views.buy)
If the comp_id value is id1, this would do the trick.
url(r'companies/(?P<pdf>\w+)/buy/?$',views.buy)