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.
Related
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.
I am new to Django and scratching my head with the following question
how do I know which function is run in specific processes?
for example in below view, retrieve function is executed. I flipped the documentation, however, could not find anything named retrieve to overwrite
class ProfileRetrieveAPIView(RetrieveAPIView):
serializer_class = ProfileSerializer
def retrieve(self, request, username, *args, **kwargs):
print("hey yo i am running 01")
try:
profile = Profile.objects.select_related('user').get(
user__username=username
)
except Profile.DoesNotExist:
raise
serializer = self.serializer_class(profile)
return Response(serializer.data)
In general the generic views in Django Rest Framework are made by inheriting from various mixins. It is also documented which mixin a view is inheriting from. For example the documentation for RetrieveAPIView which you use is as follows:
Used for read-only endpoints to represent a single model instance.
Provides a get method handler.
Extends:
GenericAPIView,
RetrieveModelMixin
So you can override get here if needed (it actually calls the retrieve method here). If you further follow the link for RetrieveModelMixin you will see that it's documentation has this line:
Provides a .retrieve(request, *args, **kwargs) method, that implements
returning an existing model instance in a response.
Here we can see it mentions that it provides a retrieve method. So you just need to check the documentation for the super classes for a respective class if you want to know what method is used by it.
Note: If all else fails when searching the documentation consider simply looking at it's the source code on GitHub or in your own IDE.
;)
According to DRF documentation, there is a default mapping the views and the router:
list is for GET on a list of objects, like /users/
retrieve is for GET on a single object, like /users/3/
create is for POST on a non-detailed endpoint, like /users/
update is for PUT on a detailed endpoint, like /users/3/
partial_update is for PATCH on a detailed endpoint, like /users/3/
destroy is for DELETE on a detailed endpoint, like /users/3/
Those are the expected/standard mappings within DRF views and viewsets
You can view some examples here: https://www.django-rest-framework.org/api-guide/generic-views/#mixins
I am struggling to understand how permissioning in DRF is meant to work. Particularly when/why should a permission be used versus when the queryset should be filtered and the difference between has_object_permission() & has_permission() and finally, where does the serializer come in.
For example, with models:
class Patient(models.Model):
user = models.OneToOneField(User, related_name='patient')
class Appointment(models.Model):
patient = models.ForeignKey(Patient, related_name='appointment')
To ensure that patients can only see/change their own appointments, you might check in a permission:
class IsRelevantPatient(BasePermission):
def has_object_permission(self, request, view, obj):
if self.request.user.patient == obj.appointment.patient:
return True
else:
return False
But, modifying the queryset also makes sense:
class AppointmentViewSet(ModelViewSet):
...
def get_queryset(self):
if self.request.user.is_authenticated:
return Appointment.objects.filter(patient=self.request.user.patient)
What's confusing me is, why have both? Filtering the queryset does the job - a GET (retrieve and list) only returns that patient's appointments and, a POST or PATCH (create or update) only works for that patient's appointments.
Aside from this seemingly redundant permission - what is the difference between has_object_permission() & has_permission(), from my research, it sounds like has_permission() is for get:list and post:create whereas has_object_permission() is for get:retrieve and patch:update. But, I feel like that is probably an oversimplification.
Lastly - where does validation in the serializer come in? For example, rather than a permission to check if the user is allowed to patch:update an object, You can effectively check permissions by overriding the update() method of the serializer and checking there.
Apologies for the rambling post but I have read the docs and a few other question threads and am at the point where I am probably just confusing myself more. Would really appreciate a clear explanation.
Thanks very much.
First, difference between has_object_permission() and has_permission() :
has_permission() tells if the user has the permission to use the view or the viewset without dealing with any object in the database
has_object_permission() tells if the user has the permission to use the view or the viewset based on a specific object in the database.
The important note thaw is that DRF wont perform the test itself in the case of object level permission, but you have to do it explicitly by calling check_object_permission() somewhere in your view (doc here).
The second important note is that DRF will not filter the result of the query based on object permission. If you want the query to be filtered, then you have to do it yourself (by overriding get_queryset() like you did or using a filter backend), that's the difference.
The serializer has nothing to do with permission neither with filtering. It handles objects one by one, applying validation (not permission) on each field of each objects.
I have a table called 'users' and 'location'. Users table has a foreign key that relates to location table. I have a users serializer to get the JSON. What would I do to get the hyperlinks for the users table using its primary key?
In django rest framework documentation, I couldn't find a solution. I tried using hyperlinkrelatedfield. But still I couldn't achieve this. Can someone help me in finding the solution?
Using rest-framework HyperlinkedRelatedField does not work because it was never built to expose the URL of the object being requested. Mainly because since the client already has the url of the user, why send it back again? Nevertheless you can achieve this by doing something like this.
class UserSerializer(serializers.ModelSerializer):
user_url = serializers.SerializerMethodField()
class Meta:
model = User
def get_label_location(self, obj):
return HyperlinkedRelatedField(view_name='user-detail',
read_only=True) \
.get_url(obj, view_name='label-detail',
request=self.context['request'], format=None)
Take note on a few things,
view-name param to the HyperlinkedRelatedField should be based on your url configuration
read-only has to be true since otherwise you'll have to specify the queryset. But since we have the object needed to generate the url we can ignore that.
I've set format param to None but you might want to set it based on your settings.
You can read up about SerializerMethodField here.
Suppose you want to show
list of all blogs
list of blogs created by a user
How do I implement them in django-rest-framework?
Do I make two viewsets?
Do I make two get_querysets() somehow?
Or two permissions class?
Edit:
you want to show all blogs in a recent tab.
you want to show a user's blog in his profile page.
you want to show popular blogs in a popular tab.
There could many more list of blogs...
It seems that you'd like to just filter the queryset based on some given parameters. You should look at the django-filter and DRF filtering options. They might be what you really need. Filtering
.../blogs/?owner=1
This will give you all blogs that have "owner" field equal to user with id==1
Another option is to use #list_route decorator inside your viewset like this:
#list_route
def popular(self, request)
.... # Do stuff
return Response(data, status=status.HTTP_200_OK)
This will add a blogs route to your viewset and return whatever you tell it to return.
So going to '../blogs/popular/' will return 'data'