What's the best way to handle form POST data in my Pylons app? I've tried:
Having a seperate GET method and a POST method with a rest.restrict('post') decorator. Problem -- if there were validation errors then you can't redisplay the form with the data which the user entered because you have to redirect back to the GET method OR you have to render the template directly from the POST method. Unfortunately this looks weird, as the URL has to change to correspond to the POST action.
Having it all in one method, and detecting if the form has been posted via a check on request.method. This works okay, but it seems clumsy to have if request.method == 'post': ... else: ...
Having it all in one method, and detecting if the form has been posted via a check on request.method. This works okay, but it seems clumsy to have if request.method == 'post': ... else: ...
I am not sure why you describe this as clumsy. Switching on request method is a valid idiom in the web app world across languages. For e.g. you'll find Django views having a single view that handles requests differently based on request.method. Similarly in Java, Servlets have doPost() and doGet() methods to provide different behavior for GET and POST requests.
Update
I'd just rather have them separated into different methods, if possible. Many other web frameworks do this
Nothing wrong with this approach either. I was merely pointing out that having the same method handle them is equally valid.
Related
I am working on my first django project which is also my first backend project. In the tutorials/reading I have completed, I haven't come across passing information back to django without a modelform.
My intention is to calculate a value on a page using javascript and pass it to django when a user hits a submit button on that page. The submit button will also be a link to another page. I know I could process the information in a view via the url if I knew how to pass the information back to django.
I'm aware that django uses MVC and as I have my models and views in place, I am lead to believe that this has something to do with controllers.
Basically, I would like to know how to pass information from a page to django as a user follows a link to another page. I understand that this isn't the place for long step by step tutorials on specific topics but I would appreciate any links to resources on this subject. I don't know what this process is even called so I can't search documentation for it.
EDIT:
From further reading, I think that I want to be using the submit button to GET or POST the value. In this particular case, POST is probably better. Could someone confirm that this is true?
Yes, generally POST is a better way of submitting data than GET. There is a bit of a confusion about terminology in Django. While Django is, indeed MVC, models are models, but views are in fact controllers and views are templates. Since you are going to use AJAX to submit and retrieve the data, you don't care about templates. So what you most likely want is something like this
in your urls.py as part of your urlpatterns variable
url(r'mything/$', MyView.as_view())
in your views.py
from django.views import View
from django.http import HttpResponse
class MyView(View):
def post(self, request):
data = request.POST
... do your thing ...
return HttpResponse(results)
and in your javascript
jQuery.post('/mything/', data, function() { whatever you do here })
There're many ways, you can achieve this in django. Following are the two ways, that I generally prefer :-
1) As a query string parameter in the URL
eg. http://localhost/getPatientInfo?patientId=23&name=Sachin
2) Making URL dynamic, to include the information in the view itself.
eg. http://localhost/patientInfo/23/Sachin
In case 1:-
You will have to do,
patientId = request.GET["patientId"]
name = request.GET["patientName"]
In case 2:
Your URL conf will be something like :
urls = [
url("^patientInfo/(\d+)/([^/]+)$", yourViewFunc)
]
And in your view func :-
def yourViewFunc(request, patientId, patientName):
# your logic goes here
pass
For info. related to URLConf, refer to https://docs.djangoproject.com/en/1.10/topics/http/urls/#example
I have a form that needs to process some data in my django app, before the form data is passed to another app.
Is this possible? If not, how might I handle this situation?
Here's a more detailed example of what I need to do. Any suggestions on HOW to accomplish this are welcome!
I have a comments app that tracks comments and replies on arbitrary objects, provides notifications, nested commenting, etc. I'm trying to keep this app generic.
I have another app called "submissions" that uses the comments app. A student enters a submission (via a CommentForm from the comments app) and the results are sent to the comments app which handles it.
In this case, I have two submit buttons on the form: Approve & Return
Regardless of which button is hit the comment app will handle the data the same. However, my submissions app needs to do stuff based on which button was pressed.
The first way I thought of doing this was to pass the form to a submissions app view first, then redirect to the comments app, but I don't think the form data will come with it.
The second way was to only pass it to a submissions app view, then call a function in the comments app, e.g. Comments.objects.create_comment(...) instead of a view to handle the data, but I'm not sure how to pass the form data.
This remembers me very much of this question: django - how to implement a 2-step publish mechanism
You can use the session to save your data after you're done in step one (the submissions app, if I get you correctly), and then redirect the user to step two where they have to submit again (or just redirect again - that'd be a chain, so a bit of a minus there, but works).
Another possible way would be to hack/read a bit into the guts of the comments app to use its interna in the submissions app, so you combine both functionality into one view/function. But I can't help you with that without knowing the code (and it's your project after all, so it's your task).
We have a large Django application made up of a large number of different views (some of which contain forms). As with most large applications, we use a base layout template that contains the common layout elements across the applications (mainly a header and a footer), which the templates for all of our views extend.
What we are looking to do is create a universal search box in our application, accessible on every page, which allows users to perform searches across the entire application, and want to place the search box inside the header, which involves placing a form inside our base layout template. This means that every view in our application will need to be able to handle the submission of this search form. Once this search form is submitted, we will need to redirect the user to another view containing the search results.
However, we are struggling to come up with a pattern to handle this. Does anyone know of functionality built into Django that will help us to build this? Failing that, can anyone suggest a good strategy for modifying our application so that we can handle this use-case without having to modify a large number of existing views (which we don't have the resources to do at the moment)?
Please note that the focus of this question is intended to be the best way to handle the submission of a form which appears in every view, and not strategies for implementing a universal search algorithm (which we have already figured out).
Ideas Explored So Far
Our first idea was to create a base View class that implements handling the universal search form submission, and have each of our views extend this. However, this is not possible because we already have views that inherit from a number of different Django view classes (TemplateView, ListView, FormView and DeleteView being some examples), and to be able to build our own common view class would mean either writing our own version of these Django view classes to inherit from our own view base class, or re-writing a large number of our views so they don't use the Django view classes.
Our next idea was to implement a mixin that would handle the universal search form submission, in an attempt to add this functionality to all our views in a way that allows us to continue using the different Django view classes. However, this brought to light two new problems: (a) how could we do this without modifying each of our views to become a form view, and (b) how can we do this in a way that allows the form handling logic to play nicely when mixed in to existing FormViews?
This seems like such an obvious question that maybe I'm overlooking something. But as others have said your universal search form should not make a POST request to the view that rendered the current page.
Each html form has an action attribute. The attribute of your search form should point towards an URL. Probably something like /search. That url would have a view behind it that handled the POST request from the form and returned the search results. Django has URL template tags to make this easy. {% url 'myapp.views.search' %} will give you the correct url for the search view function if it lived inside the views module in myapp. So the relevant bit of html in your base template would be something like:
<form action="{% url 'myapp.views.search' %}">
<input type="text" name="qs" placeholder="Search">
</form>
If you are planning on displaying the search results on a new page there is absolutely no need to return JSON or anything like that. Just have a search view that looks like this
def search(request):
query = request.POST.get('qs', '')
results = SomeModel.objects.filter(name=query) # Your search algo goes here
return render(request, 'search_results.html', dict(results=results))
Instead of handling the form submission on every view of the application, you can implement a separate view (endpoint), which handles all the search queries. (an endpoint which returns JSON result) since you dont want to add overhead of rendering the whole page with that view. So the search query (which client side AJAX performs to the webserver) will return JSON response, and the Javascript can render that response. This way you can keep the search view isolated from the rest of the views. (Django REST will be helpful in this case)
And this search form will be included in your base template, so your search box is accessible from the entire application, and it submits to the same view. And the AJAX function will handle the server response for rendering it.
It seems like you just need to create another SearchView which takes the query and displays the results. I am not sure if the results have to be displayed differently depending on which page the search has been performed from but it does not seem like.
The form would not have anything to do with the other views. You could just hard code it in the base template.
It's clear how to create a URLPattern which dispatches from a URL regex:
(r'^books/$', books),
where books can further dispatch on request method:
def books(request):
if request.method == 'POST':
...
else:
...
I'd like to know if there is an idiomatic way to include the request method inside the URLPattern, keeping all dispatch/route information in a single location, such as:
(r'^books/$', GET, retrieve-book),
(r'^books/$', POST, update-books),
(r'^books/$', PUT, create-books),
The reason it's done as a single view method is that you're usually rendering some kind of page content as context for the form you're about to submit.
Anyway, my reason for replying it this: from your sample URLConf there it looks like you're building a REST webservice with Django -- if this is the case, you might really benefit from using the rather good django-piston to automatically create your resources/collections. It uses class-based handlers that automatically redirect to the appropriate method (get-books, update-books, create-books in your case) based on the HTTP method in the request
UPDATE (four years later!) while django-piston still exists (and works), Django REST Framework is a much more sophisticated, documented and extended choice these days.
Standard Django doesn't have any mechanism for differentiating request methods besides what you used in your second snippet:
if request.method == 'POST':
...
However, there are third-party apps and snippets that attempt to make method handling a little cleaner using class based views. See, for example, this snippet (found from this SO question about class views).
Personally I'm not so sure this is a good idea. The standard Django method is so... standard... that I think this introduces extra confusion and complexity where it really isn't needed.
I am trying to find a way to prevent users from double-submitting my forms. I have javascript that disables the submit button, but there is still an occasional user who finds a way to double-submit.
I have a vision of a re-usable library that I could create to protect from this.
In my ideal library, the code block would look something like this:
try:
with acquire_lock({'field1':'abc', 'field2':'def'}) as lock:
response = #do some credit card processing
lock.response = response
except SubmissionWasDuplicate, e:
response = e.response
The lock table would look something like this:
duplicate_submission_locks
submission_hash # a MD5 of the submitted arguments
response # pickled data
created_at # used for sweeping this table
lock_expired # boolean signifying if the lock has expired
Does anyone know if this already exists? It doesn't seem to difficult to write, so if it doesn't exist I may write it myself.
You can use a session to store the hash
import hashlib
def contact(request):
if request.method == 'POST':
form = MyForm(request.POST)
#join all the fields in one string
hashstring=hashlib.sha1(fieldsstring)
if request.session.get('sesionform')!=hashstring:
if form.is_valid() :
request.session['sesionform'] = hashstring
#do some stuff...
return HttpResponseRedirect('/thanks/') # Redirect after POST
else
raise SubmissionWasDuplicate("duplicate")
else:
form = MyForm()
With this approach (not deleting the session cookie) the user can't re-store the data util the session expires, by the way, i'm assuming that exist something who identify the user who send the data
One easy solution to this problem is to add a unique hash to each form. Then you can have a rolling table of current forms. When a form is submitted, or the hash gets too old, you can expire it out of your table, and reject any form which does not have a matching hash in your table.
The HTTPRedirect is the correct way to do it, as previously mentioned.
Unfortunately, even Django's own built in admin is prone to problems related to this issue. In some cases, the cross-site scripting framework can assist to prevent some of this, but I'm afraid the current production versions just don't have this built in.
To be honest, your best bet (easy and good practice) is to issue a HTTPRedirect() to the thank you page, and if the thank you page is the same one as the form, that's OK. You can still do this.
Kristian Damian's answer is really a great suggestion. I just thought of a slight variation on that theme, but it might have more overhead.
You could try implementing something that is used in django-piston for BaseHandler objects, which is a method called exists() that checks to see if what you are submitting is already in the database.
From handler.py (BaseHandler):
def exists(self, **kwargs):
if not self.has_model():
raise NotImplementedError
try:
self.model.objects.get(**kwargs)
return True
except self.model.DoesNotExist:
return False
So let's say make that a function called request_exists(), instead of a method:
if form.is_valid()
if request_exists(request):
# gracefully reject dupe submission
else:
# do stuff to save the request
...
# and ALWAYS redirect after a POST!!
return HttpResponseRedirect('/thanks/')
It is always good to use the redirect-after-post method. This prevents user from accidently resubmitting the form using refresh function from the browser. It is also helpful even when you use the hash method. It's because without redirect after a POST, in case of hitting Back/Refresh button, user will see a question message about resubmitting the form, which can confuse her.
If you do a GET redirect after every POST, then hitting Back/Refresh won't display this wierd (for usual user) message. So for full protection use Hash+redirect-after-post.