Django newbie question....
I'm trying to write a search form and maintain the state of the input box between the search request and the search results.
Here's my form:
class SearchForm(forms.Form):
q = forms.CharField(label='Search: ', max_length=50)
And here's my views code:
def search(request, q=""):
if (q != ""):
q = q.strip()
form = SearchForm(initial=q)
#get results here...
return render_to_response('things/search_results.html',
{'things': things, 'form': form, 'query': q})
elif (request.method == 'POST'): # If the form has been submitted
form = SearchForm(request.POST)
if form.is_valid():
q = form.cleaned_data['q']
# Process the data in form.cleaned_data
return HttpResponseRedirect('/things/search/%s/' % q) # Redirect after POST
else:
form = SearchForm()
return render_to_response('things/search.html', {
'form': form,
})
else:
form = SearchForm()
return render_to_response('things/search.html', {
'form': form,
})
But this gives me the error:
Caught an exception while rendering: 'unicode' object has no attribute 'get'
How can I pass the initial value? Various things I've tried seem to interfere with the request.POST parameter.
Several things are not good here...
1) The recommended thing after a POST is to redirect. This avoids the infamous popup saying that you are resubmitting the form when using the back button.
2) You don't need to say if request.method == 'POST', just if request.POST. That makes your code easier to read.
3) The view generally looks something like:
def myview(request):
# Some set up operations
if request.POST:
form=MyForm(request.POST)
if form.is_valid():
# some other operations and model save if any
# redirect to results page
form=MyForm()
#render your form template
That is not to say that there can't be much simpler and much more complicated views. But that is the gist of a view: if request is post process the form and redirect; if request is get render the form.
I don't know why you are getting an unicode error. I can only think that it is related to one of your models that you don't provide.
The error, as spookylukey mentions is in his comment, most likely is caused by you submitting a string instead of a dict to the initial parameter.
I really recommend the django documentation, in particular the tutorial., but there is also the very nice Django Book.
All that said, I think you want something like:
def search(request, q=None):
if request.POST:
form = SearchForm(request.POST)
if form.is_valid():
q = form.cleaned_data['q']
url=reverse('search_results', args=(q,))
return HttpResponseRedirect(url)
if q is None:
form = SearchForm()
else:
form = SearchForm(initial={'q': q})
return render_to_response('things/search.html', {
'form': form,
})
Notice that the parameter to initial is a dict of the field values of your form.
Hope that helps.
Django forms are not particularly helpful for your use case. Also, for a search page, it's much better to use a GET form and maintain state in the URL. The following code is much shorter, simpler and conforms far better to HTTP standards:
def search(request):
q = request.GET.get('q','').strip()
results = get_some_results(q)
render_to_response("things/search.html", {'q': q, 'results': results})
The template:
<form method="GET" action=".">
<p><input type="text" value="{{ q }}" /> <input type="submit" value="Search" /></p>
{% if q %}
{% if results %}
Your results...
{% else %}
No results
{% endif %}
{% endif %}
</form>
Related
I mading a project and i need to get checkbox values in sequence, but django do not return anything when that checkbox are unchecked.
how can i do that return False instead of nothing?
forms.py
class myFormExemple(forms.Form):
checkbox = forms.BooleanField(required=False)
views.py
def MyViewExemple(request):
if request.method == 'POST':
print(request.POST.getlist('checkbox'))
context = {
'form': myFormExemple
}
return render(request, "cadastro/myHTMLTemplate.html", context)
and myHTMLTemplate.html:
<form method="post" id='form'>
{% csrf_token %}
{{form.checkbox}}
<button type="submit">save</button>
</form>
If the checkbox is not checked, the POST request will indeed not contain the name of that checkbox, that is how the HTML form works.
You can however validate and clean the data with your form, and transform this into a boolean with:
def MyViewExemple(request):
if request.method == 'POST':
form = myFormExemple(request.POST)
if form.is_valid():
print(form.cleaned_data['checkbox'])
context = {
'form': myFormExemple()
}
return render(request, "cadastro/myHTMLTemplate.html", context)
We thus construct a form with request.POST as data source, and then if the form is valid, we can obtain a boolean with form.cleaned_data['checkbox'].
Django's forms are thus not only used to render a form, but also to process data of a form, validate that data, and convert the data to another type.
So here is the form class in views.py:
class SearchForm(forms.Form):
query = forms.CharField(label='Search', max_length=10)
The form is added to the concerned page as:
<form action="/encyclopedia/search.html" method="get">
<input class="search" type="text" name="q" placeholder="Search Encyclopedia">
{{ form }}
</form>
I've tried to implement the view function in two ways:
first:-
def search(request):
form = SearchForm(request.GET)
if form.is_valid():
query = form.cleaned_data['query']
else:
query = None
return HttpResponse(query)
second:-
def search(request):
form = SearchForm(request.GET)
query = request.GET.get("query")
return HttpResponse(query)
The given HttpResponse is just to check if i have successfully got the desired data inside variable "query". In both ways, it simply returns None.
I see methods to get data from POST all over the internet. Am i missing something here???
I've tried so many things.
Can someone please just write a function they would use to get data from a form using GET method? I will really appreciate it. I have no idea what else to do now....
The name of the parameter is q, since the HTML component is:
<input … name="q" … >
You thus can access it with:
def search(request):
query = request.GET.get('q')
return HttpResponse(query)
or you can use a Form:
class SearchForm(forms.Form):
q = forms.CharField(label='Search', max_length=10)
and work with:
def search(request):
form = SearchForm(request.GET)
if form.is_valid():
query = form.cleaned_data['q']
else:
query = None
return HttpResponse(query)
Note: As is written in the section on Design your urls.
Django encourages beautiful URL design and doesn’t put any cruft in URLs, like .php or .asp.
You thus might want to remove the .html suffix in your paths.
I'm trying to bind a dynamic list of choices to a ModelForm. The form is rendered correctly. However, when using the form with a POST Request, I get an empty form back. My goal is to save that form into the database (form.save()). Any help would be much appreciated.
Model
I'm using a multiple choice select field ( https://github.com/goinnn/django-multiselectfield )
from django.db import models
from multiselectfield import MultiSelectField
class VizInfoModel(models.Model):
tog = MultiSelectField()
vis = MultiSelectField()
Forms
class VizInfoForm(forms.ModelForm):
class Meta:
model = VizInfoModel
fields = '__all__'
def __init__(self,choice,*args,**kwargs):
super(VizInfoForm, self).__init__(*args,**kwargs)
self.fields['tog'].choices = choice
self.fields['vis'].choices = choice
View
Choices are passed from the view when instantiating the form.
def viz_details(request):
options = []
headers = request.session['headers']
for header in headers :
options.append((header, header))
if request.method == 'POST':
form = VizInfoForm(options, request.POST)
#doesnt' get into the if statement since form is empty!
#choices are not bounded to the model although the form is perfectly rendered
if form.is_valid():
form.save()
return HttpResponseRedirect('/upload')
else:
#this works just fine
form = VizInfoForm(options)
return render(request, 'uploads/details.html', {'form': form})
Template
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>Choose variables to toggle between</p>
{{ form.tog }}
<br></br>
<p>Choose variable to be visualized</p>
{{ form.vis }}
<br></br>
<button type="submit">Submit</button>
</form>
You're saying Django doesn't get into your if request.method == 'POST' block.
This tells us that you're not sending your request through the POST method. Your template probably has an error in it, maybe you haven't specified the method on your form, or you made your button to just be a link instead of a submit ?
Show your template so we can say more, unless this was enough to solve your question !
First the code.
The ModelForm (im1 and im2 are models.ImageField):
class TestForm(forms.ModelForm):
checkme = forms.BooleanField(required=True)
class Meta:
model = UserProfile
fields = ('im1', 'im2')
The view:
def test(request):
profile = request.user.get_profile()
form = TestForm(instance=profile)
if request.method == "POST":
form = TestForm(request.POST, request.FILES, instance=profile)
if form.is_valid():
form.save()
return render(request, 'test.html', {'form':form})
The template:
<html>
<head>
<title>Test</title>
</head>
<body>
<form method="post" enctype="multipart/form-data">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="submit" />
</form>
</body>
</html>
The problems:
If im1 contains a valid image, and I check the clear checkbox next to it but don't check checkme and submit, the form comes back with an error saying that checkme is required. Although the form returns with the error, it appears as if im1 has been cleared. In reality it has not because if I reload the form im1 shows back up with its file and clear checkbox.
My question is how can I fix this? Is it something I am doing or is this something to do with django?
Django is acting exactly as it should.
If the request is a POST request, then your form is bound to the data from request.POST and request.FILES. instance=profile is simply telling the form what particular object to save to if all validation passes. Even if your form isn't valid, it's still bound to the data with the cleared image, and that's what you're passing to render().
Firstly, you shouldn't be creating the first bound form if the request method is POST:
def test(request):
profile = request.user.get_profile()
if request.method == "POST":
form = TestForm(request.POST, request.FILES, instance=profile)
if form.is_valid():
form.save()
else:
form = TestForm(instance=profile)
return render(request, 'test.html', {'form':form})
Secondly, why do you want your user to do the same exact action twice if they did indeed want to delete an image but simply missed another checkbox?
If you really need Django to act this way, I would do one of two things. Either create a bound form from an instance of UserProfile and pass both the non-valid form and the newly created form to the template and use the non-valid form for displaying the errors and the other one for displaying the rest of the form:
def test(request):
profile = request.user.get_profile()
if request.method == "POST":
errors_form = TestForm(request.POST, request.FILES, instance=profile)
if errors_form.is_valid():
errors_form.save()
form = errors_form
else:
form = TestForm(instance=profile)
return render(request, 'test.html', {'form':form, 'errors_form': errors_form})
else:
form = TestForm(instance=profile)
return render(request, 'test.html', {'form':form})
OR I'd do the same thing but save the errors from the non-valid form to the newly created form so you don't end up with renders() all over the place:
def test(request):
profile = request.user.get_profile()
if request.method == "POST":
errors_form = TestForm(request.POST, request.FILES, instance=profile)
if errors_form.is_valid():
errors_form.save()
form = errors_form
else:
form = TestForm(instance=profile)
#this is left up to you to implement, but you'd do something like
#form.errors = errors_form.errors
#and also iterate through every form attribute.errors and assign it to
#errors_form attribute.errors etc...
else:
form = TestForm(instance=profile)
return render(request, 'test.html', {'form':form})
Both aren't very elegant solutions and I'm not positive the second one will even work as expected without some more hacks since I'm not completely familiar with the Django Forms implementation.
I don't see that doing this is worth it. As I stated before, you're just creating more work for your user...
After I submit the form for the first time and then refresh the form it gets resubmitted and and I don't want that.
Here's my form in template :
<form action = "" method = "POST"> {% csrf_token %}
{{ form.as_p }}
<input type = "submit" value = "Shout!"/>
</form>
How can I fix this ?
Here's my views:
def index(request):
shouts = Shout.objects.all()
if request.method == "POST":
form = GuestBookForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
shout = Shout(author = cd['author'], message = cd['message'])
shout.save()
form = GuestBookForm()
else:
form = GuestBookForm()
return render_to_response('guestbook/index.html', {'shouts' : shouts,
'form' : form },
context_instance = RequestContext(request))
My guess is that this is a problem in your view.
After successful submission and processing of a web form, you need to use a return HttpResponseRedirect, even if you are only redirecting to the same view. Otherwise, certain browsers (I'm pretty sure FireFox does this) will end up submitting the form twice.
Here's an example of how to handle this...
def some_view(request):
if request.method == "POST":
form = some_form(request.POST)
if form.is_valid():
# do processing
# save model, etc.
return HttpResponseRedirect("/some/url/")
return render_to_response("normal/template.html", {"form":form}, context_instance=RequestContext(request))
Given your recently added view above...
def index(request):
shouts = Shout.objects.all()
if request.method == "POST":
form = GuestBookForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
shout = Shout(author = cd['author'], message = cd['message'])
shout.save()
# Redirect to THIS view, assuming it lives in 'some app'
return HttpResponseRedirect(reverse("some_app.views.index"))
else:
form = GuestBookForm()
return render_to_response('guestbook/index.html', {'shouts' : shouts,
'form' : form },
context_instance = RequestContext(request))
That will use reverse to redirect to this same view (if thats what you are trying to do)
Try:
return redirect ('url', parameter_if_needed)
instead of
return render (request, 'name.hmtl', context)
In my case it works perfectly.
Most likely: When you refresh after submitting the form, you are showing the same form page again (without doing anything). You either need to redirect to the record page or a new page after the form has been submitted.
That way, the form becomes empty its data and will not resubmit when you refresh.
I have found a way and I think it's going to work for any website. what you have to do is add a Htmx cdn or you can call the javascript library from htmx.org like bootstrap CDN.
add this
before body tag
<script src="https://unpkg.com/htmx.org#1.6.0"></script>
add this or go to their website htmx.org
then what you have to do is go to your form tag and add this....
hx-post=" then add the path in here, where you want to redirect" something like this..
contact html
<form hx-post="/contact" hx-target="body" method="post">
</form>
you have to add a target depending on your form type. The above example is a contact form I want that contact form to stay on the same page and target its body like this hx-target="body"
views.py
return render(request, "blog/contact.html")
Use HttpResponseRedirect
create a new view(lets say thank_you) for successful message to display after form submission and return a template.
After successful form submission do return HttpResponseRedirect("/thank-you/") to the new thank-you view
from django.http import HttpResponseRedirect
def thank_you(request, template_name='thank-you.html'):
return render_to_response(template_name,locals(),context_instance=RequestContext(request))
and in urls.py
url(r'^thank-you/$','thank_you', name="thank_you")
Multiple form submission happens because when page refreshes that same url hits, which call that same view again and again and hence multiple entries saved in database. To prevent this, we are required to redirect the response to the new url/view, so that next time page refreshes it will hit that new url/view.
This solution worked for me. After form submission we have have to display a message in our template in form of popup or text in any form so though HttpResponseRedirect may prevent form resubmission but it won't deliver the message so here is what I did.
from django.contrib import messages
def index_function(request):
if request.method == "POST":
form = some_form(request.POST)
if form.is_valid():
# do processing
# save model, etc.
messages.success(request, 'Form successfully submitted') # Any message you wish
return HttpResponseRedirect("/url")
Then inside your template, you can show this message. Since this is global parameter you can display it in any HTML template like the following.
{% if messages %}
<div class="alert alert-success alert-dismissible">
{% for message in messages %}
<p>{{ message }}</p>
{% endfor %}
</div>
{% endif %}