Use coverage to show what have to be covered by tests drf views. And coverage show that all view is tested ok (cover by unittest + dango client API)
But coverage shows that this part need to be covered:
def get_serializer_class(self):
return self.serializer_class `
I think that that code possible to delete as it useless (it is not my code):)
Any idea how to cover that past of code in GenericAPIView? Thx for any help
There are two approaches to specify the serializer class:
First approach is to set the serializer_class attribute in your class.
Second approach is to override the get_serializer_class()method.
If you already added the serializer_class attribute in your class (first approach) then the get_serializer_class() is definitely useless.
This is the example:
from django.contrib.auth.models import User
from myapp.serializers import UserSerializer
from rest_framework import generics
class UserList(generics.GenericAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
In most situations you should use the first approach because usually you will need only one serializer for your API view. Second approach is useful for dynamic behavior, such as using different serializers for read and write operations, or providing different serializers to different types of users.
Example:
def get_serializer_class(self):
if self.request.user.is_staff:
return StaffSerializer
return BasicSerializer
Related
My code is littered with a lot of parsing keys from request.data objects inside functions, which makes it hard to follow data flow. How can I change from
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
#api_view(["POST"])
#permission_classes([IsAuthenticated])
def greet(request):
# FYI for Character.AI we use the chat and not this stateless_chat view.
payload = request.data
user = payload.get("user", "test")
friend = payload.get("friend", "bart")
num_friends = int(payload.get("num_friends", 16))
return None
to
#api_view(["POST"])
#permission_classes([IsAuthenticated])
def greet(user:str='test', friend:str='bart', num_friends:int=16):
return None
You could take a look at DRF Serializers (https://www.django-rest-framework.org/api-guide/serializers/). In addition to cleaning up your code they provide additional advantages like validation for example.
It's not exactly what you wanted (I don't think that's even possible) but using Serializers is following best practices working with DRF.
I have two models, Foo and Bar. Bar has a foreign key to Foo. I have a ModelViewSet for Foo and I want the route /api/foos/<pk>/bars/ to return all the bars related to the given foo.
I know this can be done using actions. For example
class FooViewSet(viewsets.ModelViewSet):
serializer_class = FooSerializer
queryset = Foo.objects.all()
#action(detail=True, methods=['GET'])
def bars(self, request, pk):
queryset = Bar.objects.filter(foo=pk)
serializer = BarSerializer(queryset, many=True)
return Response(serializer.data)
#bars.mapping.post
def create_bar(self, request, pk):
request.data['foo'] = pk
serializer = BarSerializer(request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
However, this feels like a lot of unnecessary boilerplate code.
Using bars = PrimaryKeyRelatedField(many=True) unfortunately doesn't fit my case because each foo can have hundreds of bars, which I don't want to send in every request.
Is this possible to do in viewsets in a more convenient way? If not, what is the normal DRF way of doing it? A solution with a different URL is also acceptable for me as long as it minimizes boilerplate code.
The straightforward solution for me is to use a simple generics.ListAPIView for this specific endpoint:
views.py
from rest_framework import generics
class FooBarsListAPIView(generics.ListAPIView):
serializer_class = BarSerializer
def get_queryset(self):
return Bar.objects.filter(foo=self.kwargs.get('pk'))
And then you just register this view in your urls.py instead of the viewset.
This is all you need to do in order to achieve the result that you want. You just specify how your queryset filter looks and everything else is done by the ListAPIView implementation.
Viewsets are doing the work in most cases, but if you want something specific like this, the viewset can become an overkill.
Yes, now there is one additional class defined for this specific endpoint, but the boilerplate code is reduced to zero, which is what we want at the end of the day.
According to Django REST framework documentation, the following two code snippets should behave identically.
class UserViewSet(viewsets.ViewSet):
"""
A simple ViewSet for listing or retrieving users.
"""
def list(self, request):
queryset = User.objects.all()
serializer = UserSerializer(queryset, many=True)
return Response(serializer.data)
def retrieve(self, request, pk=None):
queryset = User.objects.all()
user = get_object_or_404(queryset, pk=pk)
serializer = UserSerializer(user)
return Response(serializer.data)
class UserViewSet(viewsets.ModelViewSet):
"""
A viewset for viewing and editing user instances.
"""
serializer_class = UserSerializer
queryset = User.objects.all()
But the way I interpret it, in the first case the query User.objects.all() is run with every api call, which in the second case, the query is run only once when the web server is started, since it's class variable. Am I wrong ? At least in my tests, trying to mock User.objects.all will fail, since the UserViewSet.queryset will already be an empty Queryset object by that time.
Someone please explain me why shouldn't the queryset class argument be avoided like pest and get_queryset be used instead ?
Edit: replacing queryset with get_queryset makes self.queryset undefined in the retrieve method, so I need to use self.get_queryset() within the method as well...
you are wrong, django queries are lazy so in both case the query will run at response time
from django docs:
QuerySets are lazy – the act of creating a QuerySet doesn’t involve any database activity. You can stack filters together all day long, and Django won’t actually run the query until the QuerySet is evaluated
ModelViewSet provides other actions like delete and update you may need to add some limitations on them for user model (like checking permissions or maybe you don't like to let users simply delete their profiles)
self.queryset is used for router and basename and stuff, you can ignore it and set basename in your router manually. it's not forced but I think it make my code more readable
note that usually def get_queryset is used when you want to do some actions on default queryset for example limit self.queryset based on current user. so if get_queryset is going to return .objects.all() or .objects.filter(active=True) I suggest using self.queryset to have a cleaner code
note2: if you decide to define get_queryset I suggest to also define self.queryset (if possible)
note3: always use self.get_queryset in your view method, even you didn't defined this method, you may need need to create that method later and if your view method are using self.queryset it may cause some issues in your code
Adding to Aliva's answer and quoting from DRF viewset code, under get_queryset() method definition it states:
This method should always be used rather than accessing self.queryset
directly, as self.queryset gets evaluated only once, and those results
are cached for all subsequent requests. You may want to override this if you need to provide different
querysets depending on the incoming request.
I am a newbie in Django. I had created REST API using DRF. My Django has 3 apps. So now I want to apply authentication. I have seen much help but I am not to apply the authentication properly. I also want that the model should be attached to the user. So that one user can't see another user entries. Can anyone help me in telling how to implement this a little detailed?
Thanks in advance. Will be a great help if someone answers.
You could add custom permissions,
class IsOwnerOnlyAllowed(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
return obj.owner == request.user
You may add permission_classes = (IsOwnerOnlyAllowed,)
Also, you could override the queryset attribute of your view to provide only entries which are related to the logged in users.
Edit your views,
from rest_framework import generics
from .models import DatasetModel
from .serializer import DatasetSerializer
class DatasetView(generics.ListCreateAPIView):
queryset = DatasetModel.objects.all()
serializer_class = DatasetSerializer
def get_queryset(self):
return self.queryset.filter(owner=self.request.user)
I Have just started with django-rest-framework.
Pretty enthousiastic about it, except for the fact there are very little examples available.
getting the api working is going great, but all the extra's is a puzzle.
(adding extra custom fields etc.)
Now I wonder how you can restrict the allowed_methods in for example a ListView or a DetailView.
Adding this to the class in the views.py like I read somewhere as an answer... does not seem to have any effect:
allowed_methods = ('GET',)
If you are using ModelViewSet and still want to restrict some methods you can add http_method_names.
Example:
class SomeModelViewSet(viewsets.ModelViewSet):
queryset = SomeModel.objects.all()
serializer_class = SomeModelSerializer
http_method_names = ['get', 'post', 'head']
Once you do this, get, post and head will be allowed. But put, patch and delete will not be allowed.
Sorry for necro, but I stumbled upon this question looking for a similar issue.
I only wanted to allow retrieve() but not to list(). What I ended up doing:
from rest_framework import viewsets
from rest_framework.exceptions import MethodNotAllowed
from myapp.models import MyModel
class MyViewSet(viewsets.ModelViewSet):
http_method_names = ["get"]
queryset = MyModel.objects.all()
serializer_class = MySerializer
def list(self, request, *args, **kwargs):
raise MethodNotAllowed("GET")
As almost everything in django-rest-framework, once you find it out, its very simple.
In the urls in stead of using ListOrCreateModelView I had to use ListModelView.
Besides the http_method_names, there's this solution that Django Rest Framework proposes in here: https://www.django-rest-framework.org/api-guide/viewsets/#custom-viewset-base-classes
It consists on inheriting from GenericViewSet instead of ModelViewSet, and from the appropiate mixin classes.
If you want a viewset that provides only the list and retrieve methods (both of them use GET), then do this:
class ListRetrieveViewSet(
Viewsets.GenericViewSet,
mixins.ListModelMixin,
mixins.RetrieveModelMixin,
)
Probably not relevant anymore based on recent years downvotes.. It was relevant in '12 tho :)
Django-rest-framework actually have very many examples..
Take a look at http://django-rest-framework.org, http://django-rest-framework.org/contents.html and http://rest.ep.io/ for some good examples and documentation.
If you are designing a REST function by yourself, not using any of the django-rest-framework magic (like rest.ep.io) to generate it for you, you should look into mixin (http://django-rest-framework.org/howto/mixin.html).
If you want to restrict to only get methods. Just use def get(...) and the mixin class.
Example from link provided:
curl -X GET http://rest.ep.io/mixin/
urls.py
from djangorestframework.compat import View
from djangorestframework.mixins import ResponseMixin
from djangorestframework.renderers import DEFAULT_RENDERERS
from djangorestframework.response import Response
from django.conf.urls.defaults import patterns, url
from django.core.urlresolvers import reverse
class ExampleView(ResponseMixin, View):
renderers = DEFAULT_RENDERERS
def get(self, request):
response = Response(200, {'description': 'Some example content',
'url': reverse('mixin-view')})
return self.render(response)
urlpatterns = patterns('',
url(r'^$', ExampleView.as_view(), name='mixin-view'),
)