Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I register a single view (not a viewset) on my router?

I am using Django REST framework and have been trying to create a view that returns a small bit of information, as well as register it on my router.

I have four models which store information, and all of them have a created_time field. I am trying to make a view that returns the most recent objects (based on the created_time) in a single view, where only the four creation times are returned.

So, a possible JSON output from the view would look like

{     "publish_updatetime": "2015.05.20 11:53",     "meeting_updatetime": "2015.05.20 11:32",     "training_updatetime": "2015.05.20 15:25",     "exhibiting_updatetime": "2015.05.19 16:23" } 

I am also hoping to register this view on my router, so it appears with the rest of my endpoints when the API root is loaded.

router.register(r'updatetime', views.UpdateTimeView) 

Here are the four models that I am trying to work with

class Publish(models.Model):     user = models.ForeignKey(MyUser)     name = models.CharField(max_length=50)     created_time = models.DateTimeField( default=datetime.now)  class Meeting(models.Model):     user = models.ForeignKey(MyUser)     name = models.CharField(max_length=50)     file_addr = models.FileField(upload_to=get_file_path)     created_time = models.DateTimeField(default=datetime.now)  class Training(models.Model):     user = models.ForeignKey(MyUser)     name = models.CharField(max_length=50)     image = models.ImageField(upload_to=get_file_path, max_length=255)     created_time = models.DateTimeField(default=datetime.now)  class Exhibiting(models.Model):     user = models.ForeignKey(MyUser)     name = models.CharField(max_length=50)     file_addr = models.FileField(upload_to=get_file_path)     created_time = models.DateTimeField(default=datetime.now) 

Is it possible to do this? And how would it be done?

like image 562
flytofuture Avatar asked May 22 '15 05:05

flytofuture


People also ask

What is difference between APIView and Viewset?

APIView allow us to define functions that match standard HTTP methods like GET, POST, PUT, PATCH, etc. Viewsets allow us to define functions that match to common API object actions like : LIST, CREATE, RETRIEVE, UPDATE, etc.

What is a Viewset?

A ViewSet class is simply a type of class-based View, that does not provide any method handlers such as . get() or . post() , and instead provides actions such as . list() and . create() .

What is Basename in router register?

basename - The base to use for the URL names that are created. If unset the basename will be automatically generated based on the queryset attribute of the viewset, if it has one. Note that if the viewset does not include a queryset attribute then you must set basename when registering the viewset.

What is router register in Django?

Routers are used with ViewSets in django rest framework to auto config the urls. Routers provides a simple, quick and consistent way of wiring ViewSet logic to a set of URLs. Router automatically maps the incoming request to proper viewset action based on the request method type(i.e GET, POST, etc).


2 Answers

Routers work with a ViewSet and aren't designed for normal views, but that doesn't mean that you cannot use them with a normal view. Normally they are used with models (and a ModelViewSet), but they can be used without them using the GenericViewSet (if you would normally use a GenericAPIView) and ViewSet (if you would just use an APIView).

For a list view, the request methods are mapped to ViewSet methods like this

  • GET -> list(self, request, format=None)
  • POST- > create(self, request, format=None)

For detail views (with a primary key in the url), the request methods use the following map

  • GET -> retrieve(self, request, pk, format=None)
  • PUT -> update(self, request, pk, format=None)
  • PATCH -> partial_update(self, request, pk, format=None)
  • DELETE -> destroy(self, request, pk, format=None)

So if you want to use any of these request methods with your view on your router, you need to override the correct view method (so list() instead of get()).


Now, specifically in your case you would have normally use an APIView that looked like

class UpdateTimeView(APIView):      def get(self, request, format=None):         latest_publish = Publish.objects.latest('created_time')         latest_meeting = Meeting.objects.latest('created_time')         latest_training = Training.objects.latest('created_time')         latest_exhibiting = Exhibiting.objects.latest('created_time')          return Response({             "publish_updatetime": latest_publish.created_time,             "meeting_updatetime": latest_meeting.created_time,             "training_updatetime": latest_training.created_time,             "exhibiting_updatetime": latest_exhibiting.created_time,         }) 

The comparable ViewSet would be

class UpdateTimeViewSet(ViewSet):      def list(self, request, format=None):         latest_publish = Publish.objects.latest('created_time')         latest_meeting = Meeting.objects.latest('created_time')         latest_training = Training.objects.latest('created_time')         latest_exhibiting = Exhibiting.objects.latest('created_time')          return Response({             "publish_updatetime": latest_publish.created_time,             "meeting_updatetime": latest_meeting.created_time,             "training_updatetime": latest_training.created_time,             "exhibiting_updatetime": latest_exhibiting.created_time,         }) 

Notice the two required changes: APIView -> ViewSet and get -> list. I also updated the name to indicate that it was more than just a normal view (as a ViewSet cannot be initialized the same way), but that's not required.

So with this new view, you can just register it in the router the same way as any other. You need a base_name here so the url names can be generated (normally this would pull from the queryset).

router.register(r'updatetime', views.UpdateTimeViewSet, base_name='updatetime') 

So now the updatetime endpoint will be made available in the API root and you can get the latest times by just calling the endpoint (a simple GET request).

like image 86
Kevin Brown-Silva Avatar answered Oct 02 '22 15:10

Kevin Brown-Silva


There is one more way to do it:

urlpatterns = [     # ...      url(r'^you_path/', include(router.urls)),     url(r'^you_path/you_sub_path', views.UpdateTimeView.as_view()),     # ...  ] 

But stuff like Swagger will not work with that

like image 36
Alexander Avatar answered Oct 02 '22 15:10

Alexander