My problem is how to write more strict API using more generic REST API. I have working api in my application but I need to add some more strict services based on generic API and here is a problem because I can't simple override request data because it's immutable. I'm using Django rest framework 3.
Example:
My generic api for animals:
class AnimalService(APIView):
def get(self, request, *args, **kwargs):
data = request.data.copy()
if data.get('type') == 'dog':
#...do something
Now I need api only for hardcoded dogs:
class DogService(AnimalService):
def get(self, request, *args, **kwargs):
#hardcode request.data['type'] = 'dog'
return super(DogService, self).get(*args, **kwargs)
Instead of overriding the request object, you can pass the type in kwargs. You can then use these kwargs in AnimalServiceView as these modified kwargs are passed to it.
class DogService(AnimalService):
def get(self, request, *args, **kwargs):
kwargs['type'] = 'dog' # set the animal type in kwargs
return super(DogService, self).get(request, *args, **kwargs)
Then in your AnimalService view, you can do:
class AnimalService(APIView):
def get(self, request, *args, **kwargs):
if kwargs.get('type') == 'dog': # check the 'type'
#...do something
Another way is to write a custom middleware which will set the animal_type attribute on the request depending on the requested resource. Then in your views, you can just check using request.animal_type attribute.
Related
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):
....
I have following URL set which returns JSON API. Now I wonder is there any way to create another API which aggregates all of these API results and return to one ajax call from client side lets say,url(r'^api/allData/(?P<pk>\d+)$',allData.as_view())
Does anyone know how to prepare class in views.py to achieve this?
urlpatterns = [
url(r'^api/envelope/(?P<pk>\d+)$',envelopeData.as_view(),name='api-envelope'),
url(r'^api/glass/(?P<pk>\d+)$',glassData.as_view(),name='api-glass'),
url(r'^api/opaque/(?P<pk>\d+)$',opaqueData.as_view(),name='api-opaque'),
url(r'^api/plant/(?P<pk>\d+)$',plantData.as_view(),name='api-plant'),
url(r'^api/fan/(?P<pk>\d+)$',fanData.as_view(),name='api-fan'),
url(r'^api/pump/(?P<pk>\d+)$',pumpData.as_view(),name='api-pump'),
url(r'^api/people/(?P<pk>\d+)$',peopleData.as_view(),name='api-people'),
url(r'^api/light/(?P<pk>\d+)$',lightData.as_view(),name='api-light'),
url(r'^api/smallpower/(?P<pk>\d+)$',spData.as_view(),name='api-sp'),
]
Seems like you are using APIView. So, You could call the get() post() methods of the view by using their class object.
Here is one Example
from rest_framework.views import APIView
from rest_framework.response import Response
class MyView_One(APIView):
def get(self, request, pk, *args, **kwargs):
return Response(data={"message": self.__class__.__name__})
class MyView_Two(APIView):
def get(self, request, pk, *args, **kwargs):
return Response(data={"message": self.__class__.__name__})
class MyView_Three(APIView):
def get(self, request, pk, *args, **kwargs):
return Response(data={"message": self.__class__.__name__})
class My_All_View(APIView):
def get(self, request, pk, *args, **kwargs):
return_data = {}
one = MyView_One()
return_data.update({"one": one.get(request, pk).data})
two = MyView_Two()
return_data.update({"two": two.get(request, pk).data})
three = MyView_Three()
return_data.update({"three": three.get(request, pk).data}
return Response(data=return_data)
Use this My_All_View in your urls.py as any other views
Screenshot
You can use nested serializer, for example:
class Serializer1(Serializer):
...
class Serializer2(Serializer):
....
class Serializer3(Serializer):
serializer1 = Serializer1()
serializer2 = Serializer2()
class Meta:
fields = ('serializer1', 'serializer2')
But about merging views, I do not think.
I'm trying to use single url with two views. I found an example of that on official django documentation. I have something like this:
class DetailOrderView(View):
"""Combines form and detail parts into one and in darkness binds
them."""
# import pdb; pdb.set_trace()
def get(self, request, *args, **kwargs):
view = DisplayDetailOrderView.as_view()
return view(self, *args, **kwargs)
def post(self, request, *args, **kwargs):
view = FormDetailOrderView.as_view()
return view(self, *args, **kwargs)
And in urls.py I refer to DetailOrderView.as_view()
However, when I try to run this I get this error:
.... lib/python3.6/site-packages/django/views/generic/base.py", line 84, in dispatch
if request.method.lower() in self.http_method_names:
AttributeError: 'DetailOrderView' object has no attribute 'method'
Which basically means when dispatch(self, request, *args, **kwargs) tries to call request.method.lower() it finds that request has no attribute method! Which is kinda strange right?
request should be defined in as_view() right?
This is pretty much c/p from the official documentation and it should work so I must be doing something stupid..
You are correct that you need to use .as_view() first, but you call the view
function with self as first argument. The first argument of a view function is the request, so:
class DetailOrderView(View):
"""Combines form and detail parts into one and in darkness binds
them."""
def get(self, request, *args, **kwargs):
view = DisplayDetailOrderView.as_view()
return view(request, *args, **kwargs) # no self
def post(self, request, *args, **kwargs):
view = FormDetailOrderView.as_view()
return view(request, *args, **kwargs) # no self
Note that this still can go wrong, since if the DisplayDetailOrderView for example has other URL parameters, it is possible that this view can not process the data correctly.
Although calling another view is technically possible, is is not very common.
I'm attempting to write a mixin for setting a translation language based on the language set in the user Profile model.
When a get request comes in, the mixin should set a language to user language, get response from the view that adds the mixin, and then set the language back to what it was before. I wrote the following mixin, which is invoked, but it's get method is not invoked. What am I doing wrong?
class SetUserLanguageMixin(object):
def get(self, request):
current_language = translation.get_language()
try:
translation.activate(request.user.profile.language)
response = super(SetUserLanguageMixin, self).get(request)
finally:
translation.activate(current_language)
return response
class SomeView(LoggingMixin, APIView, SetUserLanguageMixin):
def get(self, request):
...
return Response(data, status=status.HTTP_200_OK)
If your SomeView overrides get(), then your mixin's get() method will not be called unless you call super(). You could try overriding dispatch in your mixin instead.
Note that your view will be more robust if the overridden get/dispatch method accepts args and kwargs:
def dispatch(self, request, *args, **kwargs):
...
response = super(SetUserLanguageMixin, self).dispatch(request, *args, **kwargs)
...
i have a templateview,the code is here so how do
class MyTemplateView(TemplateView):
def get_context_data(self, **kwargs):
context = super(UBaseTemplateView, self).get_context_data(**kwargs)
# i want to redirect another url in here
# how to do it
return context
Well, you would so something like this:
class MyTemplateView(TemplateView):
def get(self, request, *args, **kwargs):
return HttpResponseRedirect('/<your path here>/')
You can learn more about it here, and in more detail here.
If you want to pass on post data, then all you have to do is this:
class MyTemplateView(TemplateView):
def get_context_data(self, **kwargs):
return HttpResponseRedirect(reverse('/<your url here>/', [params]))
You can also do this using the post function.
class MyTemplateView(TemplateView):
def post(self, request, *args, **kwargs):
# do what you want with post data
# you can get access via request.POST.get()
return HttpResponseRedirect('/<your url>/')
# Or use the above example's return statement, if you want to pass along parameters
A more generic way to do this is with dispatch() as described here. More background on what dispatch does is in the Django docs.
The advantage is that this is going to work whatever HTTP method (GET, PUT, POST etc) has been specified on the request, whereas the get() function's only going to get called if the method is GET.