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.
Related
Can I make my page return with the information previously entered when the form is invalid?
Views.py
def MyView(request):
[...code...]
if request.method == 'POST':
form = MyForm(request.POST or None)
if form.is_valid()
//do something and redirect to success page
else:
//back to the page with the information filled in by the user (HERE IS MY PROBLEM)
the line form = MyForm(request.POST or None) creates a MyForm object called form with request data assigned to it - its kinda like filled out form. The is_valid() method checks for errors in your form and adds particular errors to your form so this is now a filled out form with errors assigned. If you want to return this form to user you should add it to context so considering its a standard django function based view it should look like this:
def MyView(request):
[...code...]
if request.method == 'POST':
form = MyForm(request.POST or None)
if form.is_valid()
form.save()
return render(request, 'succes_page_template.html')
else:
return render(request, 'current_template.html', context = {'form': form})
if the form is invalid the next thing the user sees is same page where he filled out the form ('current_template.html') but with all the fields filled with data he/she already put it also form will have erros assigned to particular fields so you can print them. If you are new to Django I suggest getting into class based views from the start - they do the heavy lifting for you and refactoring + debugging becomes much easier. here is the link cheers!
I'm trying to learn Django and have come up with a situation I can't figure out. I have the following code:
def contact_add(request):
if request.method == 'POST':
form = ContactManageForm(request.POST)
if form.is_valid():
if form.has_changed(): # <-- ALWAYS RETURNS TRUE!
form.clean()
...
elif 'id' in request.GET: # Request to show an existing contact
new_contact_dynamic = contacts.models.PersonDynamic.objects.get(person_static = request.GET['id'],
current_record_fg = True)
form = ContactManageForm(new_contact_dynamic.__dict__, initial=new_contact_dynamic.__dict__)
else: # This must be to add a new contact
form = ContactAddForm()
return render(request, 'contact_manage.html', {'form': form})
So, if I'm sent an ID number, I read a record and display it on the screen. My template gives the user a 'submit changes' button. My problem, as noted above, is that Django always shows that the form has changed, even if the user hasn't changed any data on the screen (i.e. he just hit the submit changes button without changing anything).
So, am I doing something obviously wrong in my code that's creating this situation? Am I misinterpreting how the form.has_changed() method works?
It's my assumption that when I use the initial=parameter after a GET request, Django is storing that data somewhere and knows the context when the user then hits the 'submit data' button, is this wrong?
Yes you need to initialize your Form with initial data.
In your view the GET and POST requests have no common context. You may want to use sessions for that.
But in this case, it is not necessary. You can retrieve the instance on each request:
def contact_add(request):
if 'id' in request.GET:
new_contact_dynamic = contacts.models.PersonDynamic.objects.get(
person_static = request.GET['id'],
current_record_fg = True
)
if request.method == 'POST':
form = ContactManageForm(request.POST, initial=new_contact_dynamic.__dict__)
...
else: # Show an existing contact
form = ContactManageForm(initial=new_contact_dynamic.__dict__)
else:
form = ContactAddForm()
return render(request, 'contact_manage.html', {'form': form})
I have a URL like this under new_password.html in Django:
http://127.0.0.1:8000/new_password/zBfPGsMBWbAPVXeH6FMMrpDAODd4Cbukc6ca0ddb55be603a46c977f6d08feb9e763ef583c9f1f9d7d0ba88bb32b6368bebc9c96075185beb/hello.technerves#gmail.com
When the user presses the button Submit in the form I want this URL to go to update view in Django. Is it possible?
url(r'^new_password', 'fileupload.views.new_password'),
url(r'^update_password', 'fileupload.views.update_password'),
def new_password(request):
return render_to_response('new_password.html',
context_instance=RequestContext(request))
def update_password(request):
...This is where I want to get the URL parameter.
One option is to simply split the path and retrieve the last item in the list. If you're submitting the value in a POST, say with a hidden field called "url", you could do:
def my_view(request):
if request.method == 'POST':
email = request.POST.get('url').split('/')[-1]
...
That assumes that the entire value of the url in your original question is the value of the "url" form field.
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
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 %}