Add ListCreateAPIView-class to router - django

How can I add a ListCreateAPIView to the router urls?
Normally I do like:
router = routers.DefaultRouter()
router.register(r'busses', BusViewSet)
but now I have:
class CarList(generics.ListCreateAPIView): ...
I added it to the urlpatterns for now:
urlpatterns = patterns('',
url(r'^carts/', CarList.as_view(model=Car), name='cars'),
and I would like to add this Cars-view (which is working as intended if I call the url manually!) to the router, so it's in the overview page!
So: It works as it is, but I have to manually enter the url, it's not in the overview-page of the API.

The reason is why a ViewSet classes work with a router is a GenericViewSet which has a ViewSetMixin in a bases.
ViewSetMixin override as_view() method so that it takes an actions keyword that performs the binding of HTTP methods to actions on the Resource and router can build a map for action-method.
You can solve it by simple adding that mixin in a class bases:
from rest_framework.viewsets import ViewSetMixin
class CarList(ViewSetMixin, generics.ListCreateAPIView)
....
But it is not clear solution because ListCreateAPIView and ModelViewSet it is just an empty classes with a bunch of mixins in a bases. So you always can build you own ViewSet with a methods you need.
For example, here the code of ListCreateAPIView:
class ListCreateAPIView(mixins.ListModelMixin,
mixins.CreateModelMixin,
GenericAPIView):
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
And here the ModelViewSet:
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
pass
Notice a same mixins ListModelMixin and CreateModelMixin there is only difference in GenericViewSet and GenericAPIView.
GenericAPIView use method names and call an actions inside them. GenericViewSet instead use actions and map them to methods.
Here the ViewSet with methods you need:
class ListCreateViewSet(mixins.ListModelMixin,
mixins.CreateModelMixin,
GenericViewSet):
queryset_class = ..
serializer_class = ..
Now it will work with a router and you can override list and create methods if you need a special behavior.

Related

Override list method in Django rest ViewSet

In a Django Rest Framework ViewSet I have an overrided list()
class TicketViewSet(mixins.ListModelMixin,
viewsets.GenericViewSet):
def_list():
make_my_checks()
things_copied_from_parent()
Since the list() has many lines of code and I must make checks in every ViewSet, how can I make this checks and return the overrided method? A thing like:
def_list():
make_my_checks()
super(list())
You are almost there!!!
class TicketViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
def list(self, request, *args, **kwargs):
make_my_checks() # your custom checks
return super().list(request, *args, **kwargs) # you should return them

Defining two api end points with same generic class view Django Rest Framework?

I am new to Django. I was trying to use django generic views, class based.
How do I implement following situation, I have two api end points login/ and logout/ and the same view class handles it ?
# Login and logout
class SignInActivity(generics.RetrieveUpdateAPIView):
serializer_class = UserSerializer
request = None
def get_queryset(self):
return User.objects.filter(Q(username=self.request.data["username"])
& Q(userpassword=self.request.data["userpassword"]))
def update(self, request, *args, **kwargs):
self.request = request
query_set = self.get_queryset()
if bool(query_set):
query_set.update_or_create(username=self.request.data["username"],
userpassword=self.request.data["userpassword"],
defaults={
"lastlogin": timezone.now()
})
return Response(data={"message": "User logged in successfully.", "response_code": 222}, status=201)
else:
return Response(data={"message": "User not found.", "response_code": 444}, status=201)
And my urls.py is
path('login/', SignInActivity.as_view())
This implementation hadles login/ with this class, PUT method. Now can I use same class with another method to handle logout/ PUT method?
By default generics.RetrieveUpdateAPIView provides 3 HTTP methods, which are GET,PUT,PATCH. You could override any of the following methods to logout procedure.
class SignInActivity(generics.RetrieveUpdateAPIView):
serializer_class = UserSerializer
request = None
def get_queryset(self):
return User.objects.filter(Q(username=self.request.data["username"])
& Q(userpassword=self.request.data["userpassword"]))
def update(self, request, *args, **kwargs):
return Response(data="HTTP PUT method")
def partial_update(self, request, *args, **kwargs):
return Response(data="HTTP PATCH method")
def retrieve(self, request, *args, **kwargs):
# your log-out logic
return Response(data="HTTP GET method")
I think you can use retrieve() method to do logout procedure, because, there is no need of sending additional payloads to the end-point, I assume
I think it's not possible to points two end points to same method function (POST or PUT) of the same view class.
One of the solution could be to use inheritance, where there could be a base class and login and logout endpoint classes inheriting from this class, and later implement respective method functions inside the child classes.
So basically, the two end points will still use functions of different class, with common properties inherited.
class SignInActivity(generics.RetrieveUpdateAPIView):
should be
from rest_framework.views import APIView
class SignInActivity(APIView):
Urls.py
path('login/', SignInActivity.as_view()),
path('logout/', SignInActivity.as_view())
This should be doing the fact as when the request is recieved our domain.com/logout/ it would forward it to the SignInActivity View as well as when request over domain.com/login/ is received!

Django-REST-framework Concrete-View-Classes not returning json?

I'm working on a new app using React and DRF in which React must get the data from DRF in JSON and parse the data. But I think some of my Generic View Classes do not return JSON correctly.
For Example, a "ListCreateAPIView" class returns this:
[{"id":5,"name":"5 Storey I=1.3","Value":1399511075,"NSt":5},{"id":6,"name":"5 Storey I=0.7","Value":1344981250,"NSt":5},{"id":7,"name":"5 Storey I=1","Value":1363157800,"NSt":5}]
While a "RetrieveUpdateDestroyAPIView" class returns this:
{"id":6,"name":"5 Storey I=0.7","Value":1344981250,"NSt":5,"jRatio":"[0.2,0.4,0.4]","jEDR":"[0.02,0.1,0.5,1]","jStrDrSL":"[0.3826667,0.6046667,0.78,0.8666667]","StrDrB":0.8,"jNStrDrSL":"[0.4,0.8,2.5,5]","NStrDrB":0.75,"jNStrAccSL":"[0.25,0.5,1,2]","NStrAccB":0.5,"jPGA":"[0.141,0.374,0.550,0.822]","jRePr":"[75,475,975,2475]","NNTH":7,"jP_CL":"[ 0 , 5 , 6 , 7 ]","jMIDR":"[[[0.014488,0.021893,0.010635,0.029521,0.009106,0.013556,0.034016], [0.019524,0.022306,0.013733,0.041172,0.012122,0.019027,0.027467], \r\n[0.018057,0.019549,0.01485,0.034628,0.010172,0.022447,0.02065], \r\n[0.018057,0.015954,0.009193,0.024401,0.006838,0.022809,0.017592], \r\n[0.017192,0.012215,0.009257,0.016268,0.005854,0.019945,0.012777]], \r\n\r\n[[0.016604,0.025492], \r\n[0.026047,0.03113], \r\n[0.02794,0.02432], \r\n[0.024571,0.01748], \r\n[0.023962,0.014474]], \r\n\r\n[[0.040325], \r\n[0.044064], \r\n[0.035164], \r\n[0.024971], \r\n[0.020532]], \r\n\r\n[[1], \r\n[1], \r\n[1], \r\n[1], \r\n[1]]]","jMAcc":"[[[0.081014271,0.126595311,0.094557594,0.094250765,0.068444444,0.088719674,0.118411825], \r\n[0.06911213,0.091793068,0.097146789,0.11106422,0.056927625,0.118172273,0.103258919], \r\n[0.076614679,0.077261978,0.120961264,0.144769623,0.055780836,0.08214475,0.123833843], \r\n[0.082191641,0.08675739,0.129832824,0.13788685,0.04724159,0.071845056,0.091466871], \r\n[0.18904791,0.111108053,0.098691131,0.13933945,0.052319062,0.198410805,0.117994903]], \r\n\r\n[[0.184002039,0.156264016], \r\n[0.18011213,0.122554536], \r\n[0.24753211,0.136911315], \r\n[0.292653415,0.120941896], \r\n[0.255610601,0.127876656]], \r\n\r\n[[0.201146789], \r\n[0.173687054], \r\n[0.17719368], \r\n[0.172267074], \r\n[0.187479103]], \r\n\r\n[[1], \r\n[1], \r\n[1], \r\n[1], \r\n[1]]]"}
Notice that the data doesn't start with a bracket "[" and because of this I'm not able to parse it in React. I need to know where the problem is. Should I not use GenericViewClasses?
views.py:
class BuildingsList(ListCreateAPIView):
queryset=Building.objects.all()
serializer_class=BuildingSerializerList
class BuildingDetails(RetrieveUpdateDestroyAPIView):
queryset=Building.objects.all()
serializer_class=BuildingSerializerDetails
urls.py:
urlpatterns=[
path('', BuildingsList.as_view()),
path('<int:pk>/', BuildingDetails.as_view()),]
serializers.py:
class BuildingSerializerList(serializers.ModelSerializer):
class Meta:
model=Building
fields=['id','name','Value','NSt']
class BuildingSerializerDetails(serializers.ModelSerializer):
class Meta:
model=Building
fields='__all__'
The Detail-View means, it only returns One item/details of particular look-up. and moreover, the response behavior will purely depend on your Serializer classes (BuildingSerializerList and BuildingSerializerDetails).
The ListCreateAPIView is meant for listing all your Building instances.
In terms of react, the Detail-view returns a JSON object whereas in List-view it returns a JSON-Array
I would recommend you to use DRF's ModelViewset class for views, which is very handy
UPDATE-1
You can do simple CRUD operations on Sample model through REST-API by using following view class
from rest_framework.viewsets import ModelViewSet
class SampleViewset(ModelViewSet):
serializer_class = SampleSerializer
queryset = SampleModel.objects.all()
If you are trying to do CRUD operations by using GenericViewSet, you need to write views like this,
from rest_framework.viewsets import GenericViewSet
class SampleNew(GenericViewSet):
queryset = SampleModel.objects.all()
serializer_class = SampleSerializer
def list(self, request, *args, **kwargs):
# your list logic
return response
def create(self, request, *args, **kwargs):
# your list logic
return response
def destroy(self, request, *args, **kwargs):
# your list logic
return response
def retrieve(self, request, *args, **kwargs):
# your list logic
return response
Did you see the difference !!
Get ListCreateAPIView will return a list(jsonarray),get RetrieveUpdateDestroyAPIView will return a single object info(jsonobject) is right,that's ListCreateAPIView and RetrieveUpdateDestroyAPIView designed to be.You would better change the parse code in fontend in React.
In your case is GenericAPIView not suit your requirement,I think you want to CRUD the model Building by DRF,then the ModelViewSet is something you actually needed.
views.py
from rest_framework.permissions import IsAuthenticated
from rest_framework.viewsets import ModelViewSet
class BuildingViewSet(ModelViewSet):
queryset = Building.objects.all()
serializer_class = BuildingSerializerList
permission_classes = (IsAuthenticated,)
def get_serializer_class(self):
if self.action in ['update', 'partial_update', 'retrieve']:
return BuildingSerializerDetails
else:
return self.serializer_class
urls.py:
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'building', BuildingViewSet)
urlpatterns = [
path('', include(router.urls)),
path('admin/', admin.site.urls),
]
then
create with post http://localhost/building/
list with get http://localhost/building/ # return jsonarray
detail with get http://localhost/building/id/ # return jsonobject
update with put/patch http://localhost/building/id/
delete with delete http://localhost/building/id/
If you really want detail return a list,simple change to:
class BuildingViewSet(ModelViewSet):
....
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
return Response([serializer.data])
but it's really meaningless.

django 405 method not allowed

The error occurs when I request via post method.
views.py
class ConceptForkView(ConceptBaseView, mixins.CreateModelMixin):
print 'oclapi ConceptForkView111'
def dispatch(self, request, *args, **kwargs):
print 'oclapi ConceptForkView dispatch'
return super(ConceptForkView, self).dispatch(request, *args, **kwargs)
def post(self, request):
print 'oclapi ConceptForkView post'
urls.py
url(r'^forking/$', ConceptForkView.as_view(), name='concept-forking'),
ConceptBaseView
class ConceptBaseView(ChildResourceMixin):
lookup_field = 'concept'
pk_field = 'mnemonic'
model = Concept
permission_classes = (CanViewParentDictionary,)
child_list_attribute = 'concepts'
The command print 'oclapi ConceptForkView111' can run, but the method dispatch and post don't run. What is the reason?
I have searched many solutions, but they don't work to me. How can I solve this problem? Thank you.
Note that mixin is not a view. You probably have to inherit from View also. Mixins are usually classes that extend functionality and are not standalone. Class inheriting only from mixins probably won't work properly unless one of these mixins is not mixin in fact.
See: rest framework documentation. There is CreateAPIView which inherits not only from CreateModelMixin but also from GenericAPIView (and you probably should inherit it too). As we can read about GenericAPIView:
This class extends REST framework's APIView class, adding commonly required behavior for standard list and detail views.
So this "commonly required behavior" is important for your class to behave like view.
try using
def create(request, *args, **kwargs)
...
method instead of
def post(self, request):
...
CreateModelMixin uses create method rather than post method

Django: is_secure() decorator not working

I'm converting some FBVs with signals to CBVs, so I have this decorator:
def ensure_https(view_func):
def _checkssl(request, *args, **kwargs):
print request.is_secure()
if not settings.DEBUG and not request.is_secure():
url_str = request.build_absolute_uri()
url_str = url_str.replace('http://', 'https://')
return HttpResponseRedirect(url_str)
return view_func(request, *args, **kwargs)
return _checkssl
and added it to a function in a class based view, as so:
class ExampleTemplateView(TemplateView):
template_name = 'example.html'
#ensure_https
def dispatch(self, request, *args, **kwargs):
...
return HttpResponseRedirect(/hello/')
But I get the following error:
'ExampleTemplateView' object has no attribute 'is_secure'
However, when I use this decorator on a function-based view, it works just fine. Should I be using a particular CBV?
If you need anymore code or info, please let me know. Thanks for your help!
I think you have signal and decorator confused as the pattern in your code is a decorator. Depending on what you're doing there might be better alternatives to where you put the URL redirection logic. I'm thinking webserver(nginx), HTTP Strict Transport Security HTTP header or middleware. Having said that, from the django docs:
To decorate every instance of a class-based view, you need to decorate
the class definition itself. To do this you apply the decorator to the
dispatch() method of the class.
A method on a class isn’t quite the same as a standalone function, so
you can’t just apply a function decorator to the method – you need to
transform it into a method decorator first. The method_decorator
decorator transforms a function decorator into a method decorator so
that it can be used on an instance method. For example:
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.views.generic import TemplateView
class ProtectedView(TemplateView):
template_name = 'secret.html'
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(ProtectedView, self).dispatch(*args, **kwargs)