How to modify router and viewsets for template rendering - django

I have a simply CRUD application with Rest-Framework
my views, serializers and routers is created dynamically.
I used a simple code from DRF docs like that:
VIEWS:
class PersonViewSet(viewsets.ModelViewSet):
serializer_class = PersonSerializer
queryset = Person.objects.all()
SERIALIZERS:
class PersonSerializer(serializers.ModelSerializer):
class Meta:
model = Person
fields = '__all__'
URL:
router = routers.SimpleRouter()
router.register(r'persons', PersonViewSet)
Now I have to do smth for displaying data for end-users.
My goal is:
1. Play 1-2 weeks with build-in django functionality for frontend development
2. Consider about vue.js as a frontend framework.
So I have to use django and DRF as a backend (this is for sure).
I have some trouble with template rendering.
Now I create a simple HTML templates and try to implement them.
There are some problems when using them with ViewSets. Seems that I have to use another parent class for views.
Could you provide me some examples how should I change my views and router?
I already tried to use simply APIView, but seems that routers works only with viewsets.

Now I create a simple HTML templates and try to implement them. There are some problems when using them with ViewSets. Seems that I have to use another parent class for views.
Views
DRF defaults to rendering responses as either JSON (rest_framework.renderers.JSONRenderer) or BrowsableApi (rest_framework.renderers.BrowsableAPIRenderer). [source]
In order to render HTML from within DRF APIView (and it's subclasses) you have to set TemplateHTMLRenderer as one of renderer_classes in your class: [source]
class PersonDetail(generics.RetrieveAPIView):
queryset = Person.objects.all()
renderer_classes = [TemplateHTMLRenderer]
def get(self, request, *args, **kwargs):
self.object = self.get_object()
return Response({'person': self.object}, template_name='person_detail.html')
Routers only work with ViewSets. To expose your custom view on an endpoint you have to append it into urlpatterns in your urls file: [source]
router = routers.SimpleRouter()
router.register(r'persons', PersonViewSet)
urlpatterns = [
url(r'^person_detail/$', PersonDetail.as_view()),
]
urlpatterns += router.urls

Related

How to use allow only POST in Django REST API Viewset

I went through the django rest framework documentation on Viewsets and dont seem to understand how to allow only POST requests on the browser API.
Viewset
class EmailViewSet(viewsets.ModelViewSet):
queryset = models.Email.objects.all()
serializer_class = serializers.EmailSerializer
Model
class Email(models.Model):
email = models.EmailField(max_length=50,unique=True)
def __str__(self):
return str(self.email)
Serializer
class EmailSerializer(serializers.ModelSerializer):
class Meta:
model = models.Email
fields = ["email"]
Viewsets are used together with a router and, depending on what is being exposed in your viewset, various GET and POST will be created by the Django REST framework automatically.
Your EmailViewSet is a ModelSerializer, and exposes .list() (), .retrieve(), .create(), .update(), .partial_update(), and .destroy(), through inheritance. All those actions are GET and POST either at the {prefix}/ and {prefix}/{url_path}/ of your router.
If you want to narrow the set of actions, you should derive EmailViewSet from specific mixins that are limiting the actions of the viewset, for instance (see this example):
CreateModelMixin will be a POST on {prefix}/
UpdateModelMixin will be a POST on {prefix}/{url_path}/
from . import models, serializers
from rest_framework import mixins
class EmailViewSet(viewsets.GenericViewSet,mixins.CreateModelMixin,):
queryset = models.Email.objects.all()
serializer_class = serializers.EmailSerializer
def create(self,request):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)

Django admin loads view template when url pattern is same as model name

I'm writing my first Django project in Django 2.0.
I noticed another weird behavior with Django urlpatterns.
I have an app starrednotes and model within it as Starred(models.Model)
same is the case with Shared(models.Model) within sharednotes app
I have configured the urlpattern for with path pattern same as the model name
urlpatterns = [
url(r'^starred/$', StarredNotes.as_view(), name='starred'),
url(r'^shared/$', SharedNotes.as_view(), name='shared'),
]
and view StarredNotes is
class StarredNotes(ListView):
template_name = 'notes/starred.html'
model = Starred
context_object_name = 'starred_notes'
def get_queryset(self):
starred_notes = Starred.objects.filter(user=self.request.user)
return starred_notes
#method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
return super(self.__class__, self).dispatch(request, *args, **kwargs)
The URL setup above is accessible using
http://example.com/notes/shared
http://example.com/notes/starred
But when I'm accessing these two models from admin with URL as
http://example.com/admin/sharednotes/shared
http://example.com/admin/starrednotes/starred
These two links are loading the template setup in the StarredNotes and SharedNotes class instead of admin template.
I can not understand why it is behaving like this. Is it a Django limitation or bug in Django.
Anyway here are two ways you can get rid of it.
1. Change urlpattern
change URL pattern and replace the pattern with something other than the model name.
In my case, this is how my urlpatterns looks now.
urlpatterns = [
url(r'^starred/$', StarredNotes.as_view(), name='starred'),
url(r'^shared/$', SharedNotes.as_view(), name='shared'),
]
2. Change model name
This is not the recommended way but you can rename your model to something else only if urlpattern is more important than the model. Changing model name may require changes to many other places as well.
class SharedNote(models.Model):
# model fields
class StarredNote(models.Model):
# model fields

django rest framework: forms how to pass the select options

I am trying to create a form in reactjs as frontend, which will create an object using Django api
I am trying to create an ingredient
the following is the serializer to create or update and ingredient
class IngredientCreateUpdateSerializer(ModelSerializer):
class Meta:
model = Ingredient
fields = [
'name',
'munit',
'rate',
'typeofingredient',
]
I will be having munit and typeofingredient as select fields.
The options for these select fields have to supplied from the server.
eg: munit can have options of kg,ltr, pcs etc
and typeofingredient can have option of vegetables, spices, fruits etc
both are ForeignKey type.
So prior to using the create api i have to supply to the form the options of both munit and typeofingredient at that particular instance from the server.
So how to do this. For getting the options should i have to create another api. or is there any direct way
If both typeofingredient and munit are foreign keys then you can define serializers for each of those models and use list api to populate select options.
If you want to combine both Serializers in a single api, you can do the same thing in the ViewSet.
views.py
#Assuming MunitOptionsSerializer and TypeOfIngredientOptionsSerializer are
#your serializers
class IngredientOptionsViewSet(viewsets.ViewSet):
permission_classes = [IsAuthenticated]
def list(self, request):
# assuming MunitOptions & TypeOfIngredientOptions are the models
qs = MunitOptions.objects.all()
s1 = MunitOptionsSerializer(qs, many=True)
qs = TypeOfIngredientOptions.objects.all()
s2 = TypeOfIngredientOptionsSerializer(qs, many=True)
return Response({'munit':s1.data, 'typeofingredient':s2.data})
In your app urls.py
options_list = OptionsViewSet.as_view({
'get': 'list',
})
router = routers.DefaultRouter()
urlpatterns = patterns(
url(r'^', include(router.urls)),
url(r'^options/$', options_list, name='options-list'),
)
Also make 'read only' restriction at ViewSet by inheriting them from ReadOnlyModelViewSet instead of ModelViewSet, since you will be using those serializers only for list/retrieve functions.

Django rest framework- Only see's one view

I'm creating 4 different views for my API. However, Django Rest Framework only see's one API/URL.
The last project in my views is always the one that appears with DRF. EG if I remove "ProjectViewSet" from my views, "Location" will appear as the URL at DRF.
This screenshot provides info:
My views
class DataViewSet(viewsets.ModelViewSet):
queryset = Task.objects.exlude(Q(tag=1)|Q(name=1))
serializer_class = TaskSerializer
class EventViewSet(viewsets.ModelViewSet):
queryset = Task.objects.exlude(Q(tag=2)|Q(name=2))
serializer_class = TaskSerializer
class LocationViewSet(viewsets.ModelViewSet):
queryset = Task.objects.exlude(Q(tag=3)|Q(name=3))
serializer_class = TaskSerializer
class ProjectViewSet(viewsets.ModelViewSet):
queryset = Task.objects.exlude(Q(tag=4)|Q(name=4))
serializer_class = TaskSerializer
My URLS (again):
router = routers.DefaultRouter()
router.register(r'Tag', TagViewSet)
router.register(r'Info', InfoViewSet)
router.register(r'Data', DataViewSet)
router.register(r'Friends', FriendsViewSet)
urlpatterns = router.urls
urlpatterns += [
url(r'^1.1/tag/', rest_views.TagView.as_view()),
url(r'^1.1/task/', rest_views.TaskView.as_view()),
]
Found the solution. As my serializers share the same data model, DRF seems to be getting stuck trying to automatically discover the url naming pattern.
Giving a base_name argument to each of my models solved the issue!
router.register(r'Data', DataViewSet, base_name='Data')
router.register(r'Friends', FriendsViewSet, base_name='Friends')

django-rest-framework limit the allowed_methods to GET

I Have just started with django-rest-framework.
Pretty enthousiastic about it, except for the fact there are very little examples available.
getting the api working is going great, but all the extra's is a puzzle.
(adding extra custom fields etc.)
Now I wonder how you can restrict the allowed_methods in for example a ListView or a DetailView.
Adding this to the class in the views.py like I read somewhere as an answer... does not seem to have any effect:
allowed_methods = ('GET',)
If you are using ModelViewSet and still want to restrict some methods you can add http_method_names.
Example:
class SomeModelViewSet(viewsets.ModelViewSet):
queryset = SomeModel.objects.all()
serializer_class = SomeModelSerializer
http_method_names = ['get', 'post', 'head']
Once you do this, get, post and head will be allowed. But put, patch and delete will not be allowed.
Sorry for necro, but I stumbled upon this question looking for a similar issue.
I only wanted to allow retrieve() but not to list(). What I ended up doing:
from rest_framework import viewsets
from rest_framework.exceptions import MethodNotAllowed
from myapp.models import MyModel
class MyViewSet(viewsets.ModelViewSet):
http_method_names = ["get"]
queryset = MyModel.objects.all()
serializer_class = MySerializer
def list(self, request, *args, **kwargs):
raise MethodNotAllowed("GET")
As almost everything in django-rest-framework, once you find it out, its very simple.
In the urls in stead of using ListOrCreateModelView I had to use ListModelView.
Besides the http_method_names, there's this solution that Django Rest Framework proposes in here: https://www.django-rest-framework.org/api-guide/viewsets/#custom-viewset-base-classes
It consists on inheriting from GenericViewSet instead of ModelViewSet, and from the appropiate mixin classes.
If you want a viewset that provides only the list and retrieve methods (both of them use GET), then do this:
class ListRetrieveViewSet(
Viewsets.GenericViewSet,
mixins.ListModelMixin,
mixins.RetrieveModelMixin,
)
Probably not relevant anymore based on recent years downvotes.. It was relevant in '12 tho :)
Django-rest-framework actually have very many examples..
Take a look at http://django-rest-framework.org, http://django-rest-framework.org/contents.html and http://rest.ep.io/ for some good examples and documentation.
If you are designing a REST function by yourself, not using any of the django-rest-framework magic (like rest.ep.io) to generate it for you, you should look into mixin (http://django-rest-framework.org/howto/mixin.html).
If you want to restrict to only get methods. Just use def get(...) and the mixin class.
Example from link provided:
curl -X GET http://rest.ep.io/mixin/
urls.py
from djangorestframework.compat import View
from djangorestframework.mixins import ResponseMixin
from djangorestframework.renderers import DEFAULT_RENDERERS
from djangorestframework.response import Response
from django.conf.urls.defaults import patterns, url
from django.core.urlresolvers import reverse
class ExampleView(ResponseMixin, View):
renderers = DEFAULT_RENDERERS
def get(self, request):
response = Response(200, {'description': 'Some example content',
'url': reverse('mixin-view')})
return self.render(response)
urlpatterns = patterns('',
url(r'^$', ExampleView.as_view(), name='mixin-view'),
)