API Query in Django Rest Framework - python

I have created an API from Database, I can view the API but I am unable to do a query via URL for example: 127.0.0.1:8000/author?author_id=9, I am not sure where to add the query code. I want to filter using fields. Here is my models.py
class AuthorAPI(models.Model):
author_id=models.IntegerField()
name=models.TextField()
author_img_url=models.TextField()
title=models.TextField()
first_published_at=models.DateTimeField()
excerpt=models.TextField()
class Meta:
db_table = 'view_author'
serializers.py
from rest_framework import serializers
from .models import SortAPI, AuthorAPI
class AuthorAPISerializer(serializers.ModelSerializer):
class Meta:
model=AuthorAPI
fields='__all__'
views.py
from .serializers import APISerializer,AuthorAPISerializer
from .models import SortAPI, AuthorAPI
from rest_framework.response import Response
from rest_framework.decorators import api_view
#api_view(['GET'])
def getauthor(request):
if request.method == 'GET':
results = AuthorAPI.objects.all()
serialize = AuthorAPISerializer(results, many=True)
return Response(serialize.data)

In your views, use a ModelViewset
And add the fliter_backend attribute:
filter_backends = [django_filters.rest_framework.DjangoFilterBackend]
See here in the docs:
https://www.django-rest-framework.org/api-guide/filtering/#setting-filter-backends
class AuthorViewset(viewsets.ReadOnlyModelViewset):
serializer_class = AuthorAPISerializer
queryset = AuthorAPI.objects.all()
filter_backends = [django_filters.rest_framework.DjangoFilterBackend]
IMPORTANT
Using django_filter will require you to install additional requirements, buts its well worth it, see installation steps for django_filter here:
https://django-filter.readthedocs.io/en/stable/guide/install.html
And in your urls.py you need to register your viewser with a SimpleRouter as described in the docs here:
https://www.django-rest-framework.org/api-guide/viewsets/#example
Additionally, you'll need to set the filterset_fields to tell DRF what fields you want to allow the user to filter with.
As specified in the docs here:
https://django-filter.readthedocs.io/en/stable/guide/rest_framework.html#using-the-filterset-fields-shortcut
And important word of warning which might not be emphasised enough in the documentation is this point:
Note that using filterset_fields and filterset_class together is not supported.
Once complete, if you browse to /author you should see some filter controls available, et voila

You can use request.GET to get data from URL parameters.
Give this a try
#api_view(['GET'])
def getauthor(request):
if request.method == 'GET':
results = AuthorAPI.objects.all()
# get author_id from the url query parameter
author_id = request.GET.get('author_id', None)
#if author_id is present in the url query parameter then filter the resluts queryset based on the author_id
if author_id:
results = results.filter(author_id=author_id)
serialize = AuthorAPISerializer(results, many=True)
return Response(serialize.data)

Thanks to Swift but there was some errors, viewsets.ReadOnlyModelViewset wasn't working perfectly so I tried this
views.py
import django_filters.rest_framework
from django.contrib.auth.models import User
from rest_framework import generics,viewsets,filters
from django_filters.rest_framework import DjangoFilterBackend,OrderingFilter
from rest_framework.pagination import PageNumberPagination
from rest_framework.renderers import JSONRenderer
class CustomPagination(PageNumberPagination):
page_size = 50
page_size_query_param = 'page_size'
max_page_size = 1000
class AuthorViewset(generics.ListAPIView):
renderer_classes = [JSONRenderer]
pagination_class = CustomPagination
serializer_class = AuthorAPISerializer
queryset = AuthorAPI.objects.all()
filter_backends = [DjangoFilterBackend,filters.SearchFilter,filters.OrderingFilter]
filterset_fields = ['name', 'id','author_id','status','title','first_published_at','story_type']
search_fields=['id','author_id','name','title','first_published_at']
ordering_fields=['id','author_id','name','title','first_published_at']
class Meta:
name="AuthorViewset"
I could have used Sumithran's answer but it was a bit complex if I would like to allow multiple fields because for every field I had to add the same code with some modification which could increase code lines.

Related

How i can to filter queryset by current user in django rest framework

class SalonCarDetailsSerializer(serializers.ModelSerializer):
salon = PrimaryKeyRelatedField(queryset=Salon.objects.filter(owner=?))
class Meta:
model = SalonCarDetails
fields = ["salon", "car", "price", "number_of_cars"]
CurrentUserDefault() doesn't works
Well, you could write your own PrimaryKeyRelated field like that:
class SalonKeyRelatedField(serializers.PrimaryKeyRelatedField):
def get_queryset(self):
qs = super().get_queryset()
request = self.context.get('request')
return qs
then you can filter qs by request.user, this will be called only on POST and PUT requests. You can then include it in your serializer
salon = SalonKeyRelatedField()
don't forget to include salon in your fields
I wish I could see your views.py, but anyway I make some assumptions about it and put it in the class implementation scenario.
#
### views.py
#
# Django native libraries
from rest_framework.serializers import Serializer
from rest_framework import viewsets, mixins, status
from django.db.models import Q
# your serializer and model
from .serializers import YourSalonCarSerializer
from .models import SalonCarDetails
class YourCustomViewSet(mixins.RetrieveModelMixin,
mixins.ListModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
viewsets.GenericViewSet):
queryset = SalonCarDetails.objects.all()
serializer_class = YourSalonCarSerializer
def get_queryset(self):
if self.request.user.is_anonymous:
return []
lookups = Q(user=self.request.user)
queryset = self.queryset.filter(lookups)

django's filter SearchFilter isn't filtering the results, it returns me all of the objects

Im using Django and trying to filter my response data by SearchFilter
http://localhost:8000/autocomplete/a/?search=Something
the problem that it returns me all of the data objects. like there was no filter at all
http://localhost:8000/autocomplete/a/
my views.py:
from autocomplete.models import Autocomplete
from autocomplete.serializers import AutcompleteSerializer
from rest_framework import generics
from rest_framework.views import APIView
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import OrderingFilter, SearchFilter
class AutocompleteListView(generics.ListAPIView):
serializer_class = AutcompleteSerializer
queryset = Autocomplete.objects.all()
filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
filter_fields = ("IATA", "IATAcity") # < this is working
ordering_fields = ("AirportName") # < not working
search_fields = ("IATA", "IATAcity") # < not working
saw possible solution here, in the last comment:
Django REST - SearchFilter not filtering
but dont really got where exactly should i post it.
what am i doing wrong?
Thanks!!
found a solution:
make sure that on settings.py you dont have 'SEARCH_PARAM': 'SOMETHING'!!
you can change your search_fields format tuble to list

validated_data is empty when making POST request with Django REST

I'm trying to create a new entry in my database with Django REST framework and I am able to successfully send a request however validated_data is empty:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.shortcuts import render
from rest_framework import viewsets
from django.shortcuts import get_object_or_404, get_list_or_404
from .models import GetPurchases, CarFax
from .serializers import PurchasesSerializer
from .serializers import CarFaxSerializer
from rest_framework.response import Response
# Create your views here.
class GetCarFax(viewsets.ModelViewSet):
''' This view will be used for POSTing new carfax reports to the database '''
queryset = CarFax.objects.all()
serializer_class = CarFaxSerializer
# authentication_classes = []
permission_classes = []
#print('TEST')
def list(self, request):
# accessed at url: ^api/v1/carfax/$
queryset = CarFax.objects.all()
serializer = CarFaxSerializer(queryset, many=True)
return Response(serializer.data)
def retrieve(self, request, pk=None, *args, **kwargs):
# accessed at url: ^api/v1/retrieve/{pk}/$
queryset = CarFax.objects.all()
record = get_list_or_404(queryset, vin__exact=pk)
serializer = CarFaxSerializer(record, many=True)
return Response(serializer.data)
def create(self, request, **validated_data):
print('TEST')
print(request.data)
print(validated_data)
serializer = CarFaxSerializer(CarFax.objects.create(**validated_data))
headers = self.get_success_headers(serializer.data)
print(serializer.data)
print(headers)
return Response(serializer.data, headers=headers)
serializers.py
class CarFaxSerializer(serializers.ModelSerializer):
class Meta:
model = CarFax
fields = ('vin', 'structural_damage', 'total_loss',
'accident', 'airbags', 'odometer', 'recalls',
'last_updated')
When I print request.data I can see the data I sent. However validated_data is empty, which is the dict I'm using to create the models.
The accepted answer is a nice refactoring but it doesn't answer the question, and thus may not help future readers (like myself, who just went through this). The real reason validated data was empty was because the when you override .create on the ModelViewSet you wipe out the serializer model field bindings and have to explicitly specify them as you would on a regular serializer.
CarFax serializer should have been defined like so:
class CarFaxSerializer(serializers.ModelSerializer):
vin = serializers.CharField(23),
structural_damage = serializers.BooleanField(),
total_loss = serializers.BooleanField(),
accident = serializers.BooleanField(),
airbags = serializers.BooleanField(),
odometer = serializers.IntegerField(),
recalls = serialziers.BooleanField(),
last_updated = serializers.DateField(),
class Meta:
model = CarFax
fields = ('vin', 'structural_damage', 'total_loss',
'accident', 'airbags', 'odometer', 'recalls',
'last_updated')
First of all, your GetCarFax class seems like messy :( because you'd override most of the methods, but it doesn't provide any advantages to the view. So change your GetCarFax view class to below,
class GetCarFax(viewsets.ModelViewSet):
"""
This view will be used for POSTing new carfax reports to the database
"""
queryset = CarFax.objects.all()
serializer_class = CarFaxSerializer
# authentication_classes = []
permission_classes = []
lookup_field = 'myfieldname'
This few lines of code will handle all the CRUD operations for you :)

Bulk Update data in Django Rest Framework

I am creating a Notification apps by Django Rest Framework which users can MARK AS READ ALL notification by using PATCH API in frontend. How can I Bulk Update data can do this task.
This serializer and viewset below just for PATCH only one notification object, but I want to do it all with Notifications which have field is_read = False
Edited with the right way
My Serializers:
class NotificationEditSerializer(ModelSerializer):
class Meta:
model = Notification
fields = (
'id',
'is_read'
)
My Viewset:
from rest_framework.response import Response
class NotificationListAPIView(ReadOnlyModelViewSet):
queryset = Notification.objects.all()
permission_classes = [AllowAny]
serializer_class = NotificationEditSerializer
lookup_field = 'id'
#list_route(methods=['PATCH'])
def read_all(self, request):
qs = Notification.objects.filter(is_read=False)
qs.update(is_read=True)
serializer = self.get_serializer(qs, many=True)
return Response(serializer.data)
My URL:
from rest_framework import routers
router.register(r'notifications/read_all', NotificationListAPIView)
You can try to use list_route for example:
from rest_framework.response import Response
from rest_framework.decorators import list_route
class NotificationListAPIView(ReadOnlyModelViewSet):
#YOUR PARAMS HERE
#list_route()
def read_all(self, request):
qs = Notification.objects.filter(is_read=False)
qs.update(is_read=True)
serializer = self.get_serializer(qs, many=True)
return Response(serializer.data)
the api is available by ^YOUCURRENTURL/read_all/$ more details marking-extra-actions-for-routing
NOTE! since DRF 3.10 #list_route() decorator was removed, you should use #action(detail=False) instead, I used #action(detail=False, methods=['PATCH']) to bulk patch, for example Thank you #PolYarBear

Django REST Framework #detail_route not working as expected

I can't get #detail_route to work as I think it is supposed to.
I have two API calls I want to handle:
/movie/
/movie/highlight
I am trying to use #detail_route to pickup the /movie/highlight url in the viewset.
My urls.py looks like this:
from django.conf.urls import url, include
from rest_framework import routers
from api import views
router = routers.DefaultRouter()
router.register(r'users', views.UsersViewSet)
router.register(r'groups', views.GroupViewSet)
router.register(r'movie', views.MovieViewSet)
My views.py looks like this:
rom django.contrib.auth.models import User, Group
from movies.models import Movie
from api.serializers import UserSerializer, GroupSerializer, MovieSerializer
from rest_framework.response import Response
from rest_framework import permissions
from rest_framework import renderers
from rest_framework import viewsets
from rest_framework.decorators import detail_route
# Code from DRF quickstart tutorial
class UsersViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = User.objects.all().order_by('-date_joined')
serializer_class = UserSerializer
# Code from DRF quickstart tutorial
class GroupViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows groups to be viewed or edited.
"""
queryset = Group.objects.all()
serializer_class = GroupSerializer
# MY CODE
class MovieViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows groups to be viewed or edited.
"""
queryset = Movie.objects.all().order_by('-title')
serializer_class = MovieSerializer
#detail_route(renderer_classes=(renderers.StaticHTMLRenderer,))
def highlight(self, request, *args, **kwargs):
snippet = "Highlight"
return Response(snippet)
The serializers.py looks like this:
from django.contrib.auth.models import User, Group
from movies.models import *
from rest_framework import serializers
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('url', 'username', 'email', 'groups')
class GroupSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Group
fields = ('url', 'name')
# My code...
class MovieSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Movie
fields = ('title', 'details')
If I try the /movie/ url API call it works as expected. If I try the /movie/highlight/ call I get a 404 error.
I am a newbie to DRF so suspect I am doing something very silly here but can't find out what from the various docs and tutorials I have scanned.
You need to use #list_route decorator instead of #detail_route decorator.
This will generate the url movie/highlight/.
Using #list_route decorated method generates the url of type {prefix}/{methodname}/ whereas detail_route decorated method generateds url of type {prefix}/{lookup}/{methodname}/. Here methodname is name of your method and lookup is the lookup value on which lookup is performed to get the object for detail view.
class MovieViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows groups to be viewed or edited.
"""
queryset = Movie.objects.all().order_by('-title')
serializer_class = MovieSerializer
# use list_route decorator
#list_route(renderer_classes=(renderers.StaticHTMLRenderer,))
def highlight(self, request, *args, **kwargs):
snippet = "Highlight"
return Response(snippet)
If you register your URLs using routers.DefaultRouter(), it will generate the URL /movie/pk/highlight instead of /movie/highlight. If you want a custom URL other than the pre-generated one, use URL mapping.

Categories

Resources