Django Rest Framework - Efficient API Design - django

In any rest API,
Data comes in request body
We perform some Logic on
data and perform some queries
3.finally API responds serialized data.
My question is :-
Where to put data processing Logic? And what is the efficient way of designing any REST API?

Logic on data is mainly put on the views. You can use any views, be it functional, generic API views, and viewsets. There are three types of views in DRF.
You should be comfortable in using one of them, however should understand all of them. Go through this link.
https://micropyramid.com/blog/generic-functional-based-and-class-based-views-in-django-rest-framework/
I personally find comfort in using CBV (Class-Based Views). It is in the views where most of the SQL joins take place too.
For eg:
class GetReviewAPIView(ListAPIView):
permission_classes = [IsAuthenticated]
serializer_class = ReviewSerializer
def get_queryset(self):
user = self.request.user
return Review.objects.filter(user=user)
The above is a good example of CBV where the API call gets all the reviews of a particular user only. There is a SQL join happening between the User table and Review table.
Also, you can write the logic in the serializer class as well instead of view. This is done mainly when you have to write the logic for each serializer field differently and set characteristics for each field. I can give you an example for this as well if you want.

Related

Returning related fields of a model instance

I am creating an app with a rest API that should return values for instances of objects based on the url given. Right now I have the API working using ModelViewSets of my objects for the API.
For example I have three objects, user, transactions, and goals.
As it stands I can go to /mysite/api/users and return a list of all users
I can also go to /mysite/api/users/1 to return just the user with the id '1'.
I can do something similar with transactions and goals.
What I'm looking to do is go to url /mysite/api/users/1/transaction/1/goal
to find the goal associated with the transaction for that user.
I've been scouring tutorials and am not sure what the right question is to ask in order to find something useful to learn how to do this. What is the correct way to go about setting up my rest api like this?
If I understand correctly, you want to create nested ressources.
If you are using Viewsets, then the ExtendedRouter class of the drf-extensions package will allow you to achieve this.
Drf-extensions documentation about this feature: https://chibisov.github.io/drf-extensions/docs/#nested-routes
There is also this module, who also offer the same features.
You can either use url params or query params to solve your issue. I will explain the URL params solution here,
serializers.py
#Write a Goal Serializer
urls.py
#change the URL according to your environment
url(r'^users/(?P<uid>[0-9]+)/transaction/(?P<tid>[0-9]+)/goal/$', GoalViewSet.as_view({'get': 'user_transaction_goal',}), name='user-transaction-goal'),
views.py
class GoalViewSet(viewsets.ModelViewSet):
permission_classes = [IsAuthenticated]
queryset = Goal.objects.all()
def user_transaction_goal(self, request, uid, tid):
#assuming user is FK in transaction and transaction is a FK in goal
#modify the filter rule according to your model design
goals = Goal.objects.filter(transaction=tid, transaction__user=uid)
serializer = GoalSerializer(goals, many=False)
return Response(serializer.data)
As #clement mentioned you can also use plugins to handle this situation.

Django REST framework - views serving both regular webpages and API responses depending on the request

I am writing a web site using Django REST framework. This is my first days with the REST, so please bear with me. Basically, the question is,
Can I come up with a class-based view which could serve both as an API for Android developers (with JSON response) and a view rendering regular Django template? Or I have to define two different views for this purpose ?
If the answer to question 1 is that I have to define two separate views, then what is the most DRY method to do that taking into account that the querysets are the same ?
The view:
class TestList(APIView):
renderer_classes = [TemplateHTMLRenderer]
template_name = 'android/test.html'
def get(self, request):
queryset = Test.objects.all()
return Response({'test_qs': queryset})
Put in other words, imagine that I have a model queryset, I want to both render this on my site to my end user, and to send it to my Android developers. What is the best practice in terms of REST framework code architecture? Two different class based views? Or one view with two methods inside it ? Or one view with one magic method which would do both jobs for me ?
I would suggest to keep it separate. With simple CRUD - you will not have the issues with DRY because they are simply different views, consider:
DRF (basically this is all for CRUD, if you want only a list use: ListModelMixin):
class ServiceViewSet(viewsets.ModelViewSet):
queryset = Service.objects.all()
serializer_class = ServiceSerializer
I think that merging this into one View - sooner or later will get you into troubles.
What kind of troubles?
Your templates can at some point use much more data to display the page to the user than your REST API (simple example: current time) - you will start to implement different context for template and different for REST;
and nothing more came to my mind ;) but I have a feeling that two separate views make it much more clean.
I also understand the risk of repeating the same code twice - but you can always extract the repeating code to some helping structures.
As for queryset - if it's simple - do not bother with one place for storing it. If it can get complicated - again - there's no problem to store the queryset in some helping structure and use in both views:
class ProjectQuerysets(object):
my_test_qs = Test.objects.filter(created_at__gte=now-timedelta(seconds=30)).all()
or event something more sophisticated:
class TestQSMixni(object):
def get_queryset(self, *args, **kwargs):
return Test.objects.filter(user=self.request.user) # something like that;
And later:
class TestList(TestQSMixin, APIView):
def get(self, *args, **kwargs):
queryset = self.get_queryset()
# and in REST:
class ServiceViewSet(TestQSMixin, viewsets.ModelViewSet):
serializer_class = ServicesSerializer
# no queryset needed here
(sorry for example with this services, but I have this in some notes :))
Hope this will help you a little.
At the end - it all depends on your needs :) And the project requirements.
Happy coding.

Django api calculate count of responses

I am trying to do a survey application in django. My model is as follows:
class mymodel(models.Model):
resptype = models.ForeignKey(Response)
ques = models.ForeignKey(Question)
response = models.CharField(max_length=5, blank=True)
Here i am using rest framework to send data to my front end. Right now i have my api defined as follows:
class mymodelList(APIView):
def get(self, request, format=None):
surveydata = mymodel.objects.all()
serialized_surveydata = mymodelSerializer(surveydata, many=True)
return Response(serialized_surveydata.data)
In my app, I have a standard set of 16 questions with multiple choice responses and the choice is saved in the response column in the model.
Now what I am trying to achieve is to calculate the count of responses for each question . ie. For question 1, what is the count that a person responded with 1 or 2 or etc.
Also i would like to know how to send the calculated counts through another json field from the rest framework because I don't have any model defined separately for this data.
EDIT:
This command did the trick for my query but i still not able to figure out how to send it to the front end as a serialized object.
x = mymodel.objects.values('ques','response').order_by().annotate(number_of_responses=Count('response'))
That's not really a great structure for your model, it would probably be easier to create separate Question and Choice classes. The Django tutorial actually uses this type of application as an example... take a look at that for some guidance
Check #detail_route or #list_route from viewsets depending on if you want to show this info per question o for all questions at once.
This will allow you to define a custom endpoint to request the information you are asking for. To do so, you may also need to define a custom serializer to pass extra data or a filter if you want to filter by question, user, etc.

Get an url pk in a generic RESTful view?

I'm trying to manage my REST API like that :
http://xxx/users/userid[0-9]+/projects/projectid[0-9]+/tasks/taskid[0-9]+/
So I can access the JSON easily in my website. But, the thing is, I defined my view classes using the REST framework generic views. For example, here is my UserDetail view :
class UserDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
But of course I don't want all my users to be displayed, I just want my user with the ID userid to be displayed. I don't really know how to do it, I tried
queryset = User.objects.filter(id=userid)
but of course userid is not defined... Any help please ?
Edit : just to clarify, here is the url that leads to this view :
url(r'^users/(?P<pku>[0-9]+)/$', views.UserDetail.as_view(
), name='user-detail'),
First of all, If you want to use class based views, you should read some more about them. They are thoroughly explained on Django's docs and you can read about the specific generics of the framework you're using on the REST framework docs too. I'm not saying that you haven't read those, just that you seem to be missing some basic concepts that are explained there.
Now, to the problem at hand, if you look at the doc's of the generic view you're extending, you can see that it represents the endpoints for a single instance, meaning that it won't act on all your model's instances (as you seem to assume).
Also, you can see that this view is built on top of a series of other classes, the more critical one being GenericAPIView. On there you can see two things:
The queryset class field in this context is meant for filtering the posible instances you can manipulate, not obtaining the specific instance described on your url.
The lookup_field is the field that defines which attribute from your model will be used for getting the actual instance. You should define this field to whatever field you're going to use on your url to identify your object (generally it's pk). It's also important to note that the url should include a keyword argument corresponding to this value.
Internally, the view will take care of calling the get_object method, which usese the lookup_field value to find the specific model, and then feed that object to the serializer and return the result back to the client.
DISCLAIMER: I've never used Django REST framework, I put this answer togheter by reading the relevants docs and based on my experience.
I guess you need this:
Resolve function (django 1.4)
Then in your view class method you can do:
temp1, args, kwargs = resolve(self.request.path)

Django admin for User's objects

I would like to allow a User to have an admin interface to their own Video objects. I was planning on writing some views that allowed things like setting attributes like "published" or deleting objects.
I've started looking into using django's Admin site - but that seems like it might be overly complicated for what I want (just deleting/ setting the published attribute).
Is one approach better than the other? Writing something from scratch or using the Admin site?
If I were to write something from scratch - what is the correct way to achieve ModelAdmin style actions (ie. delete_selected(queryset, request))
This is exactly what the admin should be used for! How could it be too complicated? Even writing a handful of lines of HTML would take longer.
If you built this yourself, no matter how simple, you'll have to define views that list objects, validate input, check permissions, write HTML, implement some kind of multiple action system that maps to python code, ....
Assuming you don't want to do that:
You're going to want to look into making multiple admin sites and filtering admin results to only those that belong to the user via overriding the queryset method on a ModelAdmin
# pasted from docs
class MyModelAdmin(admin.ModelAdmin):
def queryset(self, request):
qs = super(MyModelAdmin, self).queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(author=request.user)