How do I pass arguments in Django REST framework modelviews? - django

Here is my views for DRF API
class CityEventsViewSet(viewsets.ModelViewSet):
def __init__(self, request, *args, **kwargs):
queryset = CityEvents.objects.filter(city=kwargs.get('city_name'))
serializer_class = CityEventsSerializer
URL:
router.register(r'cityevents/(?P<city_name>[\w\-]+)/$', CityEventsViewSet, base_name='cityevents')
I am not able to access the views function. It is not able to resolve the URL.

kwargs['city_name']
if I understand well what you mean

The url kwargs can be accessed in anywhere in the viewset as it is an instance attribute of the viewset. As a result, you don't need to do that filtering in __init__() but in the get_queryset() method.
Something like this should suffice:
def get_queryset(self):
city_name = self.kwargs['city_name']
queryset = CityEvents.filter(city_name=city_name)
return queryset

This fix worked for me. The retrieve function will receive the arguments passed through the url. and we don't need to add the regex in URL
class CityEventsViewSet(viewsets.ModelViewSet):
queryset = CityEvents.objects.all()
serializer_class = CityEventsSerializer
def retrieve(self, request, pk=None):
queryset = CityEvents.objects.filter(city=pk)
return JsonResponse(CityEventsSerializer(queryset,many=True).data,safe=False)
URL :
router.register(r'cityevents', CityEventsViewSet)

Related

Django rest framework creating Orders and order items

I want to create a Order and order items.
For this i am simply creating new model object in views.py using CreateApiView but i am receiving error that "Serializer_class" should be included but i don't need serializer for this.
//views.py
class CreateOrder(CreateAPIView):
def Post(self,request):
header_token = request.META.get('HTTP_AUTHORIZATION', None)
print(header_token)
access_token = header_token.split(' ')[1]
status,user = validate_token(access_token)
cart=Cart.objects.get(user=user)
print(cart)
if cart:
total=cart.total
userprofile=UserProfile.objects.get(user=user)
order,created=Order.objects.get_or_create(billing_profile=userprofile,total=total)
cart_items=CartItem.objects.get(cart=cart)
print(cart_items)
for item in cart_items:
itemid=item.item_id
qty=item.quantity
item_instance = Items.objects.get(item_id=item)
order_item,created = OrderItems.objects.get_or_create(order=order, product=item_instance,quantity=qty)
order.save()
order_item.save()
if created:
item.delete()
return Response (status=rt_status.HTTP_200_OK)
I want to understand how to achieve this with or without serializer
You are overriding the incorrect post method. If you look at the source code of CreateAPIView you will see the method named as shown below.
class CreateAPIView(mixins.CreateModelMixin, GenericAPIView):
"""
Concrete view for creating a model instance.
"""
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
NOTE: The method is all lower case.
This method calls self.create which is derived from the CreateModelMixin and this method needs a serializer.
If you need something light weight where a serializer is not needed I would suggest using APIView.
from rest_framework.views import APIView
class CreateOrder(APIView):
def post(self, request):
....

Django rest framwork: DELETE without pk

I user Django Rest Framwork. I want to make a api for delete an object like this
DELETE .../items/
to delete request.user's item. (Each user can create at most one item only, and only owner can delete his item.)
I use mixins.CreateModelMixin, mixins.ListModelMixin, viewsets.GenericViewSet for list view and create. I have tried
#action(methods=['delete'], detail=False, url_path='')
def leave(self, request, *args, **kwargs):
...
but url pattern will go:
.../items/leave/$
How can I config the router or path for this? Thanks
In Django rest framework decorators, if url_path be empty strig that replace by function name. So you cannot use url_path='' as a URL path.
You can use just a simple APIView with GET method, and do what you want in that. like this:
class MyDeleteAPIView(APIView):
def get(self, request, *args, **kwargs):
# for example
try:
user = request.user
instance = SomeModel.objects.get(user=user)
instance.delete()
return Response({"message":"deleted successfuly"}, status=status.HTTP_200_OK)
except:
return Response({"message":"delete fail"}, status=status.HTTP_400_BAD_REQUEST)
now you can define your desired url:
path('delete/', MyDeleteAPIView.as_view(), name='delete'),

Passing kwargs data to custom paginator with Django Rest Framework

I have a custom paginator that needs to access the kwargs argument pk given a url like this one.
r'^(?P<pk>[0-9]+)/stuff/$'
Here's what I am doing right now, but it feels a little hackish.
This is my view.
class StuffList(ListAPIView):
serializer_class = MySerializer
pagination_class = MyPagination
def get_queryset(self):
self.request.kwargs = self.kwargs
return Stuff.objects.all()
and then, in my paginator, when the request object is passed as an argument to paginate_queryset, I can access the kwargs through it.
def paginate_queryset(self, queryset, request, view=None):
"""Returns paginated queryset."""
self.pk = request.kwargs.get('pk')
pass
Is there another way to access it from the request object without manually adding the kwargs to it in get_queryset? I find it odd, because i can easily get a ton of info with the request object, but can't seem to be able to simply get the url kwargs, which forces me to add them myself. Thank you!
Based on the documentation it's valid to access the kwargs on the instance object: http://www.django-rest-framework.org/api-guide/filtering/#filtering-against-the-url

how to make a get request to a url inside django view and return the response?

without using serializer_class and queryset properties inside viewclass(which inherits viewsets.ModelViewSet) ;
i want to make a get request to a another url and return its response as the result of django get request.
can we do that?
Thanks.
You can put any dummy model and make it work.
class SampleViewSet(APIView):
model = DummyModel
renderer_classes = (JSONRenderer,)
def get(self, request, *args, **kwargs):
if request.method == "GET":
// do some operation
return Response(<json response>, status=status.HTTP_200_OK)

How do you access data in the template when using DRF ModelViewSet and TemplateHTMLRenderer?

I've got a Django Rest Framework ModelViewSet and am trying to use the TemplateHTMLRenderer to display HTML. Following along in the tutorial:
from rest_framework import permissions, renderers, viewsets
from rest_framework.decorators import link
from . import models, serializers
from .permissions import IsOwnerOrReadOnly
class SnippetViewSet(viewsets.ModelViewSet):
template_name = 'snippet-list.html'
queryset = models.Snippet.objects.all()
serializer_class = serializers.SnippetSerializer
renderer_classes = (renderers.TemplateHTMLRenderer,)
permission_classes = (permissions.IsAuthenticatedOrReadOnly,
IsOwnerOrReadOnly,)
#link(renderer_classes=[renderers.StaticHTMLRenderer])
def highlight(self, request, *args, **kwargs):
snippet = self.get_object()
return Response(snippet.highlighted)
def pre_save(self, obj):
obj.owner = self.request.user
If I add a key in def resolve_context() I can access the model objects in my template that are passed into the RequestContext. If I don't add the data key then I don't know how to access the Snippets.
def resolve_context(self, data, request, response):
if response.exception:
data['status_code'] = response.status_code
#return RequestContext(request, data) # original source on github
return RequestContext(request, {'data': data}) # if I add a key I can access it
So I've got to be missing something easy or how I'm expecting this to behave is not how the authors intended?
I would go this way:
class SnippetViewSet(viewsets.ModelViewSet):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
renderer_classes = (renderers.JSONRenderer, renderers.TemplateHTMLRenderer)
def list(self, request, *args, **kwargs):
response = super(SnippetViewSet, self).list(request, *args, **kwargs)
if request.accepted_renderer.format == 'html':
return Response({'data': response.data}, template_name='home.html')
return response
and use http://127.0.0.1:8000/snippets/.html to get table (or whatever suffix you use).
This way you don't override resolver for each render type.
Other solution would be to just create dedicated view for list action and only use HTML renderer. But then you would have a small code duplication.
I also met the same question with you, and I also thought so. I came here by Google. I didn't like override "def list(self, request, *args, **kwargs):", because I felt it broke the viewset design idea. After I researched the snippet tutorial and source code in the "site-packages\rest_framework", I got the key, not viewset but "serializer.data". In the "site-packages\rest_framework\serializers.py", I found the class BaseSerializer, i.e., the top base class of ModelSerializer. Its property "data" is defined as follows:
#property
def data(self):
... # omitted the function body here, because it didn't care about this solution.
return self._data
This property data is just the "serializer.data" that is just the response passed to template. So I just overrided the data property in "snippets/serializers.py", and after calling the father's method, set the key for the returned data:
class SnippetSerializer(serializers.ModelSerializer):
#property
def data(self):
return { 'data' : super(serializers.ModelSerializer, self).data } #'data' can be replaced with other wanted name.
class Meta:
model = Snippet
fields = ('id', 'title', 'code', 'linenos', 'language', 'style')
OK, use the name 'data' in your template.
I subclassed and overrode the method that provides the template context, so that the serializer data is available under data within the template context:
from rest_framework.renderers import TemplateHTMLRenderer
class MyHTMLRenderer(TemplateHTMLRenderer):
def get_template_context(self, data, renderer_context):
context = {'data': data}
response = renderer_context['response']
if response.exception:
data['status_code'] = response.status_code
return context
Inside the viewset use renderer class
renderer_classes = (renderers.JSONRenderer, renderers.TemplateHTMLRenderer)
like above and override the ListModelMixin's list method.
mariodev's answer gives the best example also.