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?
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.
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() .
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.
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).
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).
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With