I'm trying to submit two forms within one view. First, a user shares a URL through the first form. My program then renders some graphs and will ask the user at the end of the page to fill out a form and submit it. Here's how I'm trying to solve it:
views.py:
if request == "POST":
if 'first-input-name' in request.POST: # or in request.POST.get('name')
# do something
elseif 'second-input-name' in request.POST: # or in request.POST.get('name')
# do something else
template:
<input type="submit" name="first-input-name"/>
<input type="submit" name="second-input-name"/>
This is the approach I found in answers to similar questions. However, in my requests.POST I don't find the name of my input, and therefore I don't get the expected behavior.
Any ideas on how to solve this?
I recommend a better and less difficult approach, that is to create different action urls with related functions:
In template:
<form action="{% url 'view-1' %}">
<input type="submit" name="first-input-name"/>
# other...
</form>
<form action="{% url 'view-2' %}">
<input type="submit" name="second-input-name"/>
# other...
</form>
In urls:
path(first/, first_view, name="view-1"),
path(second/, second_view, name="view-2"),
In views:
def first_view(request):
if request.method == "POST":
#do something...
def second_view(request):
if request.method == "POST":
#do something...
You can just have one form with two fields to reduce code complexity. And do the below
def single_view(request):
if request.method == "POST":
# i also advice you use underscore(AKA snake_case) instead of hyphens
first_input_name = request.POST.get("first_input_name")
second_input_name = request.POST.get("second_input_name")
This also mean in your inputs you will have value="first_input_name" and value="second_input_name" in your inputs
Related
i am trying to build a django search functionality for my app but the input form keeps returning a none
views.py
def search(request):
if request.method == 'POST':
query = request.POST.get('text')
houses = Product.objects.filter(name__contains='query')
context = {
'houses':houses,
}
return render (request, 'searchresult.html', context)
search.html
<form>
<input type='text' placeholder='search houses>
<button type='submit'>Search</button>
</form>
First off, your python indentation is invalid, and your HTML is also invalid on the input line. I will assume this is a typo in the question, but if not, you have issues there.
Your main problem is the filter for houses:
houses = Product.objects.filter(name__contains='query')
is looking for a name containing the string "query". You need the variable you've just defined.
houses = Product.objects.filter(name__contains=query)
You have an indentation issue in the code you have posted.
You need to add action and method in your Form.
<form action="/url_of_search/" method="post">
Missing quote in input line.
<input type='text' placeholder='search houses'>
You need to use query instead of 'query' in the filter.
Product.objects.filter(name__contains=query)
Things missing in html code:
form action attribute
form method attribute
input field name attribute
<!-- add form attributes method and action -->
<form method="POST" action="{% url '<url_name>' %}">
<!-- add input attribute name to identify the field and pass the value in request body -->
<input type='text' placeholder='search houses' name='search_text'>
<button type='submit'>Search</button>
</form>
update views for search
def search(request):
if request.method == 'POST':
# use input field name to get the search text
query = request.POST.get('search_text')
houses = Product.objects.filter(name__contains=query)
context = {
'houses':houses,
}
return render (request, 'searchresult.html', context)
How do I get Flask to return the user to the same page, filled out the same way, after he/she submits the form on the page? 'render_template' only seems to work if I know all the variables ahead of time. My form is dynamic, and the variables change depending on selections the user has made. See the psuedo-code below for what I'm thinking of. I know render_template doesn't work like this, but is there a way to just say "use the same form values that came in when rendering the template?"
#app.route('./MyPage', methods=['GET', 'POST'])
def MyPage():
if request.method == 'POST':
# Do some stuff
# return render_template('MyPage.html', context=request.form)
else:
# Do some other stuff
The simplest way to do a download in the way you are asking it to use target="_blank" on your form:
<form action="/MyPage" method="POST" target="_blank">
<ul>
{% for input in form %}
<li>{{ input.label }} {{ input }}</li>
{% endfor %}
</ul>
</form>
Then your POST-handling method doesn't need to do anything else but return the CSV:
#app.route('/MyPage', methods=['GET', 'POST'])
def MyPage():
if request.method == 'POST':
# Turn `request.form` into a CSV
# see https://stackoverflow.com/q/26997679/135978 for an example
headers = {'Content-Disposition': 'attachment; filename=saved-form.csv', 'Content-Type': 'text/csv'}
return form_as_csv, headers
else:
# Do some other stuff
If you need multiple buttons on the form, then instead of setting target on the form, you can just set formtarget="_blank" on the button that triggers the CSV:
<form action="/MyPage" method="POST">
<ul><!-- ... snip ... --></ul>
<button name="submit_action" value="SAVE_AS_CSV" formtarget="_blank">Save as CSV</button>
<button name="submit_action" value="RUN_CALCULATION">Run Calculation</button>
</form>
Then you just need to add a check for request.form['submit_action'] in your if request.method == 'POST' block and you're off to the races:
if request.method == 'POST':
if request.form['submit_action'] == 'SAVE_AS_CSV':
# Return the CSV response
else:
# Return the calculation response
See also: Writing a CSV from Flask framework
I am using Django for develop a website. The website is intended to use to search information stored in a MySQL database.
This is the current basic flow of the web site.
1) index.html - this has a form to select an option
2) according the option, users will redirect to search.html (include a form)
3) once the user provides the criteria, the result will be displayed in reply.html
In my views.py , I have two functions.
from django.shortcuts import render
from website.models import WebsiteRepository
from .forms import SearchForm
from .forms import SelectTypeForm
def Search(request):
if request.method == 'POST':
#do something
return render(request, 'reply.html', {'env_dict':env_dict})
else:
#do something
return render(request, 'search.html', context = context)
def index(request):
if request.method =='POST':
#do something
return render(request, 'search.html', context = context)
else:
#do something
return render(request, 'index.html', context= context)
When I go to index.html page, I can select a option and it will direct me to search.html. After, I fill the form there and submit, it wont give me the reply.html page.
I have a feeling that, I could make this work by changing urls.py.
from django.urls import path
from website import views
urlpatterns = [
path('', views.index, name='index'),
#path('search/', view.Search, name ='Search')
]
I tried to google it. But its too much details and Iam kind of lost.
Do any of you guys know how to achieve this?
Thanks
search.html
{% extends "base_generic.html" %}
{% block content %}
<h3>Welcome to search information Repository</h3>
<form method="post">
{% csrf_token %}
{{form.as_p}}
<button type = 'submit'>submit</button>
</form>
{% endblock %}
index.html
{% block content %}
<h3>Welcome to information Repository</h3>
<form method="post">
{% csrf_token %}
{{form.as_p}}
<button type = 'submit'>submit</button>
</form>
just for clarify things more, ill add the forms.py too
from django import forms
from .models import WebsiteRepository
class SearchForm(forms.Form):
websiterepository = WebsiteRepository
env_indicators = websiterepository.objects.filter (key_aspect='Environmental').values_list('repo_id','indicator')
indicator = forms.ChoiceField(choices=env_indicators,label = 'Indicator' )
OPTIONS = (('2000','2000'),('2001','2001'),('2002','2002'), ('2003','2003'),('0000','0000'),)
year = forms.ChoiceField(choices=OPTIONS)
class SelectTypeForm(forms.Form):
OPTIONS = (('1', 'Envirnmental Indicators'),('2','Economic Indicators'),('3','Social Indicators'),)
types = forms.ChoiceField(choices=OPTIONS)
Your code is wrong on many points.
First thing first: for a search, you want a GET request, not a POST (POST is for updating the server's state - adding or updating your database mostly). This is the semantically correct method (since you want to GET data), and it will allow a user to bookmark the url.
Second point: you don't want to submit the search form to the index view but to the search view. No need for redirects etc, just use the {% url %} templatetag to fill the action attribute of your form (you of course need to have a 'Search' url in your urls.py):
<form method="get" action="{% url 'Search' %}">
{% csrf_token %}
{{form.as_p}}
<button type = 'submit'>submit</button>
</form>
if you want to have this form on more than one page (which is often the case for search forms), use an inclusion tag tha will take care of creating an unbound SearchForm and render the template fragment.
Then in your search view, you only want GET requests, and do not use two different templates, this will only lead to useless duplication.
def Search(request):
form = SearchForm(request.GET)
# use the form's data - if any - to get search results
# and put those results (even if empty) in you context
return render(request, 'reply.html', {'env_dict':env_dict})
And finally, your search form is totally broken:
class SearchForm(forms.Form):
# this is totally useless
websiterepository = WebsiteRepository
# this will only be evaluated once at process startup, so you will
# get stale data in production - and probably different data
# per process, in a totally unpredictable way.
# You need to either put this in the form's __init__ or wrap it
# in a callable and pass this callable
env_indicators = websiterepository.objects.filter (key_aspect='Environmental').values_list('repo_id','indicator')
indicator = forms.ChoiceField(choices=env_indicators,label = 'Indicator' )
# are you going to manually add a new year choice every year ???
OPTIONS = (('2000','2000'),('2001','2001'),('2002','2002'), ('2003','2003'),('0000','0000'),)
year = forms.ChoiceField(choices=OPTIONS)
For the "indicators" ChoiceField you want something like:
def get_indicators_choices():
return Websiterepository.objects.filter (key_aspect='Environmental').values_list('repo_id','indicator')
class SearchForm(forms.Form):
# IMPORTANT : we are NOT calling the function here, just
# passing it (python functions are objects) to the field, which
# will call it everytime the form is instanciated, so you don't
# have stale data
indicator = forms.ChoiceField(
choices=get_indicator_choices,
label='Indicator')
As a last note: be consistent with your namings (ie why name one view in all lower (index) and capitalize the other (Search) ? Whichever convention you choose (I strongly suggest respecting pep8 here), at least stick to it for the whole project.
The problem is that code is not redirecting to /search, instead rendering search.html after post from index.html.
Try doing like-
views.py-
#your code
def index(request):
#do something
if request.method == 'POST':
return redirect('Search')
else:
#render index.html
def search(request):
#do something
if request.method == 'POST':
#render reply.html
else:
#render search.html
Another way to achieve this is if you specify action in your form so that form posts on /search.
search.html
<form method="post" action="/search">
{% csrf_token %}
{{form.as_p}}
<button type = 'submit'>submit</button>
</form>
{% if firstpass != secondpass %}
errors.append('Passwords are not the same')
I am trying to make a page where users can change their personal information. This one in particular pertains to checking whether the password textbox (firstpass) and password re-entering textbox (secondpass) contain the same password. For some reason, I am getting a compiler error on the line with the != sign. Can anyone suggest why? : (
If my taught is right you need to append this error message to errors list. Then you need to change it a bit.
First you need to create a form in template. I just create a dummy form for understand what happening.
<form action="/password-confirm/" method="post">{% csrf_token %}
<input type="text" name="firstpass">
<input type="text" name="secondpass">
<input type="submit" name="">
</form>
Second Create a view in views.py.
def password_confirm(request):
if request.method == "POST":
firstpass = request.POST["firstpass"]
secondpass = request.POST["secondpass"]
if firstpass == secondpass:
// Write code if passwords are same.
else:
errors.append("Passwords are not the same")
return render(request, 'password_confirm.html')
Third in urls.py.
url(r'^password-confirm/$', 'happytenants.views.password_confirm', name='about_us'),
And if you need to display error in template, you just pass the variable to template.
def password_confirm(request):
...
return render(request, 'password_confirm.html', {"errors": errors})
I'm using the code found here (SO.com) to use the same template to both add and edit a record, but when I add a new record and click Submit, I get a 404 on the URL http://192.168.1.3:5678/app/student/edit/None/, and I'm not exactly sure why.
Here is the relevant portion of my urls.py:
url(r'^app/lesson/new/$', 'edit_lesson', {}, 'lesson_new'),
url(r'^app/lesson/edit/(?P<id>\d+)/$', 'edit_lesson', {}, 'lesson_edit'),
Here is the relevant portion of my views.py:
def edit_lesson(request, id=None, template_name='lesson_edit_template.html'):
if id:
t = "Edit"
lesson = get_object_or_404(Lesson, pk=id)
stu = get_object_or_404(Student, pk=sid)
if stu.teacher != request.user:
raise HttpResponseForbidden()
else:
t = "Add"
lesson = Lesson()
if request.POST:
form = LessonForm(request.POST, instance=lesson)
if form.is_valid():
form.save()
# If the save was successful, redirect to another page
return view_lessons(request)
else:
form = LessonForm(instance=lesson)
return render_to_response(template_name, {
'form': form,
't': t,
'lesson': lesson,
}, context_instance=RequestContext(request))
And finally, here is my template:
<h1>{{ t }} Lesson</h1>
<form action="/app/lesson/edit/{{ lesson.id }}/" method="post"> {% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
I'm certain that I'm missing something really easy, but I can't seem to put my finger on it. I'm using Django 1.3.1 if that makes any difference.
Thanks,
MC
There's no need to specify any URL in the form's action attribute. Just do
<form action="" method="post">
and it will POST back to the URL that you originally used to access it, which is what you want.
In add case {{ lesson.id }} is None, because lesson is unsaved Lesson() instance, without pk, so your form is being fired to nonexistent URL.
I recommend separating create and edit views and processing them in different ways (or even inherit generic views - with new class-based generic views it's easy and pleasant).
Also, use {% url %} template tag everywhere instead of hard-coded urls.