I got a model Slider that has a simple one to many relationship with ImageSlider, thanks to ModelViewSet I can create sliders and then create ImageSliders and asociate them with a slider. But I would also like to be able to just upload a bunch of images and make several SliderImages and asociate them with the same slider I mean I want to have both options but Im not sure how to do that and how to test it with something like Postman. I hope you guys can help me understand that since Im still learning about DRF
Slider View
"""Slider view"""
# Django REST Framework
from rest_framework import viewsets
# Serializers
from api.sliders.serializers import SliderModelSerializer
# Models
from api.sliders.models import Slider
class SliderViewSet(viewsets.ModelViewSet):
"""Slider viewset"""
queryset = Slider.objects.all()
serializer_class = SliderModelSerializer
Slider Image View
"""Slider Images view"""
# Django
from django.shortcuts import get_object_or_404
# Django REST Framework
from rest_framework import viewsets
# Serializers
from api.sliders.serializers import SliderImageModelSerializer
# Models
from api.sliders.models import SliderImage
class SliderImageViewSet(viewsets.ModelViewSet):
"""Slider Image viewset"""
queryset = SliderImage.objects.all()
serializer_class = SliderImageModelSerializer
Slider serializer
"""Slider Serializers"""
# Django Rest Framework
from rest_framework import serializers
# Serializers
from api.sliders.serializers import SliderImageModelSerializer
# Model
from api.sliders.models import Slider
class SliderModelSerializer(serializers.ModelSerializer):
""" Slider Model Serializer. """
images = SliderImageModelSerializer(many=True, read_only=True)
class Meta:
"""Meta class"""
model = Slider
fields = (
'id',
'status',
'images',
)
Slider Image Serializer
"""SliderImage Serializers"""
# Django Rest Framework
from rest_framework import serializers
# Model
from api.sliders.models import SliderImage
class SliderImageModelSerializer(serializers.ModelSerializer):
""" Slider Image Model Serializer. """
slider_id = serializers.IntegerField()
class Meta:
""" Meta class """
model = SliderImage
fields = (
'id',
'url',
'link',
'slider_id',
)
I only care about uploading the files and creating/storing the resources, the other fields in ImageSlider aren't necessary
You can make another endpoint for that purpose, using actions.
Assuming a SliderImage is already created, it would be something like:
class SliderImageViewSet(viewsets.ModelViewSet):
"""Slider Image viewset"""
queryset = SliderImage.objects.all()
serializer_class = SliderImageModelSerializer
#action(detail=True, methods=['post'])
def images(self, request, pk=None):
user = self.get_object()
serializer = SliderImageSerializer(data=request.data)
if serializer.is_valid():
user.save()
return Response({'status': 'images saved'})
else:
return Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST)
With SliderImageSerializer being your new serializer and your endpoint the same as registered to SliderImageViewSet, but with /<id>/images at the end, where id is the id of your SliderImage previously created.
Related
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.
I am using Django Rest Framework so that my other Django project can change records in this project's database, and am using serializers to do so. I am using the .save() method, which will add to the database only if none of the existing records share the same primary key as the ones being saved. I would like to "update" these existing records, either by updating them straight or deleting them then creating them again.
This is my views.py:
from rest_framework.response import Response
from rest_framework.decorators import api_view
from .serializers import parcelSerialiser
#api_view(['POST'])
def addItem(request):
serializer = parcelSerialiser(data=request.data, many = True)
print(request.data)
print(serializer.is_valid())
print(serializer.errors)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
and my serializers.py:
from rest_framework import serializers
from mainMenu.checkIn.models import Parcels
class parcelSerialiser(serializers.ModelSerializer):
class Meta:
model = Parcels
fields = '__all__'
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)
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
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.