django request method dynamically - django

I know that request.method will give the type either 'GET' or 'POST' as string and to get any particular parameter we give request.GET.get('parameter') or request.POST.get('parameter')
Now I want to get the GET or POST dictionary dynamically avoiding If conditions.

This is not something there's usually any good reason to do, if you're doing the same thing with GET and POST requests your design is unusual. But it should be possible using getattr:
query_args = getattr(request, request.method)

Related

How to call a function from views.py to tasks.py?

I'm trying this: but its throwing an TypeError: auto_sms() missing 1 required positional argument: 'request' error.
Now I'm thinking of getting the function from views.py instead and calling it on tasks.py if requests is not working on tasks.py, how can I do it? Thanks!
#shared_task
def auto_sms(request):
responses = Rainfall.objects.filter(
level='Torrential' or 'Intense',
timestamp__gt=now() - timedelta(days=1),
)
count = responses.count()
if not (count % 10) and count > 0:
send_sms(request)
return
Passing the entire request is probably not a good idea since it can include Django model objects such as a user object. Now the problem that you will face is that if there is an object that is not serializable, then you'll get an error while calling the function. So instead of passing the whole request, just send the data that you actually need.
For example, I'm guessing you need the user here to send an SMS to. So instead of passing the whole request with the user object included, then just send the user_id and then get the user there. basically, you have to make sure that the data you're passing is serializable.
It's generally a good idea to pass ids of the Django models since the data might change while your function is being processed and you might get the old data if you pass the whole data.

Choosing correct Django delete view approach

I'm working on Django website and I have problem in figuring out correct/good way to handle delete view. From what I found out there are two ways to approach it:
1
class ObjectDeleteView(DeleteView):
model = Object
def get_success_url(self):
objectid = self.kwargs['object_id']
object = Object.objects.get(id = objectid)
container = object.container
containerid = container.id
url = reverse('Containers:showContainerContent', args=[containerid])
return url
def get_object(self):
return get_object_or_404(Object, pk=self.kwargs['object_id'])
2
def objectDelete(request, object_id):
object = Object.objects.get(id = object_id)
container = object.container
containerid = container.id
url = reverse('Containers:showContainerContent', args=[containerid])
return HttpResponseRedirect(url)
From what I can tell both are doing exactly the same thing - once object is deleted present user with page under Containers:showContainerContent.
The big difference I am experiencing is error I am getting when running unit test for this (simple call of the website and check of response code). With option 1 I end up getting error
django.template.exceptions.TemplateDoesNotExist: ContainerApp/object_confirm_delete.html
Which I understand - I don't have this template, this is default template for DeleteView, hence error is correct. The thing is I don't want to have any extra page. Just redirect user and that's it.
Also, I tested changing return url to return HttpResponseRedirect(url) in option 1, but result is the same.
What should I do here? Should I just continue with option 2? What are or might be the drawbacks for this approach?
There is a major difference between two class based delete view and function based view (the way you declared it).
CBV accepts get, post and delete http methods. When you send a get request to class based view, it does not delete the object. Instead it renders template with object to be deleted in context. This is basically used to have confirmation. For example you can send a get request and it will render a template with text "Do you really want to delete?" or "Please confirm blah blah..". And if you send a post or delete request, it will actually delete the object and redirect to next page.
FBV, on the other hand, give you full control over what you want to do. And as you declared it, it will accept any request type and delete the object and redirect to next page because you have not done any request type check in your view which is not a great idea IMHO. You should not allow deletion on get requests. They should be idempotent. There are plenty of otherthings that CBV provides. For example in case the object does not exist your FBV will crash. CBV, on contrary, will return proper 404 response if object does not exist.
So I think there is no bad in using FBV, but make is strong and secure enough that it handles every case (what if object does not exist?, what about confirmation?, GET should be idempotent only allow deletion with post? etc etc). Or simply use CBV.

Django 1.6 request POST/GET empty

I use a function-based view and try to validate a form which is submitted over Ajax (with jquery.form plugin):
Content-Type:application/json; charset=UTF-8
X-CSRFToken:jRr4oOBHQS5mtwopN69xHocjWJBYuJHa
X-Requested-With:XMLHttpRequest
Request payload:
csrfmiddlewaretoken=jRr4oOBHQS5mtwopN69xHocjWJBYuJHa&code=123456
now, in the view function, I have request.GET/POST empty, but request.body as a string, and I can't validate the form.
form = CodeCheckForm(parse_qs(request.body))
form.is_valid()
In the second line, the clean* functions aren't called, which is really weird.
Changing to use data or initial doesn't help either:
form = CodeCheckForm(data=parse_qs(request.body))
What am I doing wrong?
EDIT: among the answers, the decisive was to change the content type. In jquery.forms plugin I set contentType option to application/x-www-form-urlencoded; charset=UTF-8.
EDIT 2: there are 2 ways to supply arbitrary data around the standard way, but they weren't suitable in my case:
1) form = MyForm(parse_qs(request.body)) is almost correct, but Django forms expect this to be a QueryDict, and have some properties, while this is a usual dict. Form class raises exception and crashes the view.
2) form = MyForm(data=parse_qs(request.body)) works, but does not call clean* functions. This is intentional, as Django developers made this way as a way around clean functions. You're supposed to validate the data yourself, and then submit it this way. Django form then does not clean it in any way and decides the form is not validated, hence form.is_valid() will be False.
The GET and POST only contain form data. They are empty for you because your content type is 'application/json'. It's not clear to me why you've used this content type, since the payload looks like form encoded data to me, not json.
If you manually parse the payload, use the data argument. The initial argument is only used to show initial values. If you don't bind the form to data, then the form is unbound, and will never be valid.
I'm not sure why the following line didn't work. What is the value of parse_qs(request.body)?
form = CodeCheckForm(parse_qs(request.body))

implementing a custom UploadHandler in django

We've got some clients sending a custom POST of a data blob to our django servers.
They do things in a rather funky way that I'd rather not get into - and we've since moved on from making that particular format the norm. To make further implementations of our upload protocol more streamlined, I was looking to roll a custom UploadHandler in django to make our data handling in the views a bit more streamlined.
So, moving forward, we want all code in the views to access our POSTs via the:
data = request.FILES['something']
So, for our new submissions, we're handling that dandily.
What I'd like to be able to do is get the upload handler we've made, affectionately called LegacyUploadHandler(), to populate the request.FILES dictionary with the right parts, so the code in our view can access the parts the same way.
So, my question:
How does a custom uploadhandler actually populate the request.FILES dictionary? The django documentation doesn't really give a descriptive example of doing that.
Our particular desire is that we have a singular blob of data coming in. We custom parse it and want it to appear as the request.FILES dictionary.
The current code as it stands right now does this:
def handle_raw_input(self, input_data, META, content_length, boundary, encoding=None):
files_dict = {}
files_dict = magic_parser(input_data.read())
#now what do I do?
I see examples of setting a files MultiValueDict in the http.MultiPartParser, but that seems to be outside the scope/control of where I am in my handlers.
Any ideas of how to actually do the return value? Or am I trying to populate the request.FILES object the completely wrong way?
From handle_raw_input you have to return a tuple of what will be POST and FILES on the requst. So in your case it's something like:
def handle_raw_input(self, input_data, META, content_length, boundary, encoding=None):
files_dict = magic_parser(input_data.read())
return QueryDict(), files_dict
The magic_parser should return a MultiValueDict of the form {'filename': fileobj}. A fileobj is an instance of some suitable django.core.files.File subclass (or may be that class itself).

Capturing URL parameters in request.GET

I am currently defining regular expressions in order to capture parameters in a URL, as described in the tutorial. How do I access parameters from the URL as part the HttpRequest object?
My HttpRequest.GET currently returns an empty QueryDict object.
I'd like to learn how to do this without a library, so I can get to know Django better.
When a URL is like domain/search/?q=haha, you would use request.GET.get('q', '').
q is the parameter you want, and '' is the default value if q isn't found.
However, if you are instead just configuring your URLconf**, then your captures from the regex are passed to the function as arguments (or named arguments).
Such as:
(r'^user/(?P<username>\w{0,50})/$', views.profile_page,),
Then in your views.py you would have
def profile_page(request, username):
# Rest of the method
To clarify camflan's explanation, let's suppose you have
the rule url(regex=r'^user/(?P<username>\w{1,50})/$', view='views.profile_page')
an incoming request for http://domain/user/thaiyoshi/?message=Hi
The URL dispatcher rule will catch parts of the URL path (here "user/thaiyoshi/") and pass them to the view function along with the request object.
The query string (here message=Hi) is parsed and parameters are stored as a QueryDict in request.GET. No further matching or processing for HTTP GET parameters is done.
This view function would use both parts extracted from the URL path and a query parameter:
def profile_page(request, username=None):
user = User.objects.get(username=username)
message = request.GET.get('message')
As a side note, you'll find the request method (in this case "GET", and for submitted forms usually "POST") in request.method. In some cases, it's useful to check that it matches what you're expecting.
Update: When deciding whether to use the URL path or the query parameters for passing information, the following may help:
use the URL path for uniquely identifying resources, e.g. /blog/post/15/ (not /blog/posts/?id=15)
use query parameters for changing the way the resource is displayed, e.g. /blog/post/15/?show_comments=1 or /blog/posts/2008/?sort_by=date&direction=desc
to make human-friendly URLs, avoid using ID numbers and use e.g. dates, categories, and/or slugs: /blog/post/2008/09/30/django-urls/
Using GET
request.GET["id"]
Using POST
request.POST["id"]
Someone would wonder how to set path in file urls.py, such as
domain/search/?q=CA
so that we could invoke query.
The fact is that it is not necessary to set such a route in file urls.py. You need to set just the route in urls.py:
urlpatterns = [
path('domain/search/', views.CityListView.as_view()),
]
And when you input http://servername:port/domain/search/?q=CA. The query part '?q=CA' will be automatically reserved in the hash table which you can reference though
request.GET.get('q', None).
Here is an example (file views.py)
class CityListView(generics.ListAPIView):
serializer_class = CityNameSerializer
def get_queryset(self):
if self.request.method == 'GET':
queryset = City.objects.all()
state_name = self.request.GET.get('q', None)
if state_name is not None:
queryset = queryset.filter(state__name=state_name)
return queryset
In addition, when you write query string in the URL:
http://servername:port/domain/search/?q=CA
Do not wrap query string in quotes. For example,
http://servername:port/domain/search/?q="CA"
def some_view(request, *args, **kwargs):
if kwargs.get('q', None):
# Do something here ..
For situations where you only have the request object you can use request.parser_context['kwargs']['your_param']
You have two common ways to do that in case your URL looks like that:
https://domain/method/?a=x&b=y
Version 1:
If a specific key is mandatory you can use:
key_a = request.GET['a']
This will return a value of a if the key exists and an exception if not.
Version 2:
If your keys are optional:
request.GET.get('a')
You can try that without any argument and this will not crash.
So you can wrap it with try: except: and return HttpResponseBadRequest() in example.
This is a simple way to make your code less complex, without using special exceptions handling.
I would like to share a tip that may save you some time.
If you plan to use something like this in your urls.py file:
url(r'^(?P<username>\w+)/$', views.profile_page,),
Which basically means www.example.com/<username>. Be sure to place it at the end of your URL entries, because otherwise, it is prone to cause conflicts with the URL entries that follow below, i.e. accessing one of them will give you the nice error: User matching query does not exist.
I've just experienced it myself; hope it helps!
These queries are currently done in two ways. If you want to access the query parameters (GET) you can query the following:
http://myserver:port/resource/?status=1
request.query_params.get('status', None) => 1
If you want to access the parameters passed by POST, you need to access this way:
request.data.get('role', None)
Accessing the dictionary (QueryDict) with 'get()', you can set a default value. In the cases above, if 'status' or 'role' are not informed, the values ​​are None.
If you don't know the name of params and want to work with them all, you can use request.GET.keys() or dict(request.GET) functions
This is not exactly what you asked for, but this snippet is helpful for managing query_strings in templates.
If you only have access to the view object, then you can get the parameters defined in the URL path this way:
view.kwargs.get('url_param')
If you only have access to the request object, use the following:
request.resolver_match.kwargs.get('url_param')
Tested on Django 3.
views.py
from rest_framework.response import Response
def update_product(request, pk):
return Response({"pk":pk})
pk means primary_key.
urls.py
from products.views import update_product
from django.urls import path
urlpatterns = [
...,
path('update/products/<int:pk>', update_product)
]
You might as well check request.META dictionary to access many useful things like
PATH_INFO, QUERY_STRING
# for example
request.META['QUERY_STRING']
# or to avoid any exceptions provide a fallback
request.META.get('QUERY_STRING', False)
you said that it returns empty query dict
I think you need to tune your url to accept required or optional args or kwargs
Django got you all the power you need with regrex like:
url(r'^project_config/(?P<product>\w+)/$', views.foo),
more about this at django-optional-url-parameters
This is another alternate solution that can be implemented:
In the URL configuration:
urlpatterns = [path('runreport/<str:queryparams>', views.get)]
In the views:
list2 = queryparams.split("&")
url parameters may be captured by request.query_params
It seems more recommended to use request.query_params. For example,
When a URL is like domain/search/?q=haha, you would use request.query_params.get('q', None)
https://www.django-rest-framework.org/api-guide/requests/
"request.query_params is a more correctly named synonym for request.GET.
For clarity inside your code, we recommend using request.query_params instead of the Django's standard request.GET. Doing so will help keep your codebase more correct and obvious - any HTTP method type may include query parameters, not just GET requests."