Django - Form element's default value error - django

there are two links in one page[admin page]. both goes to same form page[ Add Post ]. in admin page ; you can click to 'new post' link or you can type your title in a textbox which is connected to same page[Add Post]. in that page ; i have a form which have a checkbox field.
isdraft = forms.BooleanField(required=False,initial=True)
as you can see ; i set the field's default value True. if i click the 'new post' at admin page ; to come that form page ; it works great. checkbox comes to me as True. but if i use other way [ typing title in textbox ] checkbox comes to me as False. i couldn't figure that out.
edit : i think the question is complicated. not clear. if any additional data needed ; i can add it.
edit :
admin.html :
<h1>PostsNew Post</h1> #this works!
<form action="{% url add_post %}" method="POST">{% csrf_token %}
{{ form.title }} <!-- this is textbox -->
{{ form.isdraft.as_hidden }} <!-- when i add this line form works correctly. but i get 'please fill all fields' warning when redirected to add_post page. -->
</form>
edit2 :
add_post view:
#login_required(login_url='/login/')
def add_post(request):
template_name = 'add.html'
owner = request.user
if request.method == "POST":
form = addForm(request.POST)
if form.is_valid():
titleform = form.cleaned_data['title']
bodyform = form.cleaned_data['body']
checkform = form.cleaned_data['isdraft']
n = Post(title=titleform, body=bodyform, isdraft=checkform, owner=owner)
n.save()
messages.add_message(request, messages.SUCCESS,
'New post created successfully!')
return HttpResponseRedirect('/admin/post/add/')
else:
messages.add_message(request, messages.WARNING,
'Please fill in all fields!')
else:
form = addForm()
return render_to_response(template_name, {'form': form, 'owner':owner,},context_instance=RequestContext(request))`
admin view :
#login_required(login_url='/login/')
def admin_view(request):
if request.session.get('loggedin') == "djangoo":
form = newDraft() # textbox in admin page
return render_to_response('admin.html', {'form':form },context_instance=RequestContext(request))
else:
return HttpResponseRedirect('/login/')
addForm :
class addForm(forms.Form):
title = forms.CharField(max_length=100,
widget=forms.TextInput(attrs={'placeholder':'Title here',}))
body = forms.CharField(widget=forms.Textarea(
attrs={'placeholder':'Write post here','rows':'25','cols':'90',}))
isdraft = forms.BooleanField(required=False,initial=True)

initial only works when the form is unbound.
When you click the new post link, you are doing a get request on the page, so the form is unbound and the initial value is used.
When you enter a title and submit, I assume you are doing a post request on the page. Therefore the form is bound, and the initial value will not be used.
I'm afraid I don't understand the question completely, and you haven't show much code, so I can't suggest any work arounds for you. Hope you get it working.
Update following edits to your question
When the data comes from the add_post view, don't create a bound form, because then the data will be validated and you'll get the error messages.
Instead, fetch the title from the post data, and use that to create an initial dictionary to instantiate your addForm with.
You need a way to tell whether the post request came from the admin or add post view. You could do this by adding another hidden field to the addForm.
action = forms.CharField(widget=forms.HiddenInput, initial="addform")
Then change your add_post view to something like:
if request.method == 'POST':
if request.POST.get('action') == 'addform':
form = addForm(initial={'title': request.POST.get('title'), 'isdraft': True})
else:
# your existing code for handling a request post

Related

Bind dynamic choices to ModelForm in Django

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 !

Disable validation when calling form for the first time

I am struggling a bit with my Django forms. When I call my form site, always validation errors appear (this field is required). I'd prefer to see this message after clicking the submit button, if a field is not filled like a javascript function would do. In addition I'm using regex for validation, which is working fine.
I am working with CVBs. Here is some code:
models.py
class Institute(models.Model):
name = models.CharField(max_length=50)
timestamp = models.DateTimeField(auto_now_add=True)
views.py
class InstituteCreate(CreateView):
model = Institute
form_class = InstituteForm
success_url = reverse_lazy('institute_list')
forms.py
class InstituteForm(forms.ModelForm):
name= forms.CharField(error_messages={'required': 'Own Error Text'}, validators=[RegexValidator(regex='^[a-zA-ZäüößÄÜÖ]*$', message='forbidden string', code='string_invalid')])
class Meta:
model = Institute
fields = ['name']
Hope someone has an idea on how to fix it.
edit1:
my template is quite simple
{% block pagetitle %}Institutes{%endblock %}
{% block content %}
<form class="form-horizontal" name="form_group" method="post">
{% csrf_token %}
<div>
{{ form.as_p }}
</div>
<input class="btn btn-primary" type="submit" value="click me" />
</form>
{% endblock %}
and my url config:
urlpatterns = patterns('',
url(r'^institute_create/$', views.InstituteCreate.as_view(), name='institute_create'),
)
I'm new to Django development so i'll try to explain the problem more detailed:
On my website, when i open the link www.exampleurl.com/institute_create my form is shown. Then i see the field where i have to enter the name for the institute. Above this field the text "this field is required" is displayed. But i don't want to see this, until i try to submit an empty form.
When i enter some text which doesnt match and i press submit button the error text field changes its message to forbidden string as expected.
Unless you're using a POST request to your view, form validation won't be triggered. There's likely an error somewhere else in your code, however, there are couple of things about your code that you'll want to address:
Classes in Python should always begin with an upper-case letter and follow the CapWords convention:
class Institute(models.Model):
name = models.CharField(max_length=50)
# just use the built-in `auto_now_add` argument
timestamp = models.DateTimeField(auto_now_add=True)
class InstituteCreate(CreateView):
model = Institute
form_class = InstituteForm
success_url = reverse_lazy('institute_list')
class InstituteForm(forms.ModelForm):
# All Django model/form fields are required by default, so you
# can drop the `required=True` here
name= forms.CharField(validators=[RegexValidator(regex='^[a-zA-ZäüößÄÜÖ]*$',
message='forbidden string', code='string_invalid')])
class Meta:
model = Institute
fields = ['name']
Otherwise, it's impossible to tell the difference between the class definition and an instance of the class, and you're a lot less likely to run into collisions.
Just out of curiosity, are you seeing in-browser HTML5 validation errors versus errors from Django? If you can add your template code to your question it might help.
I know this is a very old question, but I don't see it answered. I am a beginner in django too and I was following the Django tutorial when I faced the same issue.
I resolved it this way:
if 'voteButton' in request.POST:
context = {
'question': question,
'error_message': "You didn't select a choice"
}
return render(request, 'polls/details.html', context)
elif:
# do something else. Display error message
voteButton is the name of the 'submit' button in your form. Hope this helps! Please do let me know if this approach is wrong.
As Brandon mentioned, your form gets validated on a POST request. So ensure that during the first visit of the page, the Form doesn't get bound to a POST request.
For example, don't do this :
def register(request):
form = RegistrationForm(request.POST)
if request.method == 'POST':
if form.is_valid():
# Do something
return render(request, 'register.html', {'form': form})
You should bind the form to a POST request only if the page is accessed via a POST request. This should help:
def register(request):
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
# DO something
else :
form = RegistrationForm()
return render(request, 'register.html', {'form': form})

django redirect still allows post on reload only on mobile Chrome

After user submits I redirect back to the same page with the users submitted data in the form. I can see the data POST and then immediately followed by a GET of the same page in Firefox 36.0.4 desktop, Chrome 41.0.2272 desktop, Opera 10.0 iPhone 5, Chrome 41.0.2272 iPhone 5. However, only the Chrome on mobile causes a "Confirm Resubmit" on refresh and also doesn't load all the js again. Is there a fix for this problem?
Here is my minimal simple view.
def workout(request):
form = WorkoutInfoForm(request.POST or None)
if form.is_valid():
form.save()
post = request.POST.copy()
request.session['_old_post'] = post
return redirect(reverse("workout.views.workout"))
if not request.POST:
old_post = request.session.get('_old_post')
form = WorkoutInfoForm(old_post or None)
return render_to_response("workout/track_workout.html",
locals(),
context_instance=RequestContext(request))
And my form start in my template
<form method='POST' action='' class="form-horizontal" role="form"> {% csrf_token %}
If the form is valid your code works as expected. The described problem occures only in case of invalid form. You can try something like
def workout(request):
form = WorkoutInfoForm(request.POST or None)
if request.POST:
post = request.POST.copy()
request.session['_old_post'] = post
if form.is_valid():
form.save()
return redirect(reverse("workout.views.workout"))
else:
old_post = request.session.get('_old_post')
form = WorkoutInfoForm(old_post or None)
return render_to_response("workout/track_workout.html",
locals(),
context_instance=RequestContext(request))

Rendering Django template to URL with querystring

I have profile edit page with several tabs. Each tab is a separate page, so when I access it, I add "tab" parameter:
/edit/?tab=general
/edit/?tab=contacts
When user finishes to fill a form on one of these tabs (for example, /edit/?tab=general), he submit it. If form is not valid, I need to render it with errors. Browser URL input need to be filled with /edit/?tab=general, but when I render page, URL is /edit/.
Can I change it somehow? Thanks.
Code:
def _profile_edit_general(request):
profile = request.user.get_profile()
if request.method == 'POST':
form = forms.ProfileEditGeneralForm(request.POST, instance=profile)
if form.is_valid():
form.save()
else:
form = forms.ProfileEditGeneralForm(instance=profile)
return render_template(request, 'profiles/edit/general.html', {
'profile_edit_general_form': form
})
#login_required
def profile_edit(request):
if request.method == 'POST':
if 'profile_edit_general_submit' in request.POST:
return _profile_edit_general(request)
else:
tab = request.GET.get('tab')
if tab == 'general':
return _profile_edit_general(request)
else:
return Http404
It sounds like the problem is that your form action doesn't include the query string.
Normally with Django forms and views, you'll be posting to the same URL as you're already on, which means you can do something like this:
<form method="post" action="?tab={{ current_tab }}">
...
</form>
Where current_tab is a context variable indicating which tab is selected. Based on your code at the moment, you'll need to add it to the dict in your render_template call. If you're using JavaScript to switch tabs, you'll need to update the action attribute when the tab switches too.

django form resubmitted upon refresh

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 %}