Retrieve individual values in tuple without brackets - python

I'm using a database to retrieve project numbers and put them in a search for a user in a form. I am using an api (ajax google api) for autocompletion on the search.
When putting it as autocomplete in search field it contains brackets and a comma.
I need it just to be like:
24403
24429
not like:
(24403,)
(24429,)
I am unsure how to do this. My current code is:
app.py
class Element(db.Model):
__tablename__ = 'elements'
ProjectNumber = db.Column(db.Integer, primary_key=True)
#app.route("/", methods=["POST", "GET"])
def basicTemplate():
project_number = Element.query.with_entities(Element.ProjectNumber).distinct()
if request.method == "GET":
return render_template('search.html', navigation=project_number)
if __name__ == '__main__':
app.run()
base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="/static\style.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.js"></script>
<link href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/themes/ui-lightness/jquery-ui.css" rel="stylesheet" type="text/css" />
{% block head %}
<title>{% block title %}{% endblock %}- base</title>
{% endblock %}
</head>
<body>
{% block body %}
{% block project_number %} {% endblock %}
{% endblock %}
</body>
</html>
search.html
{% extends 'base.html' %}
{% block head %}
{% endblock %}
{% block project_number %}
<label for="project_code">Project Number</label><input type="text" id="project_code" name="project_code">
<input type="submit">
<script>
$( function() {
var project = [
{% for test in navigation %}
"{{test}}",
{% endfor %}
];
$( "#project_code" ).autocomplete({
source: project
});
} );
</script>
{% endblock %}

The command with_entities always returns a list of tuples.
You can combine multiple jinja filters to get a list of strings that can be passed directly to javascript.
First you extract the first element of all tuples and convert them into strings. You convert the received generator into a list and output it in a javascript-compatible manner.
$(function() {
const projects = {{ navigation | map('first') | map('string') | list | tojson }};
$('#project_code').autocomplete({
source: projects
});
});
Of course you can also perform the extraction and conversion to the string in the endpoint.
Depending on how many projects are stored in your database, I recommend getting the data via ajax.
$(function() {
$('#project_code').autocomplete({
source: '/search',
});
});
#app.route('/search')
def search():
term = request.args.get('term', '')
projects = Element.query.filter(Element.ProjectNumber.ilike(f'%{term}%')).all()
return jsonify([str(p.ProjectNumber) for p in projects])

Related

python flask render_template and another return value

I use Python flask for my web application. the application provide a CSV file to download. CSV file is the response in below code block. Also I need to send a variable to html template. How can I have two return value?
#application.route("/log_analysis", methods=['POST'])
def get_response():
output='The result of your query : '+str(i-1)+' . The full report is downloaded automatically.'
cw.writerows(csv_rows)
response = make_response(si.getvalue())
response.headers["Content-Disposition"] = f"attachment; filename={return_file_name}"
response.headers["Content-type"] = "text/csv"
return render_template('base.html',output=output)
return response, 200
The output will be shown in the html but the response in the second return doesn't work.
After reading your question, I think what you are looking for is something like flash messages. The variable content you are passing in, is just text and used to display a message.
Flash messages
You'll need to set this up in your base.html or whatever template you are rendering.
Template
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link href="{{ url_for('static', filename='css/main.css')}}" rel="stylesheet">
</head>
<body>
<main>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }}">
{{ message }}
</div>
{% endfor %}
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
</main>
</body>
</html>
from flask import render_template, url_for, flash, redirect
#application.route("/log_analysis", methods=['POST'])
def get_response():
output=f'The result of your query : {i-1} . The full report is downloaded automatically.'
cw.writerows(csv_rows)
response = make_response(si.getvalue())
response.headers["Content-Disposition"] = f"attachment; filename={return_file_name}"
response.headers["Content-type"] = "text/csv"
flash(output,'success')
return response, 200
You can also try and do something like alerts in html template

How to retrieve files from Django FileSystemStorage uploaded in a multisteps form using formtools wizard

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

Jinja2 not working in newsletter.html

I have this function which gets data from an API and then uses Jinja2 to insert this data into an HTML file. I want this data to show up in my newsletter.html and be sent as an email. Everything works fine apart from
this happening:
Here are some relevant snippets:
Getting the data as list and templating with Jinja:
# movie_finder.py
movie_list = []
for item in now_playing['results']:
if genre_id in item['genre_ids']:
movie_list.append(item['original_title'])
print movie_list
# Create jinja2 environment
try:
env = Environment(loader=PackageLoader('movie_finder', 'templates'))
template = env.get_template('newsletter.html')
rend = template.render(info=movie_list)
print "Templating successful"
except:
print "Templating fail"
return "Templating fail"
find_movies_with_genre(API_KEY, 878)
In the script that sends the email (which otherwise works fine), I simply do:
from scifi_finder import find_movies_with_genre
Template:
<!-- Newsletter template by https://github.com/derekpunsalan/-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width"/>
<!-- For development, pass document through inliner -->
<link rel="stylesheet" href="css/simple.css">
<style type="text/css">
output print movie_list:
[u'Thor: Ragnarok', u'Blade Runner 2049', u'War for the Planet of the Apes', u'Geostorm']
You are not passing the variables correctly when calling render(), here is what you are doing:
template.render(info=movie_list)
You are passing a variable info, yet in your template, you are referring to movie_list:
{% for item in movie_list %}
{{ item[0] }}
{{ item[1] }}
{% endfor %}
This will not work, you need to refer to the variable you are passing, personally I prefer it this way:
template.render(movie_list=movie_list)
...and in template...
{% for item in movie_list %}
{{ item[0] }}
{{ item[1] }}
{% endfor %}
...as you have it already. You could just as well use...
{% for item in info %}
{{ item[0] }}
{{ item[1] }}
{% endfor %}
...in your template and keep your function call as it is, but personally I find the first variant less confusing when I read it again weeks later.

deform Sequence of Mapping not updating

I have created a view which output a form containing:
class Person(colander.Schema):
name = colander.SchemaNode(colander.String())
age = colander.SchemaNode(colander.Integer(),
validator=colander.Range(0, 200))
class People(colander.SequenceSchema):
person = Person()
class Schema(colander.Schema):
people = People()
class SchemaSpace(colander.MappingSchema):
schema = Schema()
corners = colander.SchemaNode(colander.String())
The output is correct but when I click the button Add Person, nothing happen. The page blink and scrolls up and nothing, I fill anything. Do you know what is happening here? I am using multiples forms, is it coming from here?
My HTML is small and I am using jinja2:
{% extends "layout.jinja2" %}
{% block head %}
<script src="{{request.static_url('deform:static/scripts/jquery-2.0.3.min.js')}}"
type="text/javascript"></script>
<script src="{{request.static_url('deform:static/scripts/bootstrap.min.js')}}"
type="text/javascript"></script>
<script src="{{request.static_url('deform:static/scripts/jquery.form-3.09.js')}}"
type="text/javascript"></script>
<script src="{{request.static_url('deform:static/scripts/jquery-sortable.js')}}"
type="text/javascript"></script>
<script src="{{request.static_url('deform:static/scripts/jquery.maskedinput-1.3.1.min.js')}}"
type="text/javascript"></script>
{% endblock head %}
{% block content %}
<div class="row">
<div class="col-md-8">
<div class="content">
<h1>{{view.view_name}} - {{page_title}}</h1>
<p>Welcome</p>
{{form | safe}}
</div>
</div>
<div class="col-md-4">
<h2>Extra</h2>
<p>some text</p>
</div>
</div>
{% endblock content %}
From what I found from the doc, I added some scripts. But I had some issues so I manually entered them. Using:
<tal:block tal:repeat="reqt view.reqts['css']">
<link rel="stylesheet" type="text/css"
href="${request.static_url(reqt)}"/>
</tal:block>
Issue was:
<script type="text/javascript" src="{{request.static_url(reqt)}}"></script>
File "/Users/roy/Applications/miniconda3/envs/WebAppBatman/lib/python3.6/site-packages/pyramid/url.py", line 644, in static_url
if not os.path.isabs(path):
File "/Users/roy/Applications/miniconda3/envs/WebAppBatman/lib/python3.6/posixpath.py", line 64, in isabs
s = os.fspath(s)
TypeError: expected str, bytes or os.PathLike object, not Undefined
To be complete, here is the view:
#view_config(renderer='templates/settings.jinja2')
def settings(self):
html = []
if 'submit' in self.request.POST:
posted_formid = self.request.POST['__formid__']
for (formid, form) in forms.items():
if formid == posted_formid:
try:
controls = self.request.POST.items()
form['captured'] = form['form'].validate(controls)
html.append(form['form'].render(form['captured']))
except deform.ValidationFailure as e:
# the submitted values could not be validated
html.append(e.render())
else:
if form['captured'] is not None:
html.append(form['form'].render(form['captured']))
else:
html.append(form['form'].render())
else:
for _, form in forms.items():
html.append(form['form'].render())
reqts = forms['form1']['form'].get_widget_resources()
html = ''.join(html)
# values passed to template for rendering
return {'form': html, 'page_title': 'Settings', 'reqts': reqts}
I got it working! It was due to the scripts as I suspected.
Here is a working syntax using jinja2:
{% for reqt in reqts['js'] %}
<script src="{{request.static_url(reqt)}}"
type="text/javascript"></script>
{% endfor %}
I was not able to make it work using tap:repeat.

Generating a recursive sitemap with relative href links

I'm using Flask to expose a local directory of HTML files on a web page.
I am also using a jinja2 to generate the sitemap in the lefthand div of my main endpoint.
I am unable to correctly specify the URL to the endpoint of my subfolders.
As mentioned in the code below, how would I dynamically build a relative link from /docs (i.e. /docs/folder1/subfolder1/SubFolder1Page.html)?
The way I am currently setting the value for href obviously does not work.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Docs Demo</title>
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
</head>
<body>
<div id="container">
<div class="left_frame">
<h1>{{ tree.name }}</h1>
<ul>
{%- for item in tree.children recursive %}
<!-- How would I build a relative link from /docs/ i.e. /docs/folder1/subfolder1/SubFolder1Page.html -->
<li><a href="docs/{{ item.name }}" target="iframe1">{{ item.name }}
{%- if item.children -%}
<ul>{{ loop(item.children) }}</ul>
{%- endif %}</a></li>
{%- endfor %}
</ul>
</div>
<div class="right_frame">
<iframe name="iframe1"></iframe>
</div>
</div>
</body>
</html>
Folder structure example:
How it looks overall displaying the contents of file1.html:
So I figured out a satisfying way of solving my own issue.
I managed to get this very functional result:
Do note that my template is only good for files with .html extension, though it can be easily enhanced to support other file extensions.
Here is my finalized templates\template.html file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Docs Demo</title>
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
</head>
<body>
<div id="container">
<div class="left_frame">
<h1>{{ tree.name }}</h1>
<ul>
{%- for item in tree.children recursive %}
{% if '.html' in item.name %}
<li><a href="docs/{{ item.name }}" target="iframe1">
{{ item.name.split('/')[-1:][0] }}
{%- if item.children -%}
<ul>{{ loop(item.children) }}</ul>
{%- endif %}</a></li>
{% else %}
<li>{{ item.name }}
{%- if item.children -%}
<ul>{{ loop(item.children) }}</ul>
{%- endif %}</li>
{% endif %}
{%- endfor %}
</ul>
</div>
<div class="right_frame">
<iframe name="iframe1"></iframe>
</div>
</div>
</body>
</html>
You can refer to King Reload's answer for an analysis of what I've changed in the template.html file to make this work correctly.
And here is the demo_app.py script that serves my document HTML files via Flask:
import threading
import os
import webbrowser
from flask import Flask, render_template, send_from_directory
app = Flask(__name__, static_folder='static')
ROOT = os.path.dirname(os.path.abspath(__file__))
DOCS_ROOT = os.path.join(app.static_folder, 'docs')
#app.route('/')
def docs_tree():
return render_template('template.html', tree=make_tree(DOCS_ROOT))
#app.route('/docs/<path:filename>')
def send_docs(filename):
return send_from_directory(directory=DOCS_ROOT, 'docs'), filename=filename)
def make_tree(path):
tree = dict(name=os.path.basename(path), children=[])
try:
lst = os.listdir(path)
except OSError:
pass # ignore errors
else:
for name in lst:
fn = os.path.join(path, name)
if os.path.isdir(fn):
tree['children'].append(make_tree(fn))
else:
np = os.path.join(path.replace(DOCS_ROOT, ''), name).replace('\\', '/')
if np.startswith('/'):
np = np[1:]
tree['children'].append(dict(name=np))
return tree
if __name__ == '__main__':
host = 'localhost'
port = '8888'
url = 'http://{h}:{p}'.format(h=host, p=port)
threading.Timer(3, lambda: webbrowser.open(url)).start()
app.run(host=host, port=port, debug=False)
Most notable changes in demo_app.py since asking my original question were the following:
After initializing app, I set DOCS_ROOT using app.static_folder;
In the function send_docs(), I changed the send_from_directory()'s directory argument to use DOCS_ROOT;
Inside of make_tree(), inside the else block of the for loop, I added:
np = os.path.join(path.replace(DOCS_ROOT, ''), name).replace('\\', '/')
if np.startswith('/'):
np = np[1:]
All this does is take the absolute path of name, remove what matches DOCS_ROOT, leaving only the relative path (and then replacing the \\ for /), resulting in a simple relative path from static/docs. If the relative path starts with a /, I remove it (since there is a trailing / from docs in template.html.
For anyone interested in the simplistic stylesheet (static\styles.css) I used (along with some updated enhancements):
html {
min-height:100%;
position:relative;
}
body {
overflow:hidden;
}
.container {
width:100%;
overflow:auto;
}
.left_frame {
float:left;
background:#E8F1F5;
width:25%;
height:100vh;
}
.right_frame {
float:right;
background:#FAFAFA;
width:75%;
height:100vh;
}
.right_frame iframe {
display:block;
width:100%;
height:100%;
border:none;
}
To add onto the solution of #HEADLESS_0NE:
He added a few more if statements in the for loop, like so:
{%- for item in tree.children recursive %}
-> {% if '.html' in item.name %}
<li><a href="docs/{{ item.name }}" target="iframe1">
-> {{ item.name.split('/')[-1:][0] }}
{%- if item.children -%}
<ul>{{ loop(item.children) }}</ul>
{%- endif %}</a></li>
-> {% else %}
-> <li>{{ item.name }}
-> {%- if item.children -%}
-> <ul>{{ loop(item.children) }}</ul>
-> {%- endif %}</li>
-> {% endif %}
{%- endfor %}
Everything with an -> has been changed in the html, I couldn't find what he added in his python or css, but in short:
An if to check if there's a .html in the item.name.
A split on the item.name, so the / gets removed.
An else statement if there's no .html in the item.name.
This basically adds the ul's and the li's in the correct format.
For a more detailed explanation I hope HEADLESS_0NE could provide us of more information what he might've changed in the python script.
With flask, I have built a site map using the following
from flask import url_for
def get_flask_resources():
verbs = ["POST","GET","PUT","DELETE"]
resources = {}
for rule in app.url_map.iter_rules():
if has_no_empty_params(rule):
resource = url_for(rule.endpoint, **(rule.defaults or {}))
if resource not in resources:
resources[resource] = {}
for verb in verbs:
if verb in rule.methods:
resources[resource][verb] = {
'function':rule.endpoint,
'docs':app.view_functions[rule.endpoint].__doc__
}
else:
resource = rule.rule
if resource not in resources:
resources[resource] = {}
for verb in verbs:
if verb in rule.methods:
resources[resource][verb] = {
'function':rule.endpoint,
'docs':app.view_functions[rule.endpoint].__doc__
}
return resources
This function returns a dictionary like this
{
"/endpoint1": {
"GET": {
"docs": "",
"function": "endpoint1"
}
},
"/endpoint2": {
"GET": {
"docs": "",
"function": "endpoint2"
}
},
"/endpoint1/something": {
"POST": {
"docs": "",
"function": "endpoint1_something"
}
},
}
I had an endpoint return this data and then formatted it on the front end. The dictionary keys are the URIs that you would want to use in the links.
This does assume that you will have a flask route set up for each HTML document, which may not be the case.
One benefit of using this would be that it is dynamic if you add any more HTML documents/Flask routes.

Categories