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
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
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
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
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)
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
}