Tastypie Adder without models - django

I would like to make a Tastypie based API adder. Here is how it works... the user would post to the two numbers they would like added and using Tastypie + Django I would like to include the added number on the return to the user.
I have no interest in putting it into the mySQL database.
class Adder(resource):
class Meta:
authorization = Authorization()
authentication = Authentication()
def hydrate(self,bundle):
_a = bundle.data['first_number']
_b = bundle.data['second_number']
self.create_response(request, return_dict)
return bundle
The documentation for Tastypie really seems to revolve around the models (for obvious reasons).
But I was curious if the create_response can be called from within the hydrate method and if calling the hydrate method is the right way of handling the post data.

I would probably skip the finer-grained things like hydrate, apply_sorting, build_filters, etc.
I'm assuming that without objects behind the api you're using a list-looking url like /api/v1/add_stuff/, and assuming you're accepting POST requests. If these assumptions are wrong you can adjust by changing to post_detail, get_list, etc.
def post_list(self, request, **kwargs):
_a = request.POST.get('first_number', None)
_b = request.POST.get('second_number', None)
if None in (_a, _b):
raise HttpBadRequest()
return self.create_response(request, {'result': _a + _b})
Note that I think this code would work but I haven't tested it. It's meant to provide a starting point.
This section of the Tastypie docs describes the order in which the various methods are called, and toward the bottom of the page there is a full API reference so you can see what parameters things expect and what they are supposed to return.
Edit:
The flow for this situation will look something like this:
In dispatch, the request uri is inspected. Depending on whether a
detail or a list uri was requested (/api/v1/add_stuff/<pk>/ or
/api/v1/add_stuff/), handling is delegated to dispatch_detail or
dispatch_list. This is also where authentication, authorization,
and throttling checks happen.
In dispatch_list, the request method is inspected and the call is
delegated to a method named '%s_list' % request.METHOD.lower().
To answer your comment, these are magical method names. If the
request method is POST, dispatch_list looks for a method named
post_list and an error is thrown if the appropriate handler
is not defined.

Related

validate if instance exists django

I am building a REST Api on DRF. I have a ModelViewSet endpoint from rest_framework.viewsets.
I have a Post and a Comment model. Each comment belongs to a post. So I have defined 2 endpoints, sort of:
1) router.register(r"posts", views.PostView
2) router.register(r"(?P<pk>[^/.]+)/comments", views.CommentView
Both of them inherit from ModelViewSet so I can perform CRUD operations on them.
I have a question regarding the second endpoint. Since we create comments to posts, I am getting a post pk from posts/int:pk/comments. But the problem is when I do a GET request on this endpoint it'll return the list of all comments, but I need those to belong to a post (id in url). When I try make a POST request on this endpoint if the post does not exist it raises DoesNotExist error which is logical.
What I have done so far is:
redefined a get_queryset() to retrieve only objects belonging to a particular post. If the post does not exist it returns an empty list (though I think it should raise 404)
redefined validate() in my serializer to check if the post exists. Returns 404 if it does not
But when I check PUT, DELETE, PATCH methods on posts/int:pk/comments/int:pk it won't take the post instance into consideration either i.e. it'll allow these operation even if the post does not exist which wrong I believe.
Is there a method in ModelViewSet class that gets triggered on every request and checks if the post instance exists? So that I don't have to redefine every CRUD method just to check it.
EDIT
CommentView
class CommentView(ModelViewSet):
queryset = Comment.objects.all()
serializer_class = CommentSerializer
lookup_url_kwarg = 'pk2'
def get_queryset(self):
queryset = Comment.objects.filter(post=self.kwargs.get('pk'))
return queryset
def perform_create(self, serializer, **kwargs):
post = get_object_or_404(Post, pk=self.kwargs.get('pk'))
serializer.save(author=self.request.user, post=post)
Maybe take a look at drf-nested-routers package.
If it doesn't solve your problem, maybe what you want is standard router registration:
router.register(r"posts", views.PostView)
router.register(r"comments", views.CommentView)
and then use a package like django-filter to filter via query params when listing the comments: /comments/?post=<id>. When creating a comment just send the post id in the body of the request (don't forget to include the 'post' field in CommentSerializer)

Django is there a benefit of checking every view with `method=='POST':`

Is there a benefit to starting every one of my view functions with if request.method=='POST': or if request.method=='GET':? Or would I just be adding unnecessary lines of code?
I've followed a few examples where views for Ajax are all checking if the HTTP is made with GET.
Could it, for example, prevent DDOS from latching on to a POST method and hammering it with GETs? Or, more practically, prevent API consumers from incorrectly PATCHing when they should PUT or POST?
def employee_delete(request, uid):
if request.method == 'DELETE':
def employee_detail(request, uid):
if request.method == 'GET':
def employee_create(request):
if request.method == 'POST':
def employee_update(request, uid):
if request.method == 'PUT':
Is there a benefit to starting every one of my view functions with if request.method=='POST':
Yes, even if you only support one method, it is better to guard this. The HTTP protocol specifies that GET requests, should not have side-effects (well effects in the sense of counting visitors are probably no problem, but not something that changes the "entities" of your business logic is strictly speaking not acceptable).
Now "web crawlers" (for example used by search engines or scrapers) typically detect links on a page, and make GET requests on these links (since they aim to "discover" new pages). If there is a view behind this URL that, for example, deletes an employee, it can happen that accidentally a "web crawler" will edit your database.
Other methods like GET, HEAD, PUT and DELETE should be idempotent (that means that making the same request twice, should have the same side-effects, as making the request only once).
So by not "protecting" your views, you lose a layer of "protection" against accidental misuse of your webserver.
A hacker could also aim to make a request with another method, and see how the server responds in a search to find exploits: for example, look if a server makes certain assumptions on the method that fail when performing a DELETE request. For example, a rather generic view implementation that handles all methods, could - if not guarded - unintentionally allow the deletion of files (for example you write a generic view to create and edit content can be "misused" by a hacker by using a DELETE request that the parent view implemented, but should not be supported for that specific entity).
In the early days, some HTTP webservers for example did not check authentication when a HEAD request was used. As a result, a hacker could, by trying several HEAD requests "scan" the id space, and thus obtain knowledge what id's were filled in in the database. Of course that in itself does not leak much data, but it is a vulnerability that can be used as a first step in hacking data.
Note that although Django has some protection against this when using, for example, class-based views, a person can just use any string for the request. So a person can write as method FOOBAR. If the view for example specifies if request.method == 'POST', and an else: statement, it can thus be used, to enter the else statement with a non-GET method.
But regardless of the use-case, "better be safe than sorry", and guarding the HTTP methods, is just one of the aspects to check.
That being said, if only a subset of methods are allowed, you can use the #require_http_methods [Django-doc] decorator:
from django.views.decorators.http import require_http_methods
#require_http_methods(["GET", "POST"])
def my_view(request):
# I can assume now that only GET or POST requests make it this far
# ...
pass
This decorator thus makes it more elegant to guard that the proper method is used.
To offer a different perspective, I think your question illustrates why you should consider using class-based views, which make life so much simpler when dealing with such problems.
For example the generic CreateView already comes with all the logic built in to restrict the type of HTTP request. It will let you perform a GET request to initialise a form, but require a POST request to process data. Thus you can't accidentally trigger data to be saved through a GET request.
It also provides the framework for proper form data validation, error handling etc - which you would have to implement yourself in a procedural view.
Same goes for the range of other views that Django provides - UpdateView, DetailView etc.
All Django class-based views come with a http_method_names attribute that you can use to control which methods are allowed on your views, e.g.,
from django.views.generic import View
class MyView(View):
# only GET and POST allowed. Anything else will get a 405 Method Not Allowed response.
http_method_names = ['get', 'post']
def get(self, request, *args, **kwargs):
# Logic for GET requests goes here.
def post(self, request, *args, **kwargs):
# Logic for POST requests goes here. No risk of it getting mixed up with GET.
This in addition to providing a lot of other helpers for things like form handling, template loading etc. Procedural views may feel simpler initially, but you will quickly realise that you end up having to write a lot more code to get them to do what you need.

How to safely access request object in Django models

What I am trying to do:
I am trying to access request object in my django models so that I can get the currently logged in user with request.user.
What I have tried:
I found a hack on this site. But someone in the comments pointed out not to do it when in production.
I also tried to override model's __init__ method just like mentioned in this post. But I got an AttributeError: 'RelatedManager' object has no attribute 'request'
Models.py:
class TestManager(models.Manager):
def user_test(self):
return self.filter(user=self.request.user, viewed=False)
class Test(models.Model):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(Test, self).__init__(*args, **kwargs)
user = models.ForeignKey(User, related_name='test')
viewed = models.BooleanField(default=False)
objects = TestManager()
I trying to access request object in my Django models so that I can get the currently logged in user with request.user.
Well a problem is that models are not per se used in the context of a request. One for example frequently defines custom commands to do bookkeeping, or one can define an API where for example the user is not present. The idea of the Django approach is that models should not be request-aware. Models define the "business logic" layer: the models define entities and how they interact. By not respecting these layers, one makes the application vulnerable for a lot of problems.
The blog you refer to aims to create what they call a global state (which is a severe anti-patten): you save the request in the middleware when the view makes a call, such that you can then fetch that object in the model layer. There are some problems with this approach: first of all, like already said, not all use cases are views, and thus not all use cases pass through the middleware. It is thus possible that the attribute does not exist when fetching it.
Furthermore it is not guaranteed that the request object is indeed the request object of the view. It is for example possible that we use the model layer with a command that thus does not pass through the middleware, in which case we should use the previous view request (so potentially with a different user). If the server processes multiple requests concurrently, it is also possible that a view will see a request that arrived a few nanoseconds later, and thus again take the wrong user. It is also possible that the authentication middleware is conditional, and thus that not all requests have a user attribute. In short there are more than enough scenario's where this can fail, and the results can be severe: people seeing, editing, or deleting data that they do not "own" (have no permission to view, edit, or delete).
You thus will need to pass the request, or user object to the user_test method. For example with:
from django.http import HttpRequest
class TestManager(models.Manager):
def user_test(self, request_or_user):
if isinstance(request_or_user, HttpRequest):
return self.filter(user=request_or_user.user, viewed=False)
else:
return self.filter(user=request_or_user, viewed=False)
one thus has to pass the request object from the view to the function. Even this is not really pure. A real pure approach would only accept a user object:
class TestManager(models.Manager):
def user_test(self, user):
return self.filter(user=user, viewed=False)
So in a view one can use this as:
def some_view(request):
some_tests = Test.objects.user_test(request.user)
# ...
# return Http response
For example if we want to render a template with this queryset, we can pass it like:
def some_view(request):
some_tests = Test.objects.user_test(request.user)
# ...
return render(request, 'my_template.html', {'some_tests': some_tests})

Can i call the get method in django class based views from the post method

In my django app's views.py i have a class based view Profile(View) which has two methods get(self, request) and post(self, request). So when a post request is send to this view, when the input passes a test, i want to go invoke the get() method which will return the template with updated data. Is it possible?
Technically, this is of course perfectly possible and as simple as return self.get(request, **kwargs) BUT you very certainly DONT want to do that, as it will break the expectations of POST vs GET. The canonical navigation pattern is that a sucessful POST should be followed by a redirect, and is known as the "post-redirect-get" pattern. Note that you can use sessions (directly or with the messages framework) if you want to pass some state (outside database state) from the POST to the GET.

django rest post return 405 error code

I am using django with rest framework and I try to test POST on existing object but I keep getting 405. my ViewSet looks like this:
class Agents(viewsets.ModelViewSet):
serializer_class = serializer.AgentSerializer
model = serializer_class.Meta.model
....
and in the urls:
router = routers.SimpleRouter()
router.register(r'rest/agents', api_views.Agents, "Agent")
...
urlpatterns += router.urls
I call the post request from within APITestCase class (rest testing), my post request looks like this:
response = self.client.post(url, {'available': True, 'online':True}, format='json')
and printing url shows "/chat/rest/agents/1910_1567/", while 1910_1567 is the valid id of an existing agent (I create the agent during the setup and use its id).
I've seen other questions about rest post getting 405, but all the solutions there were url-related and in my case the url is correct. I even ran the setup outside the test and accessed the url via browser to get the object and the object indeed exist. but when I try to post to it - 405.
any ideas?
thanks!
Most probably your url is somehow matching with some different url's regex, and the request is being dispatched to some other view which disallows post request. Could you mention others urls in your urls.py? Infact you can verify this by adding pdb(debugger) in dispatch method of your view. If you made it to the pdb, then you can assume I was wrong.
If that is not the case, then you can evaluate the issue from dispatch method with debugger. Just in case you have any doubt about how to do that -
class Agents(viewsets.ModelViewSet):
serializer_class = serializer.AgentSerializer
model = serializer_class.Meta.model
def dispatch(self, *args, **kwargs):
import ipdb; ipdb.set_trace()
return super(Agents, self).dispatch(*args, **kwargs)
Solution Found:-
You are passing id in url, so this request would be routed to detail view and detail view only allow GET, PUT, DELETE operations on resource since resource already exists. So to create resource, don't provide the id. Otherwise use PUT request and provide support for creation in PUT.
POST are supposed to be for creation purpose. If you want to update with a ViewSet you'll need to PUT or PATCH (partial update) instead.
Edit:
For more about this, here's some explanation on the HTTP methods used for REST API:
http://restful-api-design.readthedocs.org/en/latest/methods.html
This is also described in the DRF documentation at:
http://www.django-rest-framework.org/api-guide/routers/#simplerouter