Extending multiple CustomViewsets - as readonly with Django - django

in our project, our lead developer assigned a task to refactor some viewsets in our project.
Create a readonly Asset view that will return all defaults and romy assets
So the original code looks like this
class DefaultAssetViewSet(viewsets.ModelViewSet):
queryset = DefaultAsset.objects.all()
serializer_class = DefaultAssetSerializer
permission_classes = [IsAdminUser]
filter_backends = [DjangoFilterBackend, OrderingFilter, SearchFilter]
filterset_fields = ['name']
search_fields = ['name', 'default_value']
#action(detail=False, methods=['get'])
def defaults(self, request):
defaults = {}
for d in self.queryset.all():
defaults[d.name] = d.default_value
return Response({'defaults': defaults})
def destroy(self, request, *args, **kwargs):
try:
return super().destroy(request, *args, **kwargs)
except models.ProtectedError:
return Response(
{'detail': ErrorDetail('Unable to perform this action.')},
status=status.HTTP_403_FORBIDDEN)
class RomyAssetViewSet(viewsets.ModelViewSet):
queryset = RomyAsset.objects.all()
serializer_class = RomyAssetSerializer
permission_classes = [IsAdminUser]
filter_backends = [DjangoFilterBackend, OrderingFilter, SearchFilter]
filterset_fields = [
'romy', 'default_asset'
]
search_fields = [
'romy', 'default_asset'
]
So my first idea to come to my mind is to extend these two Views into AssetViewSet class
class AssetViewSet(RomyAssetViewSet, DefaultAssetViewSet,viewsets.ReadOnlyModelViewSet):
""" some code here"""
Is it possible to extend custom viewsets like these? And also how to implement get or list for both RomyAssetViewet and DefaultAssetViewset inside AssetViewsetClass?

Related

partial update in django rest viewset(not modelviewset)

i am beginner in django and i was trying to learn work with djangorest viewset(not modelviewset)
and i cant find any resource to undrestand it.
i want to learn how to write a partial_update with viewset.
here is what i have tried:
models.py :
class UserIdentDocs(models.Model):
owner = models.ForeignKey(User,on_delete=models.CASCADE, related_name = "user_ident_Docs")
code = models.PositiveBigIntegerField(null=True)
img = models.ImageField(null=True)
video = models.FileField(null=True)
is_complete = models.BooleanField(default=False)
serializers.py:
class AdminUserIdentSerializer(serializers.ModelSerializer):
class Meta:
model = UserIdentDocs
fields = [
"owner",
"code",
"img",
"video",
"is_complete",
]
views.py:
from rest_framework.parsers import MultiPartParser, FormParser
class UserDocAdminViewSet(viewsets.ViewSet):
"""User docs admin view set"""
permission_classes = [AllowAny]
parser_classes = (MultiPartParser, FormParser)
serializer_class = AdminUserIdentSerializer
queryset = UserIdentDocs.objects.filter(is_complete=False)
def list(self, request):
serializer = self.serializer_class(self.queryset, many=True)
return Response(serializer.data)
def retrive(self, request, pk=None):
doc_object = get_object_or_404(self.queryset, pk=pk)
serializer = self.serializer_class(doc_object)
return Response(serializer.data)
def create(self, request ):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data,status=status.HTTP_201_CREATED)
def partial_update(self, request, pk=None):
doc_object = get_object_or_404(self.queryset, pk=pk)
serializer = self.serializer_class(doc_object,data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response({"detail":"item updated succesfuly"}, status=status.HTTP_205_RESET_CONTENT)
urls.py:
urlpatterns = [
...
# admin viewset for docs
# list of accounts with is_complete=False
path("admin/userdocs/", UserDocAdminViewSet.as_view({"get":"list", "post":"create"}), name="user-docs-list"),
path("admin/userdocs/<int:pk>", UserDocAdminViewSet.as_view({"get":"retrive","patch":"partial_update"}), name="user-docs-detail"),
]
i can create user in browsable api but when i want to use partial update i can't even see the fields in browsable api and i only see this :
media type: multipart/form-data
content:this is what i see

How to turn off pagination for a particular viewset in Django Rest Framework?

This is a snippet of the code that I tried to disable pagination as suggested in answers to similar questions. But this is not working for me. I even tried overriding the default paginate_queryset method but ended up observing that it wasn't even getting called(maybe I am doing it the wrong way)
class TestViewSet(viewsets.ModelViewSet):
queryset = Test.objects.all()
serializer_class = TestSerializer
filter_backends = (django_filters.DjangoFilterBackend, SearchFilter)
filterset_fields = {'status': ('exact', 'in'), }
search_fields = ['=id', ]
pagination_class = None
Below is also the code I tried to override paginate_queryset
def paginate_queryset(self, queryset, request, view=None):
if 'no_page' in request.query_params:
return None
return super().paginate_queryset(queryset, request, view)
try this
class TestViewSet(viewsets.ModelViewSet):
queryset = Test.objects.all()
serializer_class = TestSerializer
filter_backends = (django_filters.DjangoFilterBackend, SearchFilter)
filterset_fields = {'status': ('exact', 'in'), }
search_fields = ['=id', ]
paginator = None # -- add this
#pagination_class = None # -- remove this
You can try this.
#property
def paginator(self):
self._paginator = super(TestViewSet, self).paginator
if self.action == 'the_action_you_want_pagination_disabled':
self._paginator = None
return self._paginator

TypeError: 'ShiftSerializer' object is not callable

I am trying to do with ModelViewSet. I am facing this error now
Here is my viewset=>
class ShiftViewSet(viewsets.ModelViewSet):
queryset = Shift.objects.all()
serializer_class = ShiftSerializer()
# filter_backends = (filters.DjangoFilterBackend,)
# filterset_fields = ('shiftid',)
#action(methods=['get'], detail=False)
def newest(self, request):
newest = self.get_queryset().order_by('Created_DT').last()
serializer = self.get_serializer_class()(newest)
return Response(serializer.data)
#action(methods=['get'], detail=False)
def shiftsum(self, request):
query = (
Shift.objects.values('shiftid')
.annotate(shiftdesc=Max('shiftdesc'))
.annotate(overnight=Max('overnight'))
.annotate(isspecialshift=Max('isspecialshift'))
.annotate(ct=Count('*')) # get count of rows in group
.order_by('shiftid')
.distinct()
)
serializer = ShiftSummarySerializer(query,many=True)
return Response(serializer.data)
#action(methods=['get'], detail=False)
def byshiftid(self, request):
shiftid = self.request.query_params.get('shiftid',None)
query = self.get_queryset().filter(shiftid=shiftid)
serializer = ShiftSerializer(query,many=True)
return Response(serializer.data)
Here is my router and url =>
router.register('shifts_mas', ShiftViewSet, base_name='shifts')
path('api/', include(router.urls))
Normally I can call like /api/shifts_mas/ and I will get all record of shift but just now i got this error and i dont know why. May i know why?
You should have a serializer class and not an instance of the class as your serializer_class attribute
serializer_class = ShiftSerializer # No parenthesis here

Django DRF - add a sub view/url?

I want to create a subview (if this is the right term?) under a view that alters the query set for example
parent URL
mysite.com/api/sites
Child URL
mystic.com/apit/sites/open
and also each one of those URLS could be searched so
parent URL
mysite.com/api/sites/search=London
Child URL
mystic.com/api/sites/open/search=London
my parent View, serializer, and URL already exists
class SiteROView(viewsets.ReadOnlyModelViewSet):
queryset = Site.objects.all()
serializer_class = SiteSerializer
permission_classes = (IsAdminUser,)
filter_class = Site
filter_backends = (filters.SearchFilter,)
search_fields = ('location','postcode','state')
so I think I need to somehow add the suburl to that
class SiteROView(viewsets.ReadOnlyModelViewSet):
queryset = Site.objects.all()
serializer_class = SiteSerializer
permission_classes = (IsAdminUser,)
filter_class = Site
filter_backends = (filters.SearchFilter,)
search_fields = ('location','postcode','state')
def url_open:
queryset = Site.objects.filter(state='open')
Is this possible, and how would I achieve it?
Thanks
You can do that by using detail_route decorator
from rest_framework.response import Response
class SiteROView(viewsets.ReadOnlyModelViewSet):
..........
# your codes up here
#list_route(methods=['get'],url_path='open' permission_classes=[YourPermissionClass])
def open(self, request, *args, **kwargs):
# your rest of code and response
queryset = <your_filtered_queryset>
serializer = self.serializer_class(queryset, many=True)
return Response(data=serializer.data)

How to return a list of objects with an extra field in Json (Django Rest Framework)

I'm using ModelViewSet to generate a list of items by get_queryset. I want to return a json with the list of objects and an extra field (the sum total time of the DurationField).
Example:
{
"total_time":"00:10:00",
"objects":[
{
"pk":1,
"title":"Title",
"duration": "00:05:00"
},
{
"pk":1,
"title":"Title",
"duration": "00:05:00"
}
]
}
How can I do this? Here's my code below.
ModelViewSet:
class ModelViewSet(viewsets.ModelViewSet):
queryset = Model.objects.all()
serializer_class = ModelSerializer
permission_classes = (IsAuthenticated,)
def get_queryset(self):
user = self.request.user
list = Model.objects.filter(user=user)
total_time = .. ## sum the duration of list of objects
## I want to return the list and total_time
return list
serializers.py:
class ModelSerializer(serializers.ModelSerializer):
pk = serializers.IntegerField(read_only=True)
user = serializers.PrimaryKeyRelatedField(queryset=User.objects.all(), required=False, allow_null=True)
project = serializers.PrimaryKeyRelatedField(queryset=Project.objects.all(), required=False, allow_null=True)
class Meta:
model = Model
fields = ('pk', 'title', 'user', 'project', 'duration')
urls:
router = DefaultRouter()
router.register(r'^', views.ModelViewSet)
urlpatterns = [
url(r'^api', include(router.urls)),
]
One way would be to subclass the paginator (the default pagination class is PageNumberPagination) and tweaking the get_paginated_response. This is wholly untested, but maybe something like this:
from rest_framework.pagination import PageNumberPagination
class CustomPageNumberPagination(PageNumberPagination):
def get_paginated_response(self, data, total_time):
return Response(OrderedDict([
('count', self.page.paginator.count),
('next', self.get_next_link()),
('previous', self.get_previous_link()),
('total_time', total_time),
('results', data)
]))
class ModelViewSet(viewsets.ModelViewSet):
queryset = Model.objects.all()
serializer_class = ModelSerializer
permission_classes = (IsAuthenticated,)
pagination_class = CustomPageNumberPagination
def get_queryset(self):
user = self.request.user
list = Model.objects.filter(user=user)
self.total_time = .. ## sum the duration of list of objects
return list
def get_paginated_response(self, data):
return self.paginator.get_paginated_response(data, self.total_time)
Anyhow, hopefully this helps a bit.
Good luck!
Finally I did it! But I'm not sure if it's the best practice. If anyone has a better idea, please share!
class ModelJSONRenderer(JSONRenderer):
"""
Add "objects" and "total_time" in Json
"""
def render(self, data, accepted_media_type=None, renderer_context=None):
data = {'objects': data, 'total_time': renderer_context['total_time']}
return super(ModelJSONRenderer, self).render(data, accepted_media_type, renderer_context)
ModelViewSet:
class ModelViewSet(viewsets.ModelViewSet):
queryset = Model.objects.all()
serializer_class = ModelSerializer
permission_classes = (IsAuthenticated,)
renderer_classes = (ModelJSONRenderer, )
def get_queryset(self):
user = self.request.user
list = Model.objects.filter(user=user)
self.total_time = .. ## sum the duration of list of objects
return list
def get_renderer_context(self):
"""
Returns a dict that is passed through to Renderer.render(),
as the `renderer_context` keyword argument.
"""
return {
'view': self,
'args': getattr(self, 'args', ()),
'kwargs': getattr(self, 'kwargs', {}),
'request': getattr(self, 'request', None),
'total_time': self.total_time
}