Redirect to views according to form selection - django

User registration in my application is performed in steps. After submitting the form, some validation is performed and then register_prompt view is called. It renders a form, with two options - 'ok' and 'cancel'. Clicking ok will run registration, and clicking cancel should redirect to main page. Problem is that no matter which of the two I choose, I'm redirected to .../user/registration/function_1_or_2_name with a blank page (although I have specified url in HttpResponseRedirect ). How can I make it work ?
def register_prompt(request):
context = RequestContext(request)
return render_to_response('user/data_operations/alert.html', context_instance=context)
Form loaded on alert.html :
<form action="" method="post">
<input type="submit" class="submit" name="submit" onClick="this.form.action='{% url register_new %}'" value="Ok" />
<input type="submit" class="submit" name="submit" onClick="this.form.action='{% url redirect_home %}'" value="Cancel" />
</form>
Redirect views (maybe there is a better way to do that ?):
def redirect_home(request):
return HttpResponseRedirect('/')
def register_new(request):
(... registration magic here ...)
return HttpResponseRedirect('/user/registration/complete/')
Finally url conf :
url(r'^register_new/$', register_new, name="register_new"),
url(r'^redirect_home/$', redirect_home, name="redirect_home"),
url(r'^register_prompt/$', register_prompt, name="register_prompt"),
At first I was trying to add some abstract values to form's buttons (like 'action=ok'), and then catch them in register_prompt but it was a total disaster.

I'm not sure if that onClick="" hack is really valid (I'll let someone else weigh in on that), but did you try just using normal links, to see if it's that or something else?
OK
Cancel
(Or check out <button> if you really want it to look like buttons. Or just use two <form>s, each with a separate action="" attributes.)
Hum, other than that... You say ".../user/registration/function_1_or_2_name" -- what exactly does the {% url .. %}s give you? Does it not add the final / (which you require in your patterns)?
At first I was trying to add some
abstract values to form's buttons
(like 'action=ok'), and then catch
them in register_prompt but it was a
total disaster.
But you seem to want to manipulate the action attribute, so you can't catch anything in register_prompt()?
All in all I'd suggest you either use normal links, or actually handle the POST in the view that presented it:
def register_prompt(req):
if 'ok' in req.POST:
return register_new(req)
if 'cancel' in req.POST:
return register_cancel(req)
return render_to_response('user/data_operations/alert.html',
context_instance=RequestContext(req))
Maybe something like that? Renaming your buttons to ok and cancel, and having the form post back to itself...

Related

django redirect after post with post parameters after update

I was looking for help but couldn't find any suitable solution.
I would like to ask about redirect after POST.
Situation looks as below step by step:
Simple search form with POST with parameter called 'find' within form. Search form appears on every site and if i call search on site with devices i'm checking referrer and i'm doing query for device model fields and results go to devices.html template. Same situation is when I search within localization site, checking for referrer and querying for model fields of localization model and returning query result in localization.html template.
When I try to update device i'm opening new edit device template with url '/edit/device/dev_id' when every field of this model is shown so i can update some fields manually.
After commiting changes on edit site device 'post' goes to url 'update/device/dev_id' and changes to device are saved properly.
The problem is how can I redirect after updating device to step number one where are the results of search view for devices?
If i redirect after update ('update/device/dev_id') device to 'request.META.get('HTTP_REFERER')' i'm getting 'edit/device/dev_id' url ?
Worth to mention is that method POST for search form sends text search input to action="/search" and then after checking referrer shows results, so there is nothing like 'action="search/find".
I know that in web browser we can do few times previous click to show search results incuding POST parameters but it is not the point.
If you have any ideas how to redirect to search results (method POST, 1 point) after updating please let me know.
Regards AD
I will try to simplify the question ( too much code to paste ):
Given search below in template:
<form class="navbar-form navbar-left" action="/search" method="post">{% csrf_token %}
<div class="form-group">
<input type="text" class="form-control" name="find" placeholder="Search">
</div>
<button type="submit" class="btn btn-default" >Search</button>
</form>
in views.py:
def search_dev(request):
to_find=request.POST.get('find')
(..)
<--query updating_fields in instance considering only 1 search result-->
redirect ? (to search query with POST )
Is it possible to go back to search results after some changes to instance ( for example update ) considering only 1 search result ?

data How to keep form when user gets redirected back to the form when they fail a validation (Python, Django)?

I know this might be a duplicate question, but the previous one was an older question and those questions uses a form instance which doesn't really help me.
How do I keep my form data after a failed validation? I have multiple dropdowns and input fields and I hate to see my users re-do and re-type everything when they fail validation in the backend. Let's say I have this very simple form:
HTML:
<form class="" action="/register" method="post">
<label for="">First Name</label>
<input type="text" name="" value="">
<label for="">Last Name</label>
<input type="text" name="" value="">
<label for="">Password</label>
<input type="password" name="" value="">
</form>
views.py:
def register(self):
.....
if errors:
for err in errors
messages.errors(request, err)
return redirect('/')
else:
messages.success(request, "Welcome User!")
return redirect('/dashboard')
Most examples that I came across were using the form instance which uses form.save() etc. I opted out on that one. Is there a way to auto-populate my form with the data that the user submitted if they were to fail validation? Thanks!
Django form classes are the way to go. Form validation and rendering are the tasks they were build for. I would strongly recommend using them, because they also take care of security aspects, when it comes to passing user input back to the browser (all input from user land is evil!).
If you really need to achieve this without form classes, you need to add the form values to your rendering context manually - this allows you to use them in your template.
The main problem with your approach is, that you want to redirect in case of validation error. A redirect is a response to the browser that tells: I have nothing for you, please go to this location. Usually the browser does not post the data send in the first request also to the second one (which is generally a good behavior). You may work around that by answering with status code 307 instead of 302. Read e.g. Response.Redirect with POST instead of Get? for more information. Alternatively you may encode your form data into the target location using get parameters.
Again: You should have a very good reason to not just use the django approach of one view that acts on GET and POST different and handles the form properly using the form instance.

Submit Button Confusion and Request being sent Twice (Using Flask)

I'm pretty much trying to create a web app that takes 2 svn urls and does something with them.
The code for my form is simple, I'm also using WTForms
class SVN_Path(Form):
svn_url=StringField('SVN_Path',[validators.URL()])
I'm trying to create 2 forms with 2 submit buttons that submit the 2 urls individually so my test3.html looks like this:
<form action="" method="post" name="SVNPath1">
{{form1.hidden_tag()}}
<p>
SVN Directory:
{{form1.svn_url(size=50)}}
<input type="submit" value="Update">
<br>
{% for error in form1.svn_url.errors %}
<span style="color: red;">[{{error}}]</span>
{% endfor %}
</p>
</form>
<form action="" method="post" name="SVNPath2">
{{form2.hidden_tag()}}
<p>
SVN Directory:
{{form2.svn_url(size=50)}}
<input type="submit" value="Update">
<br>
{% for error in form2.svn_url.errors %}
<span style="color: red;">[{{error}}]</span>
{% endfor %}
</p>
</form>
MY FIRST QUESTION is how do I know which submit button was clicked so I can run the proper function on the corresponding svn url. I have tried doing something like
if request.form1['submit'] == 'Update':
if request.form2['submit'] == 'Update':
but that does not work at all. I'm new to web dev in general and flask so a detailed explanation would be helpful.
SECONDLY, since submits weren't working properly I also tried an alternative to keep my work moving so in my .py file I have
#app.route('/test3', methods=['GET','POST'])
def test3():
basepath=createDir()
form1=SVN_Path()
form2=SVN_Path()
if request.method=="POST":
if form1.validate_on_submit():
svn_url = form1.svn_url.data
prev_pdf=PDF_List(svn_url,basepath,'prev') #some function
if form2.validate_on_submit():
svn_url2 = form2.svn_url.data
new_pdf=PDF_List(svn_url,basepath,'new') #some function
return render_template('test3.html', form1=form1, form2=form2)
CreateDir is a function that creates a directory in the local /tmp using timestamps of the local time.
Whenever I go the webpage it creates a directory, lets call it dir1, since its calling CreateDir. Thats what I want, but when I click submit on the form it creates another directory dir2 in the tmp folder which is NOT what I want since I want everything to being the same dir1 directory.
In addition when I put a url in one of the forms and click submit, it automatically puts it the same value in the 2nd form as well.
Sorry if this is really long and possibly confusing, but any help is appreciated.
:) Let's see if we can clarify this a little.
To your first question:
As #dim suggested in his comment, You have a few options:
You can submit your form to separate unique urls. That way you know which form was submitted
You can create two similar but different Form classes (the fields will need different names like prev_svn_url and cur_svn_url). This way in your view function, you instantiate two different forms and you'll know which form was submitted based on form.validate_on_submit()
The third option would be to add a name attribute to your submit button and then change the value attributes to something like 'Update Previous' and 'Update Current'. This way in your view function you can check the value of request.data[<submit button name>] to determine if 'Update Previous' was pressed or 'Update Current'.
To your second question:
Multiple directories are being created because you're calling createDir() each time the page is loaded to show the forms and when the forms get posted. In order to create just once, you'll need some kind of logic to determine that the directory was not previously created before calling createDir()
In addition: Since both forms are from the same SVN_Path class, they read post data exactly the same way, that's why whatever you type in form 1 appears in form 2.
Now for my 2 cents:
I assume you're trying to write some kind of application that takes two SVN urls as input, creates a folder and does something with those URLs in that folder. If this is the case, the way you are currently going about it is inefficient and won't work well. You can achieve this with just one form class having 2 svn_url fields (with different names of course) and then handling all of that in one post.
EDIT: The job of the submit button is to tell the browser that you're ready to send the data on the form to the server. In this case you should only need one submit button (SubmitFiled => when rendered). Clicking that one submit button will send data from both input fields to your view function.
Your form should look something like:
class SVN_Path(Form):
prev_svn_url=StringField('Previous SVN_Path',[validators.URL()])
new_svn_url=StringField('New SVN_Path',[validators.URL()])
and your view function:
def test():
form = SVN_Path()
if request.method == "POST":
if form.validate_on_submit():
basepath = createDir() # Only create dir when everything validates
prev_svn_url = form.prev_svn_url.data
new_svn_url = form.new_svn_url.data
prev_pdf = PDF_List(prev_svn_url, basepath, 'prev')
new_pdf = PDF_List(new_svn_url, basepath, 'new')
...
return render_template('test3.html', form1=form1, form2=form2)

Get the clicked submit button in a django form in a generic way

I have a finite state machine (django-fsm) which allows an object to go from a source state into one of several target states. I can add all the actions in a dictionary like:
ACTIONS { 'button_1': action1,
'button_2': action2,
...
}
This translates in a form with a submit button for each state.
{% for n,m in object.get_available_current_state_transitions %}
<input type="submit" class="btn" value="{{ n|get_action|capfirst }}"
name="button_{{n}}" />
{%endfor%}
<input type="submit" class="btn primary" value="Save">
<a class="btn" onclick="javascript:history.go(-1)">Cancel</a>
This usually results in more than 3 buttons.
Clicking a button results in a specific action, defined in my case in the model class.
Now, I know I can get the clicked button in the request.POST dictionary, but this would result in a cascade if like:
if 'button_1' in request.POST:
action_1()
elif 'button_2' in request.POST:
...
Is there any way to get the button pressed separately (ideally from the request object) in a variable so I can have something like
ACTIONS[clicked_button_name](...)
? In other words, is there any way to obtain the clicked button outside the POST dictionary?
PS: I've looked other replies on the "multiple buttons" question, but all offer request.POST as answer.
If all of the actions and strings are already in your view, why don't you just iterate over that actions dict?
for key, value in ACTIONS.items():
if key in request.POST:
value()
Just make your button names very unlikely to be used as a regular form field name.
A few alternatives: use javascript to handle the submission and have it set a single form field such as "action".
Use more unique keys and filter through request.POST.keys() with a regex pattern or string comparison.
action = [x for x in request.POST.keys() if 'FAIRLY_UNIQUE_BUTTON_PREFIX' in x]
if action:
ACTIONS[action]()
I don't think so directly, but a couple workarounds could be:
Send your buttons to different urlconfs with some variable (like a three letter arg). All of these confs point to the same view taking this three letter arg as an argument, which then knows what to do. This might still result in a cascade if else though.
Or, send them to different views all together.
You could try doing something ajaxy. The data will still be in a post dict, but you will have more control over how the post dict is structured.
I'm also assuming GET isn't an option for any of these (yet that still results in if else structures.)

Django -- pass "next" parameter to cancel button?

I know how to pass a next get parameter to a view so that when the view redirects, it goes to whatever url is in next. Is it possible to do this with the cancel button of a form?
If my form buttons are like this:
<input type="submit" value="Save Changes"><input type="button" value="Cancel Changes" onclick="window.location.href='/systems/'">
Is there some way to add the next parameter into the href instead of /systems/?
Thanks!
Well, this is just HTML in your template, so why can't you do
onclick="window.location.href='{{ myurl }}'">
?
Edit after comment
Sounds like you need a context processor, which automatically adds the value of the next GET parameter to the context. As simple as:
def get_next(request):
if 'next' in request.GET:
return {'next': request.GET['next']}
Now add this to the list of CONTEXT_PROCESSORS in settings.py, and make sure you use a RequestContext when rendering your template (or just the new render() shortcut).