Django: use POST or links, which is better practice? - django

A noobish question to be sure.
<a href="{% url 'stuff.views.SomeView' %}/somethingnew">
<button>See something new on this page</button>
</a>
<form action="" method="post">{% csrf_token %}
<button name="somethingnew" type="submit" value=True>See something new on this page</button>
</form>
With either choice, I update some boolean variable, perform the appropriate calculations, call the page view and render a page with something new on this page. Part of the reason I use either method is to save the state of a collection of boolean variables. What is the best way 1) change a boolean variable 2) save its state 3) perform the necessary updates when the button is clicked and finally 4) render page after the underlying data has been updated?
Right now, I am using forms rather than links so that I don't need to code a url for each boolean variable. Which method is better? Will one method improve the time it takes to reload the page (assuming many boolean variables)?

1) Following the REST mindset, a POST request is in order to transmit the user input, since you are altering database objects.
2) I'd save it in the Session object if the input is not needed forever (session duration). Otherwise in the database as you are doing now.
3/4) I'd gather all the necessary info in a form. When the user commits the form in a POST request, I'd compute the data and respond with the rendered page containing the computed result. If the input variables are gathered step by step with intermittent computation, I'd just update the input form accordingly (display different choices in a combo box or something like that). Of course the transmitting could be done in an AJAXy way, too.

Related

CFWheels: Display form errors on redirectto instead of renderpage

I have a form which I am validating using CFWheels model validation and form helpers.
My code for index() Action/View in controller:
public function index()
{
title = "Home";
forms = model("forms");
allforms = model("forms").findAll(order="id ASC");
}
#startFormTag(controller="form", action="init_form")#
<select class="form-control">
<option value="">Please select Form</option>
<cfloop query="allforms">
<option value="#allforms.id#">#allforms.name#</option>
</cfloop>
</select>
<input type="text" name="forms[name]" value="#forms.name#">
#errorMessageOn(objectName="forms", property="name")#
<button type="submit">Submit</button>
#endFormTag()#
This form is submitted to init_form() action and the code is :
public function init_form()
{
title = "Home";
forms = get_forms(params.forms);
if(isPost())
{
if(forms.hasErrors())
{
// don't want to retype allforms here ! but index page needs it
allforms = model(tables.forms).findAll(order="id ASC");
renderPage(action="index");
//redirectTo(action="index");
}
}
}
As you can see from the above code I am validating the value of form field and if any errors it is send to the original index page. My problem is that since I am rendering page, I also have to retype the other variables that page need such as "allforms" in this case for the drop down.
Is there a way not to type such variables? And if instead of renderPage() I use redirectTo(), then the errors don't show? Why is that?
Just to be clear, I want to send/redirect the page to original form and display error messages but I don't want to type other variables that are required to render that page? Is there are way.
Please let me know if you need more clarification.
This may seem a little off topic, but my guess is that this is an issue with the form being rendered using one controller (new) and processed using another (create) or in the case of updating, render using edit handle form using update.
I would argue, IMHO, etc... that the way that cfWheels routes are done leaves some room for improvement. You see in many of the various framework's routing components you can designate a different controller function for POST than your would use for GET. With cfWheels, all calls are handled based on the url, so a GET and a POST would be handled by the same controller if you use the same url (like when a form action is left blank).
This is the interaction as cfwheels does it:
While it is possible to change the way it does it, the documentation and tutorials you'll find seem to prefer this way of doing it.
TL; DR;
The workaround that is available, is to have the form be render (GET:new,edit) and processing (POST:create,update) handled by the same controller function (route). Within the function...
check if the user submitted using POST
if it is POST, run a private function (i.e. handle_create()) that handles the form
within the handle_create() function you can set up all your error checking and create the errors
if the function has no errors, create (or update) the model and optionally redirect to a success page
otherwise return an object/array of errors
make the result error object/array available to view
handle the form creation
In the view, if the errors are present, show them in the form or up top somewhere. Make sure that the form action either points to self or is empty. Giving the submit button a name and value can also help in determining whether a form was submitted.
This "pattern" works pretty well without sessions.
Otherwise you can use the Flash, as that is what it was created for, but you do need to have Sessions working. their use is described here: http://docs.cfwheels.org/docs/using-the-flash and here:http://docs.cfwheels.org/v1.4/docs/flashmessages
but it really is as easy as adding this to your controller
flashInsert(error="This is an error message.");
and this to your view
<cfif flashKeyExists("error")>
<p class="errorMessage">
#flash("error")#
</p>
</cfif>

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)

Django redirect page does not update the view

I'm using the Django Framework on Google App Engine.
I have multiple forms on the same view, to submit to different URL.
Trouble is after I get a form submitted: even if the called method update the datastore and some data, the previous page (where the forms are put in) is not refreshed, showing the updated data.
I could solve this problem using jQuery or some javascrip framework, appending dinamically content returned by the server but, how to avoid it?
Suggestions?
Am I wrong somewhere?
A part of "secure.html" template
<form action="/addMatch" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
Matches:
<br />
{% for m in matches%}
{{m.description}} ---> {{m.reward}}
{% endfor%}
the "/addMatch" URL view:
def addMatch(request):
form = MatchForm(request.POST)
if form.is_valid():
user = User.all().filter('facebookId =', int(request.session["pbusr"]))
m = Match(user=user.get(),description =form.cleaned_data["description"],reward=form.cleaned_data["reward"])
m.save()
return HttpResponseRedirect("/secure/")
else:
logging.info("Not valid")
return HttpResponseRedirect("/secure")
The view method whose seems not working:
#auth_check_is_admin
def secure(request):
model={}
user = User.all().filter('facebookId =', int(request.session["pbusr"]))
u = user.get()
if (u.facebookFanPageId is not None and not u.facebookFanPageId == ""):
model["fanPageName"] = u.facebookFanPageName
model["form"] = MatchForm()
model["matches"] = u.matches
else:
....
return render(request,"secure.html",model)
Francesco
Based on what you posted, it seems like you're redirecting properly and are having database consistency issues. One way to test this would be to look at the network tab in the Google Chrome developer tools:
Click on the menu icon in the upper right
Click on "Tools"
Click on "Developer Tools"
Click on "Network" in the thing that opened up at the bottom of the screen.
Now, there will be a new entry in the network tab for every request that your browser sends and every response it receives. If you click on a request, you can see the data that was sent and received. If you need to see requests across different pages, you might want to check the "Preserve log" box.
With the network tab open, go to your page and submit the form. By looking at the network tab, you should be able to tell whether or not your browser issued a new GET request to the same URL. If there is a new request for the same page but that request has the old content, then you have a datastore consistency issue. If there was NOT a new request that yielded a response with the data for the page, then you have a redirect issue.
If it turns out that you have a datastore consistency issue, then what's happening is the data is being stored, but the next request for that data might still get the old data. To make sure that doesn't happen, you need what's called "strong consistency."
In a normal App Engine project, you get strong consistency by putting entities in the same entity-group and using ancestor queries. I'm not certain of what database/datastore you're using for Django and how the different database layers interact with App Engine's consistency, so this could be wrong, but if you can give your users the right key and then fetch them from that key directly (rather than getting all users and filtering them by key), you might get strong consistency.

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: How do I position a page when using Django templates

I have a web page where the user enters some data and then clicks a submit button. I process the data and then use the same Django template to display the original data, the submit button, and the results. When I am using the Django template to display results, I would like the page to be automatically scrolled down to the part of the page where the results begin. This allows the user to scroll back up the page if she wants to change her original data and click submit again. Hopefully, there's some simple way of doing this that I can't see at the moment.
It should already work if you provide a fragment identifier in the action method of the form:
<form method="post" action="/your/url#results">
<!-- ... -->
</form>
and somewhere below the form, where you want to show the results:
<div id="results">
<!-- your results here -->
</div>
This should make the page jump to the <div> with ID results.
It is complete client site and does not involve Django, JavaScript or similar.
You need to wrap your data into something like this:
<div id="some-id">YOUR DATA TO BE DISPLAYED</div>
and if you make redirect in your view you need to redirect to url: /some-url/#some-id
if you don't make redirect you need to scroll to the bottom using javascript (but note that redirect is preffered way to use in view after saving data).