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)
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
I have an HTML template that lets the user select a date via jQuery datepicker.
How can I pass the date selected into an action?
The idea is, the user selects a date, then that passes to Flask's route.py, via app.route("/date/<date>")
calendar.html
{% block topscripts %}
<link rel="stylesheet" type="text/css" href= "{{ url_for('static',filename='styles/calendar.css') }}">
<script>
$(function() {
$("#datepicker").datepicker({dateFormat: 'yy-mm-dd'});
});
</script>
{% endblock %}
{% block content %}
<form method="post" action="{{ url_for('specific_date', date='2019-04-11') }}">
<p>Date: <input type="text" id="datepicker" name='go-to-date'></p>
<input type="hidden" name="calendar-form">
<input type="submit">
</form>
{% endblock %}
So, when the user selects a date in the datepicker ID, I want to pass that date to the url_for. Currently I hardcoded the date (2019-04-11) just to check that it works, and it does. How can I have that part be dynamic to whatever the user selects in the Calendar?
...If it helps, here's in routes.py (default_template() is the function that renders the template in the end).:
#app.route("/date/<date>/", methods=["GET", "POST"])
def specific_date(date):
print("\n\nDate:", date, "\n\n")
images = get_files_on(date)
print("\n\nSpecific date images:", images)
return default_template(date=date, image_list=images)
Make a POST request to the /date route like so.
Changes to calendar.html:
{% block content %}
<form method="post" action="{{ url_for('specific_date') }}">
<p>Date: <input type="text" id="datepicker" name='go-to-date'></p>
<input type="hidden" name="calendar-form">
<input type="submit">
</form>
{% endblock %}
Changes to the date route:
from flask import request
# only allow POST request method
#app.route("/date/", methods=["POST"])
def specific_date():
# getting the date from the POST request
date = request.form['go-to-date']
print("\n\nDate:", date, "\n\n")
images = get_files_on(date)
print("\n\nSpecific date images:", images)
return default_template(date=date, image_list=images)
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
So I'm trying to pass a value from a Jinja2 template back to my Python code. I'm trying to do this with a hidden input. My form class is this:
class TrueOrFalseForm(flask_wtf.FlaskForm):
choice = RadioField(choices=[('True', 'TRUE'), ('False', 'FALSE')], validators=[validators.InputRequired()])
hidden = HiddenField()
submit = SubmitField('Submit')
And my form is this:
<form autocomplete="off" action="" method="post">
{{ form.hidden_tag() }}
<div style="text-align: center">
<div style="display: inline-block">
{{ form.choice }}
{{ form.hidden(value="{{ result }}") }}
{{ form.submit(class_="btn btn-primary btn-lg") }}
</div>
</div>
</form>
result is a string that I'm passing when rendering the template.
When checking the value of form.hidden.data, though, it comes back as ''. The tag also renders as <input id="hidden" name="hidden" type="hidden" value="">.
I've also tried doing value={{ result }} instead of value="{{result}}" but that makes Jinja throw a TemplateSyntaxError.
Any idea on how to do this?
EDIT:
I'm overwriting result every time I call the function.
This is my route function:
#app.route('/', methods=['GET', 'POST'])
def home():
form = forms.TrueOrFalseForm()
x = random.randint(-100, 100)
y = random.randint(-100, 100)
statement_str = generate_statement_string(2)
tree = BinTree.build_tree(statement_str)
statement_result = BinTree.solve_tree(tree, x, y) # result gets overwritten here
if form.validate_on_submit():
if not flask_login.current_user.is_anonymous:
# same as the else, except with some sql, not relevant
else:
if form.choice.data == form.hidden.data:
flask.flash('Correct!')
else:
flask.flash('Incorrect!')
return flask.render_template('home.html', x_value=str(x), y_value=str(y), statement=statement_str,
result=str(statement_result), form=form)
{{ form.hidden(value="{{ result }}") }} is already in templating syntax with the outer double curly brackets. Therefore, you should just be able to plainly write the result variable, like this: {{ form.hidden(value=result) }}
EDIT
Replace {{ form.hidden_tag() }} with {{ form.csrf_token() }} as well as doing what is in my original answer.
You may also have to instantiate the form with form = forms.TrueOrFalseForm(request.form). Some forms behave weirdly if you don't do that.
Since you're using {{ form.hidden_tag() }} in your template, you do not need to explicitly render the hidden form field. It will be included in the hidden_tag() call.
You can set the value of the hidden field in your views before rendering the template.
views.py
form.hidden.data = result
return render_template("index.html",form=form)
index.html
<form autocomplete="off" action="" method="post">
{{ form.hidden_tag() }}
<div style="text-align: center">
<div style="display: inline-block">
{{ form.choice }}
{{ form.submit(class_="btn btn-primary btn-lg") }}
</div>
</div>
</form>
My proposal is:
<input type="hidden" id="locphoto" value="{{ mbrs.photoName|safe }}" />
Previous answer are correct but I think they need some correction putting safe in variable jinja:
I know how to mutiple file upload in djnago. I use:
<form enctype="multipart/form-data" action="" method="post">
{% csrf_token %}
<p>Press control to upload more than image at same time</p>
<input type="file" name="myfiles" multiple>
<input type="submit" name="upload" value="Upload">
</form>
but what I want is a single file upload, but permit user to click in a"+" button and automatic create a new file upload, permit user upload mutiple files. like attach a file in hotmail.
You're looking for a FormSet - a set of multiple forms, and some JavaScript to populate new forms.
https://docs.djangoproject.com/en/dev/topics/forms/formsets/
Here are some references to JS code that will help dynamically build the HTML for new forms:
Dynamically adding a form to a Django formset with Ajax
Setting up the formsets is easy (it's documented everywhere), but you might want help with the JS part:
I actually use a different method to dynamically add forms. I set up a hidden div with formset.empty_form which comes with easily replaceable __prefix__es in its attributes:
var form_count = {{ formset.total_form_count }};
$('#add_form').click(function() {
var form = $("#empty_form").html().replace(/__prefix__/g, form_count);
$('#forms').append(form);
form_count++;
$('#id_form-TOTAL_FORMS').val(form_count);
});
<div id="empty_form" style="display:none;">
{{ formset.empty_form.as_p }}
</div>
<div id="add_form">Add another form</div>
<form id="forms">
{{ formset.management_form }}
{% for form in formset %}
{{ form.as_p }}
{% endfor %}
</form>