Django Rest Framework viewsets queryset duplicate - python

Can I use same query set for two different ModelViewSet classes?
I try to use the same queryset, but django rest framework duplicate my API route.
views.py
class PlannerAgentViewSet(viewsets.ModelViewSet):
queryset = EstateStatus.objects.all()
serializer_class = PlannerAgentListingSerializer
permission_classes = (IsAuthenticated,)
http_method_names = ['get']
class PlannerClientViewSet(viewsets.ModelViewSet):
queryset = EstateStatus.objects.all()
serializer_class = PlannerClientListingSerializer
permission_classes = (IsAuthenticated,)
http_method_names = ['get']
urls.py
router = routers.DefaultRouter()
router.register(r'planner_agent', views.PlannerAgentViewSet)
router.register(r'planner_client', views.PlannerClientViewSet)
And I obtain:
"planner_agent": "http://127.0.0.1:8000/api/v1/planner_client/",
"planner_client": "http://127.0.0.1:8000/api/v1/planner_client/",
When I use different queryset works. So I can use same queryset?
Thanks for helping.
UPDATE:
Route "http:// 127.0.0.1:8000/api/v1/planner_agent/" exists but must accessed manually

Of course you can do it, but you have to manually set the base_name for your routes to change those uris.
In your case :
router.register(r'planner_agent', views.PlannerAgentViewSet, base_name='planner_agent')
router.register(r'planner_client', views.PlannerClientViewSet, base_name='planner_client')

Related

405 Method Post Not Allowed

I wrote the following code:
class UserViewSet(viewsets.ReadOnlyModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [IsCreation|permissions.IsAuthenticated]
def change_password(self, request):
print(request.user)
With the corresponding route in urls.py:
path('api/users/password', views.UserViewSet.as_view({'post': 'change_password'}))
But when I make a request to 'api/users/password/', I get this error:
{
"detail": "Method \"POST\" not allowed."
}
What am I doing wrong here?
ReadOnlyModelViewSet only provides the 'read-only' actions and that's why you are getting error POST not allowed. You can read more about it Here
The ReadOnlyModelViewSet only support the HTTP GET method, as the name indicates.
In order to support other methods, you need to use ModelViewSet class (or other suitable classes)
For example:
class UserViewSet(viewsets.ModelViewSet): # change the super class
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [IsCreation | permissions.IsAuthenticated]
Side Note: the method change_password(...) does seem like a custom action, but you are missing #action decorator.

Django Rest Framework: Passing Context in Viewsets

Viewsets are convenient because we can do stuff like this and get a fully working serializer:
class StoreObjectViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
permission_classes = [IsAuthenticated]
queryset = StoreObject.active_objects.all()
serializer_class = serializers.StoreObjectSerializer
Unfortunately, as far as I know– to pass the context into the serializer we need to do things like this:
PostSerializer(data=request.data, context={'request': request})
Which means we need to manually override every convenient method provided by ViewSets (as far as I know). Is there a way to inject the context into every serializer while still keeping Viewsets convenient?
By default, request is being sent to any Generic View and ViewSet. You can check the source code in GitHub as well. So you do not have to inject them in every view. If you want to pass extra context, then override get_serializer_context(...) method:
class StoreObjectViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
...
def get_serializer_context(self):
context = super().get_serializer_context()
context['custom_context'] = 'Your custom context'
return context

django rest framework same route, different

i'm using django rest framework to build an api, here is my problem
url(r'^profiles/(?P<pk>[0-9]*)', ProfileRetrieveView.as_view(), name='profiles-detail'),
url(r'^profiles/(?P<pk>[0-9]*)', ProfileUpdateView.as_view(), name='profiles-update'),
class ProfileRetrieveView(RetrieveAPIView):
queryset = Profile.objects.all()
serializer_class = ProfileSerializer
class ProfileUpdateView(UpdateAPIView):
queryset = Profile.objects.all()
serializer_class = ProfileSerializer
permission_classes = (IsAuthenticated, )
When i query the api with the link /profile/2 and the method patch, i receive 405, method not allowed, only the get method is allowed, how can i solve that without haven to transform my two view classes into on class with a GenericView Base class and Retrive + update Mixins.
urls.py
url(r'^profiles/(?P<pk>[0-9]*)', ProfileRetrieveUpdateView.as_view(), name='profiles-detail-update'),
views.py
from rest_framework.generics import RetrieveUpdateAPIView
class ProfileRetrieveUpdateView(RetrieveUpdateAPIView):
queryset = Profile.objects.all()
serializer_class = ProfileSerializer
def get_permissions(self):
if self.request.method == "GET":
return []
else:
return [IsAuthenticated()]
You should condense this into a single endpoint. You can have a single class handle all the listing, update, get, etc for this. Try...something like this:
from rest_framework import mixins, viewsets
class ProfileUpdateView(viewset.ModelViewSet,
mixins.ListModelMixin,
mixins.UpdateModelMixin):
serializer_class = ProfileSerializer
permission_classes = (IsAuthenticated, )
get_queryset(self):
return Profile.objects.all()
If you're using pure models, use the built in model stuff, and check out the mixins. It will save you TONS of generic code writing. It has some magic that knows how to route the request to the matching http method.
http://www.django-rest-framework.org/api-guide/generic-views/#mixins
Django rest framework provides generic view you don't require Mixins.
You can directly use RetrieveUpdateAPIView. Provides request methods get to retrieve data, put update data and patch for partial update.
from rest_framework.generics import RetrieveUpdateAPIView
class ProfileUpdateView(RetrieveUpdateAPIView):
queryset = Profile.objects.all()
serializer_class = ProfileSerializer
permission_classes = (IsAuthenticated, )
Reference: http://www.django-rest-framework.org/api-guide/generic-views/#retrieveupdateapiview

Django REST Generic View for related model

I have a setup similar to this - a Cookbook class, which has multiple Recipes.
I have a
class CookbookListCreateView(ListCreateAPIView):
permission_classes = (IsAuthenticated,)
queryset = Cookbook.objects.all()
serializer_class = CookbookSerializer
and this handles creating / listing the cookbooks.
I need a ListCreateView for the Recipe model but the list must belong to a specific cookbook, in such a way that this url:
/cookbook/2/recipes
would return only recipes found in a cookbook with pk of 2.
How can I modify ListCreateAPIView to follow this behavior?
You can create a new route/url:
/cookbook/<cookbook_pk>/recipes
And an api view as you want:
class RecipeListCreateView(ListCreateAPIView):
permission_classes = (IsAuthenticated,)
queryset = Recipe.objects.all()
serializer_class = RecipeSerializer
def get_cookbook(self):
queryset = Cookbook.objects.all()
return get_object_or_404(queryset, pk=self.kwargs['cookbook_pk'])
def get_queryset(self):
cookbook = self.get_cookbook()
return super().get_queryset().filter(cookbook=cookbook)
def perform_create(self, serializer):
cookbook = self.get_cookbook()
serializer.save(cookbook=cookbook)
Use get_cookbook whenever you need the cookbook (eg. in perform_create method as above)
That's what called a "Detail Route" in DRF.
class CookbookListCreateView(ListCreateAPIView):
....
#detail_route(methods=['get'])
def recipes(self, request, **kwargs):
# Do what you would do in a function-based view here
It will suffice for simple cases but in more complex views using nested route functionality of DRF-extensions is a better solution.

custom URLs using django rest framework

I am trying to use the django rest framework to expose my models as APIs.
serializers
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
viewset
class UserViewSet(viewsets.ModelViewSet):
"""
API end point for User details and list
"""
serializer_class = UserSerializer
queryset = User.objects.all()
routers
router.register(r'users',views.UserViewSet)
While this exposes /users/ and users/, I want my URLs to include a user-slug as well, something like /users/1/xyz-user-name.
Has anyone solved this problem? Does this need changes in both the viewset and router code or is it something that can be configured only in the router code? MY "slug" isn't really used for determining url routing, it is only for URL readability.
Any pointers?
I was able to get this to work by using the approach posted here.
django-rest-framework HyperlinkedIdentityField with multiple lookup args
The second error I was receiving was becuase I was including the url definition inside the meta section. It should be before the meta section instead. I also had to specify the lookup field in the viewset code. Here are the relevant parts of my code.
urls.py
from user.views import UserViewSet
user_list = UserViewSet.as_view({'get':'list'})
user_detail = UserViewSet.as_view({'get':'retrieve'})
urlpatterns= [
url(r'^users/$', user_list, name='user-list'),
url(r'^user/(?P<id>\d+)/(?P<slug>[-\w\d]+)/$', user_detail, name='user-detail'),
url(r'^api-auth/', include('rest_framework.urls',namespace = 'rest_framework'))
]
views.py:
class UserViewSet(viewsets.ModelViewSet):
"""
API end point for user details and user list
"""
lookup_field = 'id'
serializer_class = UserSerializer
queryset = user.objects.all()
serializers.py
class UserSerializer(serializers.HyperlinkedModelSerializer):
url = ParameterisedHyperlinkedIdentityField(view_name='user-detail', lookup_fields=(('id', 'id'), ('slug', 'slug')), read_only=True)
class Meta:
model = user
fields = ('url','name','cover_photo')
You should set the lookup_field property in the serializers and viewsets.
In the serializers.py:
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('url', 'username', 'email', 'user_slug')
lookup_field = 'user_slug'
extra_kwargs = {
'url': {'lookup_field': 'user_slug'}
}
In the viewsets.py:
class UserViewSet(viewsets.ModelViewSet):
serializer_class = UserSerializer
queryset = User.objects.all()
lookup_field = ('user_slug')

Categories

Resources