example template for django rest framework pagination - django

I am trying to use Django REST framework for the first time and looking at the tutorials there is no template examples, What can I use for template? I tried template_name = 'authorListAjax.html' but I get this response http://imgur.com/fMlyXDN
views.py
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = ('subject', 'date', 'time_start')
class AuthorListAll1(ListAPIView):
template_name = 'authorListAjax.html'
queryset = Author.objects.all()
serializer_class = AccountSerializer
paginate_by = 2
paginate_by_param = 'page_size'
max_paginate_by = 100
urls.py
url(r'^ajax/list/$', AuthorListAll1.as_view(), name='ajax_list'),

You need to set a renderer: http://www.django-rest-framework.org/api-guide/renderers#templatehtmlrenderer
This means adding this line (which tells DRF that the response will be HTML, not JSON):
renderer_classes = (TemplateHTMLRenderer,)
Also, you can not set the template name on your view class; the template name belongs to the renderer class. You can either set it directly on the renderer, like this:
TemplateHTMLRenderer.template_name = 'authorListAjax.html'
or you can overwrite the get method and set it there, like this:
return Response({'user': self.object}, template_name='authorListAjax.html')
I recommend you the second way, because the first one sets the template name globally, and it might get you in troubles :)

Related

Django REST API Listview

Currently I'm trying to develop personal blog with Django/REST API, and I have trouble for that.
There are a number of posts in blog and I want to control those posts with Hyperlink. I made it by using ModelViewSet, however, whole data in detailView is also shown in ListView.
The thing is, I only want "url" and "title" of posts to be shown in ListView while DetailView contains full data.
Here is my code and current results given by REST framework.
Don't mind IndexView
# serializers
class PostSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Post
fields = '__all__'
# views
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = serializers.PostSerializer
permission_classes = (IsAdminUser, )
Post List in REST API:
Post instance in REST API:
As far as I'm aware, you need a separate serializer for the list view.
You could create a custom serializer that takes in a fields arg to select specific fields. But its probably simpler to just have a separate one for the ListView. Also, for the list view, if you are only showing a subset of the model fields, you can use the only() function on the queryset to only return the model data that you need. For example:
qs = MyModel.objects.all().only('field_a', 'field_b', 'field_c')
Here is the custom serializer if you decide to go that way:
class CustomSerializer(serializers.ModelSerializer):
def __init__(self, *args, **kwargs):
selected_fields = kwargs.pop('selected_fields', None)
# used pop function so selected_fields is not passed to superclass
super().__init__(*args, **kwargs)
if selected_fields:
# make sure only fields for the model are allowed
fields = set(selected_fields)
current_fields = set(self.fields.keys())
for field in current_fields - fields:
self.fields.pop(field)
class MyModelSerializer(CustomSerializer):
class Meta:
model = MyModel
fields = '__all__'
In the list view:
required_fields = ('field_a', 'field_b', 'field_c')
data_to_return = MyModelSerializer(model_queryset, many=True, fields=required_fields).data
return Response(data)

django SearchFilter is not filtering the list of queryset,it returns all of it

this is my view views.py file
class StudentList(generics.ListAPIView):
queryset = Student.objects.all()
serializer_class = StudentSerializer
#pagination_class = StudentPageNumberPagination
filter_backends = [SearchFilter]
search_fields=['name','mobile']
this is my serializers class
class StudentSerializer(serializers.ModelSerializer):
class Meta:
model=Student
fields=('id','name','mobile','time','late','date')
this is what i type in browser
http://192.168.0.118:8000/students/?name=ket
and i get all of items in the database
If you use SearchFilter your search string must be passed in search parameter, like this:
http://192.168.0.118:8000/students/?search=ket
You can read about it in docs. If you want to split your query parameters into specific fields you can use DjangoFilterBackend instead of SearchFilter.

Filtering Django queryset with custom function

I'm developing a REST API for an existing system that uses custom permission handling. I'm attempting to use the built-in generics from the Django REST Framework, but I'm running into trouble filtering the list views using my custom permissions. An example of my current view is:
class WidgetList(generics.ListCreateAPIView):
permission_classes = (permissions.IsAuthenticated,)
model = Widget
serializer_class = WidgetSerializer
filter_backends = (filters.DjangoFilterBackend,)
filter_fields = ('widget_type', 'widget_owner')
def get_queryset(self):
"""
Overwrite the query set to check permissions
"""
qs_list = [w.id for w in self.model.objects.all() if
canReadWidget(self.request.user, w)]
return self.model.objects.filter(id__in=qs_list)
This works, however I feel like the get_queryset function could be improved. Because my canReadWidget is custom, I have to evaluate self.model.objects.all() and check which widgets the user can read, but the function must return a query set so I use the id__in=qs_list part. The result being that I make two database calls for what is really just one list fetch.
Is there a standard way to handle this kind of per-object filtering for a generic list view?
At some point, it's better to drop the default generic views or function and roll your own.
You should have a look at the ListModelMixin and override the list to deal with the list instead of turning it into a queryset.
You should adapt the filtering and pagination but you won't hit the DB twice as you currently do.
first install django-filter package and register in settings.py
Write this code on filter.py file
import django_filters
from .models import CustomUser
class UserFilter(django_filters.FilterSet):
first_name = django_filters.CharFilter(label="First Name", lookup_expr='icontains')
last_name = django_filters.CharFilter(label="Last Name", lookup_expr='icontains')
email = django_filters.CharFilter(label="Email", lookup_expr='icontains')
mobile_number = django_filters.CharFilter(label="Mobile No.", lookup_expr='icontains')
##Change Your Fields What You Want To Filtering
class Meta:
model = Widget
fields = {'is_verify'}
On Your Views File write this code:
class WidgetViewSet(MyModelViewSet):
queryset = Widget.objects
serializer_class = "pass your serializer"
def get_filter_data(self):
_data = self.queryset.all()
data = UserFilter(self.request.GET, queryset=_data)
return data.qs.order_by('-id')
def get_queryset(self):
return self.get_filter_data()

django rest extensions drf-extensions TypeError at /data/ register() got an unexpected keyword argument 'parents_query_lookups'

I am trying to get a simple nested route set up with drf-extensions but am having trouble following the docs, I am getting this error:
TypeError at /data/
register() got an unexpected keyword argument 'parents_query_lookups'
Trying to achieve /data/Survey/<survey>/Version/<version>/Product/<product>/
each survey has multiple versions and those versions will contain multiple Products e.g., /data/survey/survey1_name/version/survey1_version1/product/survey1_version_product1/
but currently I have un-nested endpoints
/data/survey/
/data/versions/
/data/products/
models.py
class Survey(models.Model):
survey = models.CharField(choices=SURVEYS, max_length=100)
class Version(models.Model):
version = models.CharField(choices=DATA_RELEASES, max_length=50)
survey = models.ForeignKey(Survey)
class Product(models.Model):
product = models.CharField(choices=PRODUCTS, max_length=100)
version = models.ForeignKey(Version)
views.py
class SurveyViewSet(NestedViewSetMixin, viewsets.ModelViewSet):
queryset = Survey.objects.all()
serializer_class = SurveySerializer
permission_classes = [permissions.AllowAny]
class VersionViewSet(NestedViewSetMixin, viewsets.ModelViewSet):
queryset = Version.objects.all()
serializer_class = VersionSerializer
permission_classes = [permissions.AllowAny]
class ProductViewSet(NestedViewSetMixin, viewsets.ModelViewSet):
queryset = Product.objects.all()
serializer_class = ProductSerializer
permission_classes = [permissions.AllowAny]
serializers.py
class SurveySerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Survey
fields = ('id', 'survey')
class VersionSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Version
fields = ('id', 'version', 'survey')
class ProductSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Product
fields = ('id', 'product', 'version')
urls.py
router = ExtendDefaultRouter()
router.register(r'surveys', views.SurveyViewSet, base_name='survey')
router.register(r'versions', views.VersionViewSet, parents_query_lookups=['survey']),
router.register(r'products', views.ProductViewSet, parents_query_lookups=['version'])
urlpatterns = [
url(r'^data/', include(router.urls)),
]
You should chain the calls to register(), instead of calling them directly on the router :
urls.py
router = ExtendDefaultRouter()
(router.register(r'surveys', views.SurveyViewSet, base_name='survey')
.register(r'versions', views.VersionViewSet, parents_query_lookups=['survey']),
.register(r'products', views.ProductViewSet, parents_query_lookups=['version']))
That's because NestedRouterMixin.register() returns an instance of NestedRegistryItem, which is the class that understand the parents_query_lookups parameter.
In order to keep your project well organized in accordance with the hierarchy of your data, the first thing I would do is separate each tier of urls into a separate entity, and possibly even a separate file. The second would be to add an primary-key endpoint to each tier of the rest API. In the end it would look something like this:
survey_urls.py
router.register(r'survey', views.SurveyViewSet, base_name='survey')
router.register(r'survey-version/', include('my_app.rest_server.version_urls'))
version_urls.py
router.register(r'version', views.VersionViewSet, base_name='version')
router.register(r'version-product/', include('my_app.rest_server.product_urls'))
product_urls.py
router.register(r'product/(?P<pk>[0-9]+)$', views.ProductViewSet, base_name='product')
Then, your final url could look like: /data/survey-version/33/version-product/5/product/8
In all likelihood, this is an unnecessary complication of things, since each product has its own unique id, and then you can just access it with a url like: /data/product/pk, using a single url routing, namely the last line of code above.
My examples assumed the records have a numeric primary key, if the case is otherwise you'd have to change the regex accordingly.

How to represent `self` url in django-rest-framework

I want to add a link to a single resource representation which is an URL to itself, self. Like (taken from documentation):
class AlbumSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Album
fields = ('album_name', 'artist', 'track_listing')
{
'album_name': 'The Eraser',
'artist': 'Thom Yorke',
'self': 'http://www.example.com/api/album/2/',
}
How should this be done?
If you inherit serializers.HyperlinkedModelSerializer all you need to do is pass a url field to fields. See the docs here:
http://www.django-rest-framework.org/tutorial/5-relationships-and-hyperlinked-apis/
Alright, this solved my problem but if you have a better solution please post an answer:
from django.urls import reverse
from rest_framework import serializers
self_url = serializers.SerializerMethodField('get_self')
def get_self(self, obj):
request = self.context['request']
return reverse('album-detail', kwargs={'id': obj.id}, request=request)
here is my solution,
in your view methods create serilizer object like this:
album = AlbumSerializer(data=data, {"request":request})
in your serilizer class override to_representation method (you can read about this method on DRF docs
class AlbumSerializer(serializers.HyperlinkedModelSerializer):
def to_representation(self, obj):
data = super().to_representation(obj)
request = self.context["request"]
return data
According to this issue, you can just add 'url' in the list of fields.
Here is a little more context than you got in the other answers so far. The key is the context argument passed to the serializer constructor and the 'url' in fields.
http://www.django-rest-framework.org/tutorial/5-relationships-and-hyperlinked-apis/
In your viewset:
class AlbumViewSet(viewsets.ViewSet):
def list(self, request):
queryset = Album.objects.all()
serializer = AlbumSerializer(queryset, many=True,
context={'request': request})
return Response(serializer.data)
In the serializer:
class AlbumSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Album
fields = ('album_name', 'artist', 'track_listing', 'url')
As stated above the HyperlinkedModelSerializer will convert all your related fields and remove the ID of the resource as well. I use one of the following solutions, depending on the situation.
Solution 1: import api settings and add the url field:
For more details see URL_FIELD_NAME.
from rest_framework.settings import api_settings
class AlbumSerializer(SelfFieldMixin, serializers.ModelSerializer):
class Meta:
model = Album
fields = ('album_name', 'artist', 'track_listing', api_settings.URL_FIELD_NAME)
Solution 2: a simple mixin, which only works if default fields are used:
class SelfFieldMixin:
"""
Adds the self link without converting all relations to HyperlinkedRelatedField
"""
def get_default_field_names(self, declared_fields, model_info):
"""
Return the default list of field names that will be used if the
`Meta.fields` option is not specified.
"""
default_fields = super().get_default_field_names(declared_fields, model_info)
return [self.url_field_name, *default_fields]
And it can be used like
class AlbumSerializer(SelfFieldMixin, serializers.ModelSerializer):
class Meta:
model = Album
fields = '__all__'
NOTE: It requires Python 3 due to the super() call, the a mixin must be placed before any of the serializer classes!
P.S.: To achieve the required response in the question one must also set the URL_FIELD_NAME to 'self'.
Edit: get_default_field_names must return a list object for Meta.exclude to work on ModelSerializers.
You can use the HyperlinkedIdentityField like so:
class ThingSerializer(ModelSerializer):
class Meta:
model = Thing
fields = ['self_link', ...]
self_link = HyperlinkedIdentityField(view_name='thing-detail')
You need to have your routes named appropriately but the Default routers do so automatically (documented here).
As others have pointed out the HyperlinkedModelSerializer also works. This is because it uses this field automatically. See here.