In the docs there is the example of methods with custom url:
http://www.django-rest-framework.org/tutorial/6-viewsets-and-routers
class SnippetViewSet(viewsets.ModelViewSet):
...
#link(renderer_classes=[renderers.StaticHTMLRenderer])
def highlight(self, request, *args, **kwargs):
snippet = self.get_object()
return Response(snippet.highlighted)
This example add following route:
url(r'^snippets/(?P<pk>[0-9]+)/highlight/$', snippet_highlight, name='snippet-highlight'),
It is possible to add an url without pk param, like this?
r'^snippets/highlight/$'
The ViewSets docs mention using action decorator:
from rest_framework.decorators import action
class SnippetViewSet(viewsets.ModelViewSet):
...
#action(detail=False, methods=['GET'], name='Get Highlight')
def highlight(self, request, *args, **kwargs):
queryset = models.Highlight.objects.all()
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
Then just update your queryset to do whatever it needs to do.
The advantage of doing it this way is that your serialisation is preserved.
If your urls.py looks like this:
from django.contrib import admin
from django.urls import path, include
from rest_framework import routers
from snippets import viewsets
router = routers.DefaultRouter()
router.register('snippets', viewsets.SnippetViewSet)
urlpatterns = [
path('admin/', admin.site.urls),
path('snippets/', include(router.urls)),
]
Then it is reachable via http://localhost:8000/snippets/highlights
To see usage for a POST, or how to change routing, see docs for routers.
Yes, you can do that. Just add your method in the viewset with the list_route decorator.
from rest_framework.decorators import list_route
class SnippetViewSet(viewsets.ModelViewSet):
...
#list_route(renderer_classes=[renderers.StaticHTMLRenderer])
def highlight(self, request, *args, **kwargs):
...
It will add a url without the pk param like :
r'^snippets/highlight/$'
You can even specify the methods it supports using the methods argument in your decorator.
http://www.django-rest-framework.org/api-guide/routers/#usage
Since this question still turns up on first Google Page, here is up-to-date (for the late march of 2020) snippet (pun intended) to start working on your custom ModelViewSet route for single object:
from rest_framework.decorators import action
class SnippetViewSet(viewsets.ModelViewSet):
...
#action(detail=True, methods=['POST'], name='Attach meta items ids')
def custom_action(self, request, pk=None):
"""Does something on single item."""
queryset = Snippet.objects.get(pk=pk)
serializer = self.get_serializer(queryset, many=False)
return Response(serializer.data)
Having default routers from the DRF tutorial will allow you to access this route with: http://localhost:8000/snippets/<int:pk>/custom_action/
I am trying to understand Django RESTFramework. I am already familiar with Django. I want to create an endpoint that accepts some text data and processes it and returns it to the user along with the results of the processing (in text). I have completed a couple of tutorials on the topic but I still don't understand how it works. Here is an example from a working tutorial project. How can I edit it to achieve my goal? It all looks automagical.
# views.py
from rest_framework import generics
from .models import Snippet
from .serializers import SnippetSerializer
class SnippetList(generics.ListCreateAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
# Here I would like to accept form data and process it before returning it along with the
# results of the processing.
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
Okay, I think you are a newbie in Django rest and try to understand its flow so I can explain it with an example of a subscription plan.
First, create a model in models.py file
from django.db import models
class SubscriptionPlan(models.Model):
plan_name = models.CharField(max_length=255)
monthly_price = models.IntegerField()
yearly_price = models.IntegerField()
Then create views in a view.py file like
from rest_framework.views import APIView
class SubscriptionCreateAPIView(APIView):
serializer_class = SubscriptionSerializer
def post(self, request):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(
{'message': 'Subscription plan created successfully.',
'data': serializer.data},
status=status.HTTP_201_CREATED
)
and then define a serializer for validation and fields in which we can verify which fields will be included in the request and response object.
serializers.py
from rest_framework import serializers
from .models import SubscriptionPlan
class SubscriptionSerializer(serializers.ModelSerializer):
plan_name = serializers.CharField(max_length=255)
monthly_price = serializers.IntegerField(required=True)
yearly_price = serializers.IntegerField(required=True)
class Meta:
model = SubscriptionPlan
fields = (
'plan_name', 'monthly_price', 'yearly_price',
)
def create(self, validated_data):
return SubscriptionPlan.objects.create(**validated_data)
Now add urls in src/subsciption_module/urls.py
from django.urls import path
from .views import SubscriptionCreateAPIView
app_name = 'subscription_plan'
urlpatterns = [
path('subscription_plan/', SubscriptionCreateAPIView.as_view()),
]
At the end include module url in root urls.py file where your main urls will be located. It will be the same directory which contains settings.py and wsgi.py files.
src/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/v1/', include('src.subscription_plan.urls', namespace='subscription_plan')),
]
That's it. This is how flow works in django rest and you can process data and display data in this way. For more details you can refer django rest docs.
But this is not in any way different from what you do with plain Django. Your SnippetDetail view is just a class-based view, and like any class-based view if you want to do anything specific you override the relevant method. In your case, you probably want to override update() to do your custom logic when receiving a PUT request to update data.
environment: django==1.11.11, rest_framework
Having methods with the same name handle different routes in the same view class or view set, using the decorator ?
example:
class Indexs(APIView):
#decorator("detailed/")
def get(self, request):
pass
#decorator("list/")
def get(self, request):
pass
# supplemental
I want to handle two GET requests in the same view class. One GET request details, the other request list content, but the same name request is overwritten
urls.py
from django.conf.urls import url
from indexs import views
urlpatterns = [
# url(r'category/$', views.Indexs.as_view()),
url(r'list/$', views.Indexs.as_view()),
url(r'detailed/$', views.Indexs.as_view()),
]
views.py
class Indexs(APIView):
def get(self, request):
return Response({"list": "list"})
def get(self, request):
return Response({"detailed": "detailed"})
http://127.0.0.1/list and http://127.0.0.1/detailed
Return results are all
{
"detailed": "detailed"
}
but i want
http://127.0.0.1/list Return results
{
"list": "list"
}
http://127.0.0.1/detailed
{
"detailed": "detailed"
}
Learn more about implementing class based views in django rest framewrok documentation it's pretty much straight forward. Django Rest Framework makes it easy to write DRY code with built in Generic views.
For example,an in built generic view like ListCreateAPIView below will perform GET and POST Http methods.
You can get a list of snippets and snippet detail from your urls:
urls.py
urlpatterns = [
url(r'^snippets/$', views.SnippetList.as_view()),
url(r'^snippets/(?P<pk>[0-9]+)/$', views.SnippetDetail.as_view()),
]
views.py
class SnippetAPIView(generics.ListCreateAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
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.