Reputation: 357
I am new to Django REST Framework. What I try to do is to render a Generic APIView (RetrieveUpdateDestroyAPIView) in HTML similarly to the way the ViewSet is automatically rendered in the Browsable API.
Following the official documentation, I have in my myApp/views.py:
class AnnounceViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows announces to be viewed or edited.
"""
queryset = Announce.objects.all()
serializer_class = AnnounceSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,
IsOwnerOrReadOnly,)
def perform_create(self, serializer): # without this, the POST request of the announce doesnt work
serializer.save(owner=self.request.user)
class AnnounceList(APIView):
renderer_classes = [TemplateHTMLRenderer]
template_name = 'myApp/announces_list.html'
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
def get(self, request):
queryset = Announce.objects.all()
return Response({'announces': queryset})
class AnnounceDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Announce.objects.all()
serializer_class = AnnounceSerializer
renderer_classes = [TemplateHTMLRenderer]
template_name = 'myApp/announce_detail.html'
permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly,)
In my urls.py:
from django.conf.urls import url, include
from rest_framework import routers
from myApp import views
from django.contrib import admin
router = routers.DefaultRouter()
router.register(r'api/users', views.UserViewSet)
router.register(r'api/groups', views.GroupViewSet)
router.register(r'api/announces', views.AnnounceViewSet)
# Wire up our API using automatic URL routing.
# Additionally, we include login URLs for the browsable API.
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'', include('myApp.urls')),
url(r'^', include(router.urls)),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
url(r'^accounts/', include('allauth.urls')),
url(r'^announces/$', views.AnnounceList.as_view(), name='announces-list'),
url(r'^announces/(?P<pk>[0-9]+)/$', views.AnnounceDetail.as_view(), name='announce-detail'),
When I go the Browsable API, through the link /api/announces/3/, I can see properly the announce object, with the right permissions depending on the authenticated user.
But when I go to the /announces/3/, I have this error:
NoReverseMatch at /announces/3/
Reverse for 'announce-detail' with keyword arguments '{'pk': ''}' not found. 3 pattern(s) tried: ['announces/(?P<pk>[0-9]+)/$', 'api/announces/(?P<pk>[^/.]+)\\.(?P<format>[a-z0-9]+)/?$', 'api/announces/(?P<pk>[^/.]+)/$']
Here is my announce_detail.html template:
{% load rest_framework %}
{% block content %}
<form action="{% url 'announce-detail' pk=announce.pk %}" method="POST">
{% csrf_token %}
{% render_form serializer %}
<input type="submit" value="Save">
</form>
{% endblock content %}
If I understand well, the Django REST View (be it ViewSet or ViewAPI) is for retrieving/putting data in JSON format, and the Django normal view is for HTML usual rendering. However, the Django REST View can also be used for normal Django HTML rendering. By having an exposed API endpoint, data could be retrieved and be used by other applications (the same web app or other web app/mobile app etc...). Correct me if I am wrong.
I don't understand why I got the error... Thank you for your help!
UPDATE After creating a normal APIView, I manage to get the form rendered, and the permissions is well respected (status 403 Forbidden when a user tries to click on the Save button, when the he/she is not the owner. But still, the fields appeared as 'modifyable', ie, we can input text inside the area, but the Save button doesn't save the data if it's not the owner that does the modification.
myApp/views.py
class AnnounceDetail(APIView):
renderer_classes = [TemplateHTMLRenderer]
template_name = 'myApp/announce_detail.html'
permission_classes = (permissions.IsAuthenticatedOrReadOnly,
IsOwnerOrReadOnly,)
def get(self, request, pk):
announce = get_object_or_404(Announce, pk=pk)
self.check_object_permissions(self.request, announce) # required for IsOwnerOrReadOnly to work fine see https://stackoverflow.com/questions/25554415/django-rest-framework-ignoring-my-isownerorreadonly-permissions
serializer_context = {
'request': Request(request),
}
serializer = AnnounceSerializer(announce, context=serializer_context)
return Response({'serializer': serializer, 'announce': announce})
def post(self, request, pk):
announce = get_object_or_404(Announce, pk=pk)
self.check_object_permissions(self.request, announce) # required for IsOwnerOrReadOnly to work fine see https://stackoverflow.com/questions/25554415/django-rest-framework-ignoring-my-isownerorreadonly-permissions
serializer_context = {
'request': Request(request),
}
serializer = AnnounceSerializer(announce, context=serializer_context, data=request.data)
if not serializer.is_valid():
return Response({'serializer': serializer, 'announce': announce})
serializer.save()
return HttpResponseRedirect(reverse('announces-list')) # redirect to URL that is associated with the name announces-list
Upvotes: 5
Views: 9324
Reputation: 1116
views.py
from django.shortcuts import get_object_or_404
from rest_framework.response import Response
class AnnounceDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Announce.objects.all()
serializer_class = AnnounceSerializer
renderer_classes = [TemplateHTMLRenderer]
template_name = 'myApp/announce_detail.html'
permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly,)
def retrieve(self, request, pk, *args, **kwargs):
announce = get_object_or_404(Announce, pk=pk)
serializer = self.get_serializer(announce) # typo fixed
return Response({'serializer': serializer, 'announce': announce})
announce_detail.html
{% load rest_framework %}
{% load staticfiles %}
{% block content %}
<form action="{% url 'announce-detail' pk=announce.pk %}" data-method="PUT">
{% csrf_token %}
{% render_form serializer %}
<input type="submit" value="Save">
</form>
{% endblock content %}
<script>
window.drf = {
csrfHeaderName: "X-CSRFTOKEN",
csrfCookieName: "csrftoken"
};
</script>
<script src="{% static 'rest_framework/js/jquery-1.12.4.min.js' %}"></script>
<script src="{% static 'rest_framework/js/ajax-form.js' %}"></script>
<script src="{% static 'rest_framework/js/csrf.js' %}"></script>
<script src="{% static 'rest_framework/js/bootstrap.min.js' %}"></script>
<script src="{% static 'rest_framework/js/prettify-min.js' %}"></script>
<script src="{% static 'rest_framework/js/default.js' %}"></script>
<script>
$(document).ready(function() {
$('form').ajaxForm();
});
</script>
Upvotes: 2