Django form with just a BooleanField - django

I'm rather new to Django and I'm using Django 1.0.
I have this:
forms.py:
class MyForm(forms.Form):
extra_cheeze = forms.BooleanField(required=False,
initial=False,
label='Extra cheeze')
views.py:
def order_something(request):
form = MyForm(request.POST or None)
if request.method == 'POST' and form.is_valid():
# do stuff...
The problem is that the form is not valid unless the checkbox is checked, so there doesn't seem to be a way to get a False value from the field.
As far as I can understand from the docs, it should work. It works if I add a CharField to my form...
Am I misunderstanding something here or is this a bug? (Yes, I have googled but found nothing relevant)
Update: As suggested by #Dominic Rodger, I tried adding a hidden field
dummy = forms.CharField(initial='dummy', widget=forms.widgets.HiddenInput())
and that makes the form valid. This workaround enables me to move on right now, but it would still be interesting to know if I'm misunderstanding something...

There was a bug in the code in my question. Thanks to #d0ugal for helping me spot it by including a slightly different example. The problem was here:
form = MyForm(request.POST or None) # <- PROBLEM HERE!!!!!!!!!!!!!!!!
if request.method == 'POST' and form.is_valid():
# do stuff...
The bug was that I assumed that request.POST would evaluate to True if it was a post. But since browsers don't post anything for a not-checked checkbox, and that was the only field, the POST data was an empty dictionary, which evaluates to False. This caused None to be used as initialization data, causing the form to be unbound and not valid.
#d0ugal's example does the safe thing and tests request.method first.

This also works for me on 1.1, 1.0.3 and 1.0 (I have these three Virtual environments setup). I only tested this in FireFox so if its a browser issue thats another matter but as far as I know they all handle POST data with checkboxes the same.
Here is the full code for the project so you can reproduce at your leisure and compare with yours to see the difference.
Setting up in Ubuntu
$ django-admin.py startproject testing
$ cd testing/
$ python manage.py startapp myfirst
Then in the myfirst app folder;
/myfirst/views.py
from django.shortcuts import render_to_response
from myfirst.forms import MyForm
def testing(request):
if request.method == 'POST':
form = MyForm(request.POST)
if form.is_valid():
result = "valid"
else:
result = "not valid"
else:
form = MyForm()
result = "no post"
return render_to_response('test.html', {'form':form, 'result':result,})
/myfirst/forms.py
from django import forms
class MyForm(forms.Form):
extra_cheeze = forms.BooleanField(required=False,initial=False,label='Extra cheeze')
/myfirst/templates/test.html
<html>
<head>
</head>
<body>
<form action="." method="POST">
{{ form }}
<input type="submit" value="test">
</form>
{{ result }}
</body>
</html>
/urls.py
from django.conf.urls.defaults import *
from myfirst.views import testing
urlpatterns = patterns('',
(r'.*', testing),
)
Then just run the project $ python manage.py runserver and browse to http://localhost:8000/. You'll actually find that required doesn't do anything with the checkbox, since you can't leave it blank - a 'blank' answer is effectively 'no'. If you want to make sure a user selects an answer have a multiple choice where the user has to select yes or no. You could force them to answer with radio buttons too.

This is not an answer, it describes the Django behaviour only.
Django 3.0.7 on Python 3.8.4rc1, Debian 10/testing.
My scenario is html <form> with more django forms.Form rendered classes inside, one of them has BooleanField only. (There are other small <forms> in page like a search form).
class OrderInvoiceTypeForm(forms.Form):
is_business = forms.BooleanField(label="business", initial=False) # with or without required=False
I submit via submit button which IS NOT in OrderInvoiceTypeForm but IS in the html .
With required=False form is always valid and is_business key is in .cleaned_data.
Without required (or with =True):
checked:
form_with_boolean.is_valid() # True
form_with_boolean.cleaned_data # {'is_business': True}
unchecked:
form_with_boolean.is_valid() # False
form_with_boolean.cleaned_data # {}
In 2nd case in dev tools I can see that browser (Chrome) doesn't send the is_business value POST variable. Adding of hidden field will not help.

Related

Understanding where to put the IPN reciever function in django-paypal

I have a similar issue to the post here in regards to setting up the receiver function for the paypal-ipn
What we are trying to do(I don't know this other person but I assume we stand together on this topic) is to understand how to deduce a path to receiving a paypal IPN signal in which we then can update the django database.
I have implemented the django-paypal API by following the directions here
Overall what I do is I create a view in my views.py as follows
def payment_page(request):
"""This fucntion returns payment page for the form"""
if not request.session.get('form-submitted', False):
return HttpResponseRedirect(reverse('grap_main:error_page'))
else:
amount = set_payment()
paypal_dict = {
"business": "business#gmail.com",
"amount": str(amount),
"item_name": "2017 USGF Championships",
"notify_url": "https://15b6b6cb.ngrok.io" + reverse('paypal-ipn'),
"return_url": "https://15b6b6cb.ngrok.io/Confirm",
"cancel_return": "https://15b6b6cb.ngrok.io/RegistrationForm",
}
form = PayPalPaymentsForm(initial=paypal_dict)
context = confirm_information()
context["amount"] = amount
context["form"] = form
request.session['form-submitted'] = False
valid_ipn_received.connect(show_me_the_money)
return render(request, "grap_main/payment.html", context)
Where my then I have payment.html which then create the paypal button simply by using the line as advised in the documentation
{{ form.render }}
Now I can receive a POST to the paypal url that I specified in the documentation however I don't know where I should put my signal function that will grab the IPNs once someone has completed a purchase.
def show_me_the_money(sender, **kwargs):
"""signal function"""
ipn_obj = sender
if ipn_obj.payment_status == ST_PP_COMPLETED:
print 'working'
else:
print "not working"
Right now I am calling this function in the view payment_page() however I know this is before the POST to the paypal and is not correct
I don't understand where I should call the show_me_the_money() function. I am used to creating a views in which is called from the html script as shown below
def register(request):
"""aquire information from new entry"""
if request.method != 'POST':
form = RegisterForm()
else:
if 'refill' in request.POST:
form = RegisterForm()
else:
form = RegisterForm(data=request.POST)
if form.is_valid():
form.save()
request.session['form-submitted'] = True
return HttpResponseRedirect(reverse('grap_main:payment_page'))
.html
<form action="{% url 'grap_main:register' %}" method='post' class="form">
{% csrf_token %}
{% bootstrap_form form %}
<br>
{% buttons %}
<center>
<button name='submit' class="btn btn-primary">Submit</button>
</center>
{% endbuttons %}
</form>
I believe I need to call the function after a person has completed a purchase however I don't know how to target that time window in my code. I want to make sure I handle a case in which the user once there are done paying don't always return to the merchant site.
Any help on this subject would not only benefit me but also the earlier poster. I hope to make a tutorial when I figure this out to help others that might get stuck as well.
Please note that I have also use ngrok to make sure my project is accessible to the paypal IPN service. I am using two urls.py files as well, i which the main one looks as so
urlpatterns = [
url(r'^paypal/', include('paypal.standard.ipn.urls')),
url(r'^admin/', admin.site.urls),
url(r'', include('grap_main.urls', namespace='grap_main')),
]
where the 'grap_main.urls' are all the specific views for my site, ex.
urlpatterns = [
url(r'^$', views.index, name='index'),
url(r'^Confirm', views.confirm, name='confirm'),
url(r'^Events', views.events, name='events'),
.........]
[UPDATE]: Forgot to mention where and how to put the code that you wrote (the handler if you like). Inside the handlers.py file write it like this:
# grap_main/signals/handlers.py
from paypal.standard.ipn.signals import valid_ipn_received, invalid_ipn_received
#receiver(valid_ipn_received)
def show_me_the_money(sender, **kwargs):
"""Do things here upon a valid IPN message received"""
...
#receiver(invalid_ipn_received)
def do_not_show_me_the_money(sender, **kwargs):
"""Do things here upon an invalid IPN message received"""
...
Although signals (and handlers) can live anywhere, a nice convention is to store them inside your app's signals dir. Thus, the structure of your grap_main app should look like this:
project/
grap_main/
apps.py
models.py
views.py
...
migrations/
signals/
__init__.py
signals.py
handlers.py
Now, in order for the handlers to be loaded, write (or add) this inside grap_main/apps.py
# apps.py
from django.apps.config import AppConfig
class GrapMainConfig(AppConfig):
name = 'grapmain'
verbose_name = 'grap main' # this name will display in the Admin. You may translate this value if you want using ugettex_lazy
def ready(self):
import grap_main.signals.handlers
Finally, in your settings.py file, under the INSTALLED_APPS setting, instead of 'grap_main' use this:
# settings.py
INSTALLED_APPS = [
... # other apps here
'grap_main.apps.GrapMainConfig',
... # other apps here
]
Some side notes:
Instead of using standard forms to render the paypal button use encrypted buttons. That way you will prevent any potential modification of the form (mostly change of prices).
I was exactly in your track a few months ago using the terrific django-paypal package. However, I needed to upgrade my project to Python 3. But because I used the encrypted buttons I was unable to upgrade. Why? Because encrypted buttons depend on M2Crypto which doesn't support (yet) Python 3. What did I so? I dropped django-paypal and joined Braintree which is a PayPal company. Now, not only I can accept PayPal payments but also credit card ones. All Python 3!
This is is embarrassing but the problem was that I was not using a test business account... I implemented the answer nik_m suggegested as well so I would advise doing the same. Put the function:
def show_me_the_money(sender, **kwargs):
"""signal function"""
ipn_obj = sender
if ipn_obj.payment_status == ST_PP_COMPLETED:
print 'working'
else:
print "not working"
valid_ipn_received.connect(show_me_the_money)
in handlers.py

django-allauth redirects again to signup/login forms

I am using django-allauth for local account management. I have customized templates ,login.html and signup.html. Both of these templates are placed in templates/account/ dir and both are accessible properly.
site root i.e localhost:8000 points to index.html which includes using {% include%} both the templates on main page.
form action for signup form in signup.hmtl is set to action="{% url 'account_signup' %}" and that of login.html is set to "{% url 'account_login' %}"
Both the templates appears OK on the main page. The problem arises when I try to use these forms for sigin/login. Instead processing the POST for signup or login I am redirected to locahost:8000/accounts/signup/ for signup and localhost:8000/accounts/login/ for login. I guess I am using the right urls that is account_signup and account_login
I have all settings for allauth. Is this is the default behaviour or I'm missing some thing out? Thanking in anticipation
Well I managed to get rid of the problem after spending some hours. Just in case that some one else caughtup in the same situation I would like to share my solution. The problem was forms have been instantiated by View by using prefix SignUpForm(prefix=signupform) and for that reason allauth class AjaxCapableProcessFormViewMixin(object) (which I don't know how it works) was unable to get data from the fields and its if form.is_valid() was always false as the form has error messages dictionary was containing this field is required for all the fields in the form. Jus to test I removed Prefixing from the form instantiation and it worked but make me feel strange as these kind of hidden errors can take upto indefinite time to resolve especially for some niwbie like me
class AjaxCapableProcessFormViewMixin(object):
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
if form.is_valid():
response = self.form_valid(form)
else:
response = self.form_invalid(form)
return _ajax_response(self.request, response, form=form)

Where is form data handled?? (Django)

I have read a lot of tuts and documentation on form creation and handling in Django but I still am confused on certain aspects of the implementation. Specifically, I cannot understand where I should handle the data sent by the form. Is it on the view that is using the form template or is it on another view?
For example, assume an index template with a single form:
*index.html*
{% load url from future %}
<form action="{% url 'Directories:_results'%}" method="post">
Name: <input type="text" name="txtField" />
<input type="submit" name="submit" />
</form>
So now for my view i have two versions:
#1 version (1 view): The same view displays and handles the form
def index(request):
if request.method == 'POST': # If the form has been submitted...
form = dbForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
field = form.cleaned_data['txtField']
#doSomething
else:
form = dbForm() #unbound form
return render(request, 'Directories/index.html', {'form': form})
#2 version (2 views): One view to display the form and one view to handle the form data
#the view that creates the form (unbound)
def index(request):
form = dbForm()
return render(request, 'Directories/index.html', {'form':form})
#the view that handles the data sent during form submission in the index template.
def results(request):
if request.method == 'POST':
form = dbForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
field = form.cleaned_data['txtField']
#doSomething
else:
form = dbForm() #unbound form
return render(request, 'Directories/index.html', {'form': form})
and here is my urls.py:
from django.conf.urls import patterns, url
from Directories import views
urlpatterns = patterns('',
url(r'^$', views.index, name='_index'),
url(r'^results$', views.results, name='_results'),)
As you can see data handling is performed differently in each version and as a result I want to know if any of these is wrong and if both are correct then which one is considered the best practice.
Generally a form will post to the same view it is being displayed on.
You can simplify the view logic like so:
def index(request):
form = dbForm(data=request.POST or None)
if form.is_valid(): # All validation rules pass
field = form.cleaned_data['txtField']
#doSomething
return redirect(success_url)
return render(request, 'Directories/index.html', {'form': form})
Note that it is usually good if you redirect after a successful form post, even if you redirect back to the same view. This prevents the user from being prompted to 'resend form data' if they refresh the page.
You should look at the docs for rendering a form in the template:
https://docs.djangoproject.com/en/dev/topics/forms/#looping-over-the-form-s-fields
If you don't render the field errors, for example, the user will never know what the problem was.
An example of a form that would post to a different view is if say your base template has a 'search' form which appears on every page. When you post this form you don't want to come back to the current view, you want to go to the 'search results' view.
Generally, one view corresponds to one url. Also, same url should show the form and accept the submitted form. With this logic, your first approach is better. One view shows and accepts the form.
However, there are cases where view to show form is different than the one accepts it. For example, a page that has multiple forms. Each form can be submitted to different view. But a different view can be implemented to handle that url and show such forms.
There's nothing wrong with either, it depends on what you want to do. By default forms send the data to the same request but you can send the data to a different view if that's more convenient
For most cases it's usually simpler to use the same view. Using two views is good if you're using an external tool\app\whatever or if you want tighten your security (having the second view only accept requests with post data for example etc.), but will require extra steps (error handling, succesful redirect)
The first thing to understand is that the view that processes the form is usually also the one that shows the form in the first place -- because it has to show the form again in case of errors.
In your template, you build the form HTML entirely by hand. That's unusual, because in case of errors (usually a required field that wasn't filled in) you want to render the form again, with all the values already entered present, and with a nice error message. Django's form rendering ( {{ form.as_p }} and the like) do that for you, you don't get it if you write the HTML by hand like this. In fact your view misses an else: clause on the is_valid(), leading it to
So usually the view does both, except for the second thing to understand: after a successful POST, you always redirect to a succes page, or possibly to the same page (which will show an empty form again). Mostly so the user can't accidentally re-submit the form with the refresh button then.
So your 1st is typical, except that you also need to finish with returning a ResponseRedirect in the is_valid() case, and should render more of the form in your template. No need for a second view.

Don't include blank fields in GET request emitted by Django form

On my Django-powered site, I have a search page with several optional fields. The search page is a Django form, and my view function is the typical:
def search(request):
form = SearchForm(request.GET or None)
if form.is_valid():
return form.display_results(request)
return render(request, 'search.html', {'form': form})
Form.display_results() uses the fields that are provided to query the DB and render a response. My search.html includes:
<form action="/search/" method="get">{% csrf_token %}
<!-- Render the form fields -->
<input type="submit" value="Search" />
<input type="reset" value="Reset form" />
</form>
Since most searches will have several blank fields, I'd like not to include them in the GET request emitted by the submit button on search.html. Current searches look something like:
http://mysite/search/?csrfmiddlewaretoken=blah&optional_field1=&optional_field2=&optional_field3=oohIWantThisOne
And I'd like them to look like:
http://mysite/search/?csrfmiddlewaretoken=blah&optional_field3=oohIWantThisOne
Of course, I have a several more fields. This would be nice to have because it would make search URLs more easily human-parsable and sharable.
You could use jQuery with an button trigger. Give the form and submit button ids.
$("#button_id").click(function(){
$("input").each(function(){
if($(this).val() == '') {
$(this).remove();
}
});
$("#form_id").submit();
});
That (or something similar) should remove all the empty fields before the submit.
You could also POST the form. Then build the search url and redirect with empty values removed.
See Hide empty fields from GET form by Bill Erickson:
jQuery(document).ready(function($){
// Remove empty fields from GET forms
// Author: Bill Erickson
// URL: http://www.billerickson.net/code/hide-empty-fields-get-form/
// Change 'form' to class or ID of your specific form
$("form").submit(function() {
$(this).find(":input").filter(function(){ return !this.value; }).attr("disabled", "disabled");
return true; // ensure form still submits
});
// Un-disable form fields when page loads, in case they click back after submission
$("form").find(":input").prop("disabled", false);
}
disclaimer: This is a very old question, and the only one I could find that matches the problem I ran into. It's entirely possible that my solution didn't exist at the time, and an even better way has since been added.
I'm using Django 3.2. I didn't want to use js/jQuery, nor did I want to use a POST form, so here's what I came up with. In a nutshell, it just checks the GET data to see if there are any default values present, and simply redirects to a URL that doesn't have them.
In your view:
from django.shortcuts import redirect
def myView(request):
if not is_clean_form(request.GET):
return redirect(whatever_url_path + clean_url_parameters(request.GET))
else:
# whatever your view does normally
Helper functions (consider making these static methods of your Form class, to keep all that type of stuff together):
from urllib.parse import urlencode
# fields and their default value (eg. empty string)
default_form_values = [
("some_field_name", ""),
# ...
]
def is_clean_form(form_dict):
for field, default_value in default_form_values:
if field in form_dict and form_dict[field] == default_value:
return False
return True
def clean_url_parameters(form_dict):
return "/?" + urlencode([
(field, form_dict[field]) for (field, default_value) in default_form_values
if field in form_dict and form_dict[field] != default_value
])

Django: Redirect to current article after comment post

I am trying to use comments application in my project.
I tried to use code ({% render_comment_form for event %}), shown in the documentation here:
Django comments
And the question is how to make the form redirect to the same page, after the submission.
Also the big question is:
Currently if we have any error found in the for, then we're redirected to preview template.
Is that possible to avoid this behaviour and display errors over the same form (on the same page)?
I will show you how I resolved it in my blog, so you could do something similar. My comments are for Entry model in entries application.
First add new method for your Entry (like) object.
def get_absolute_url(self):
return "/%i/%i/%i/entry/%i/%s/" % (self.date.year, self.date.month, self.date.day, self.id, self.slug)
It generates url for entry objects. URL example: /2009/12/12/entry/1/lorem-ipsum/
To urls.py add 1 line:
(r'^comments/posted/$', 'smenteks_blog.entries.views.comment_posted'),
So now you should have at least 2 lines for comments in your urls.py file.
(r'^comments/posted/$', 'smenteks_blog.entries.views.comment_posted'),
(r'^comments/', include('django.contrib.comments.urls')),
For entries (like) application in views.py file add function:
from django.contrib.comments import Comment #A
...
def comment_posted(request):
if request.GET['c']:
comment_id = request.GET['c'] #B
comment = Comment.objects.get( pk=comment_id )
entry = Entry.objects.get(id=comment.object_pk) #C
if entry:
return HttpResponseRedirect( entry.get_absolute_url() ) #D
return HttpResponseRedirect( "/" )
A) Import on top of file to have
access for comment object,
B) Get
comment_id form REQUEST,
C) Fetch
entry object,
D) Use
get_absolute_url method to make
proper redirect.
Now:
Post button in comment form on entry site redirects user on the same (entry) site.
Post button on preview site redirects user on the proper (entry) site.
Preview button in comment form on entry site and on preview site redirects user on preview site
Thankyou page is not more in use (That page was quite annoying in my opinion).
Next thing good to do is to override preview.html template:
Go to django framework dir, under linux it could by /usr/share/pyshared/.
Get original preview.html template from DJANGO_DIR/contrib/comments/templates/comments/preview.html
Copy it to templates direcotry in your project PROJECT_DIR/templates/comments/entries_preview.html
From now on, it shoud override default template, You can change extends in this way: {% extends "your_pagelayout.html" %} to have your layout and all css files working.
Take a look at "Django-1.4/django/contrib/comments/templates/comments/" folder and you will see in the "form.html" file, there is the line
{% if next %}<div><input type="hidden" name="next" value="{{ next }}" /></div>{% endif %}
Therefore, in the Article-Detail view, you can include the "next" attribute in the context data, and then the comment framework will do the rest
class ArticleDetailView(DetailView):
model = Article
context_object_name = 'article'
def get_context_data(self, **kwargs):
context = super(ArticleDetailView, self).get_context_data(**kwargs)
context['next'] = reverse('blogs.views.article_detail_view',
kwargs={'pk':self.kwargs['pk'], 'slug': self.kwargs['slug']})
return context
Simplify Django’s Free Comments Redirection
Update: Now have the option to redirect as part of the comment form: see https://django-contrib-comments.readthedocs.io/en/latest/quickstart.html#redirecting-after-the-comment-post
This is a really simple redirect to implement. It redirects you back to the page where the comment was made.
When a comment is posted, the url comments/posted/ calls the view comment_posted which then redirects back to the referer page.
Be sure to replace [app_name] with your application name.
views.py
from urlparse import urlsplit
def comment_posted( request ):
referer = request.META.get('HTTP_REFERER', None)
if referer is None:
pass
try:
redirect_to = urlsplit(referer, 'http', False)[2]
except IndexError:
pass
return HttpResponseRedirect(redirect_to)
urls.py
( r'^comments/posted/$', '[app_name].views.comment_posted' ),