Is it possible to use a django viewset with two serializers? - python

I have a model for Articles which has a lot of fields. When someone fetches a specific article by id I want to send almost all of the fields back to the client.
However, when the articles list is generated I don't want to send all of the articles with all of that data, but rather limit each article to a few important listing fields, and forgo long fields like content etc..
Can I achieve these with an elegant tweak to the the django_restframework.viewsets module, or should I just build the api methods myself using django_restframwork.generics instead?
example:
# articles/api/urls.py #
# -------------------- #
from articles.api.views import ArticlesViewSet
from django.urls import path
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'', ArticlesViewSet, basename='articles')
urlpatterns = router.urls
# articles/api/views.py #
# --------------------- #
from rest_framework.viewsets import ModelViewSet
from ..models import Article
from .serializers import ArticleSerializerFull, ArticleSerializerShort
class ArticlesViewSet(ModelViewSet):
# Perhaps some conditional code here?
serializer_class = ArticleSerializerFull
queryset = Article.objects.all()

You can use get_serializer_class() method in DRF to solve the problem...
serializers.py
class ArticleFullSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = ('__all__')
class ArticleShortSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = ('field1', 'field2' ... 'fieldn')
views.py
from rest_framework.viewsets import ModelViewSet
class ArticleViewSet(ModelViewSet):
serializer_class = ArticleSerializerFull
queryset = Article.objects.all()
def get_serializer_class(self):
if self.action == 'list':
serializer = ArticleShortSerializer
else:
serializer = ArticleFullSerializer
return serializer

**i believe you can achieve this by using generics views check_here **
class ArticleFullSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = ('__all__')
class ArticleShortSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = ('field1', 'field2'..... 'fieldn')
and in your views
class ArticleListView(ListAPIView):
permission_classes = (permissions.IsAuthenticated,)
queryset = Article.objects.all()
serializer_class = ArticleShortSerializer
class ArticleDetailView(RetrieveAPIView):
permission_classes = (permissions.IsAuthenticated,)
queryset = Article.objects.all()
serializer_class = ArticleFullSerializer
lookup_field = 'pk'
****and in urls for details****
path('detail/<int:pk>/', ArticleDetailView.as_view(), name='article-detail'),

Related

Getting an empty list when using filter - Django REST

I have my API in Django REST Framework:
Here is my models.py:
class myModel(models.Model):
user_email = models.CharField(max_length= 200, null= False)
Here is my views.py:
class GetItemsByEmail(generics.ListAPIView):
def get_queryset(self):
email_items = self.request.query_params.get("user_email")
if(email_items is not None):
itemsReturned = myModel.objects.all().filter(user_email = email_items)
return Response(data= itemsReturned)
Here is my urls.py:
url_patterns = [
path('users/account=<str:id>/shipments', GetItemsByEmail.as_view()),
]
My Question:
I am getting an empty list, getting nothing from making an API call to the above endpoint.
I want to get all the items in the database associated with a particular email?
In your views.py:
from rest_framework import generics
from .models import * # noqa
from .serializers import *
class GetItemsByEmail(generics.ListAPIView):
queryset = MyModel.objects.all() # noqa
serializer_class = MyModelSerializer
def get_queryset(self):
if self.kwargs.get('user_email_pk'):
return self.queryset.filter(id=self.kwargs.get('user_email_pk'))
return self.queryset.all()
In models.py I had to create another model to have the result that you want (get all database by a specific user_email!):
from django.db import models
class MyModel(models.Model):
user_email = models.CharField(max_length=200, null=False)
def __str__(self):
return self.user_email
class ServicesModel(models.Model):
# Just an example to emulate the expected result, do not worry about it!
name = models.CharField('Name', max_length=200)
user_email_service = models.ForeignKey(MyModel, related_name='services', on_delete=models.CASCADE) # Just an example to emulate the expected result, do not worry about it!
def __str__(self):
return self.name
In serializers.py:
from rest_framework import serializers
from .models import MyModel, ServicesModel
class ServiceModelSerializer(serializers.ModelSerializer):
class Meta:
model = ServicesModel
fields = (
'name',
)
class MyModelSerializer(serializers.ModelSerializer):
services = ServiceModelSerializer(many=True, read_only=True)
class Meta:
model = MyModel
fields = (
'id',
'user_email',
'services',
)
In urls.py:
from django.urls import path
from core.views import GetItemsByEmail
urlpatterns = [
path('users/', GetItemsByEmail.as_view(), name='users'), # Ignore!
path('users/account=<str:user_email_pk>/shipments/', GetItemsByEmail.as_view(), name='user_email'),
]
In the test that I made localy I created two 'user_email' and each one have diferent 'services' so you are able to get all the data by the id, images of the result:
You obviously only need to get attention in 'views.py' and 'serializers.py', I just created all this code to get in the expected result!
If you want your query to be case insensitive, you can try the following:
myModel.objects.filter(user_email__iexact=email_items)

Why django rest api root does not list classic endpoints

I have registerd some of my endpoints through routers and written others in classic style, by classic style I mean that they are not registerd through router but directly written in path. Now problem is that api root lists only the endpoints that are registered through router and i want both to be listed.
app/views.py
from uuid import UUID
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from event.models import Event
from event.serializers import EventSerializer
from food_album.models import FoodAlbums
from food_album.serializers import FoodAlbumsSerializer
from restaurant.models import RestaurantProfile
from restaurant.serializers import RestaurantProfileSerializer
from .serializers import *
customer_favourites = {
0: [RestaurantProfile, RestaurantProfileSerializer],
1: [Event, EventSerializer],
2: [FoodAlbums, FoodAlbumsSerializer]
}
class CustomerProfileViewSet(viewsets.ModelViewSet):
"""
A viewset that provides the standard actions
"""
queryset = CustomerProfile.objects.all()
serializer_class = CustomerProfileSerializer
lookup_field = 'public_id'
def get_serializer_class(self):
if self.action in ['create', 'update']:
return CustomerProfileCreateSerializer
return self.serializer_class
class RewardTypesViewSet(viewsets.ModelViewSet):
"""
A viewset that provides the standard actions
"""
queryset = RewardTypes.objects.all()
serializer_class = RewardTypesSerializer
lookup_field = 'public_id'
class RewardActionsViewSet(viewsets.ModelViewSet):
"""
A viewset that provides the standard actions
"""
queryset = RewardActions.objects.all()
serializer_class = RewardActionsSerializer
lookup_field = 'public_id'
class ReportItemViewSet(viewsets.ModelViewSet):
"""
A viewset that provides the standard actions
"""
queryset = ReportItem.objects.all()
serializer_class = ReportItemSerializer
lookup_field = 'public_id'
class CustomerFavouritesViewSet(viewsets.ModelViewSet):
"""
A viewset that provides the standard actions
"""
serializer_class = CustomerFavouritesSerializer
permission_classes = (IsAuthenticated,)
lookup_field = 'public_id'
def perform_create(self, serializer):
serializer.save(customer=self.request.user.customer)
def get_queryset(self):
user = self.request.user
if user.customer:
return CustomerFavourites.objects.filter(customer=user.customer)
else:
return None
class UserFeedsViewSet(viewsets.ViewSet):
"""
A viewset that provides the standard actions
"""
serializer_class = RestaurantProfileSerializer
def retrieve(self, request):
r = RestaurantProfile.objects.all()
serializer = RestaurantProfileSerializer(r, many=True)
return Response(serializer.data)
class CustomerBookmarkViewSet(viewsets.ViewSet):
"""
A viewset that provides the standard actions
"""
permission_classes = (IsAuthenticated,)
def list(self, request, reference_type):
bookmarks = CustomerFavourites.objects.filter(type=0,
reference_type=reference_type).values_list(
'reference_id', flat=True)
public_ids = [UUID(id_) for id_ in bookmarks]
r = customer_favourites.get(reference_type)[0].objects.filter(
public_id__in=public_ids).all()
serializer = customer_favourites.get(reference_type)[1](r, many=True)
return Response(serializer.data)
class CustomerFavouriteViewSet(viewsets.ViewSet):
"""
A viewset that provides the standard actions
"""
permission_classes = (IsAuthenticated,)
def list(self, request, reference_type):
favs = CustomerFavourites.objects.filter(type=1,
reference_type=reference_type).values_list(
'reference_id', flat=True)
public_ids = [UUID(id_) for id_ in favs]
r = customer_favourites.get(reference_type)[0].objects.filter(
public_id__in=public_ids).all()
serializer = customer_favourites.get(reference_type)[1](r, many=True)
return Response(serializer.data)
app/urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from . import views
router = DefaultRouter()
router.register(r'profile', views.CustomerProfileViewSet,
base_name='profile')
router.register('reward-actions', views.RewardActionsViewSet, base_name='reward_actions')
router.register('reward-types', views.RewardTypesViewSet, base_name='reward_types')
router.register('report-item', views.ReportItemViewSet, base_name='report_item')
router.register('toggle-favourites', views.CustomerFavouritesViewSet, base_name='toggle_favourites')
urlpatterns = [
path('customer/', include(router.urls)),
path('customer/user-feeds/',
views.UserFeedsViewSet.as_view({'get': 'retrieve'})),
path('customer/favourites/<int:reference_type>/',
views.CustomerFavouriteViewSet.as_view({'get': 'list'})),
path('customer/bookmarks/<int:reference_type>/',
views.CustomerBookmarkViewSet.as_view({'get': 'list'}))
]
You need to append router.urls to the list of existing views as
urlpatterns += router.urls. For more information, kindly look into - https://www.django-rest-framework.org/api-guide/routers/

Django Rest Framework Filtering Not working

Using Django 1.11 & djangorestframework==3.7.7, I want to return Videos where is_thumbnail=True on a GET call. However, all of my testing with filters have been returning all Videos objects.
Model:
class Videos(models.Model):
"""This class represents the Videos model."""
uid = models.UUIDField(
primary_key=True, default=uuid.uuid4, editable=False)
is_thumbnail = models.BooleanField(default=False)
file_name = models.CharField(unique=True, max_length=64)
file_path = models.CharField(unique=True, max_length=256)
file_created_time = models.DateTimeField()
owner = models.ForeignKey('auth.User',
related_name='videos',
on_delete=models.CASCADE)
created_time = models.DateTimeField(auto_now_add=True)
def __str__(self):
"""Return a human readable representation of the model instance."""
return "{}".format(self.file_name)
view:
class DetailsView(generics.RetrieveUpdateDestroyAPIView):
"""This class handles the http GET, PUT and DELETE requests."""
serializer_class = VideosSerializer
permission_classes = (permissions.IsAuthenticated, IsOwner)
lookup_field = 'uid'
def get_queryset(self):
return Videos.objects.filter(is_thumbnail=True)
If I put a print statement inside of the get_queryset function, I don't see that statement in the log. So it seems like the function isn't being called.
api/urls.py
urlpatterns = {
url(r'^auth/', include('rest_framework.urls',
namespace='rest_framework')),
url(r'^api/videos/$', CreateView.as_view(), name="create"),
url(r'^api/videos/(?P<uid>[0-9a-f-]+)/$',
DetailsView.as_view(), name="details"),
url(r'^get-token/', obtain_auth_token),
}
securedash_project/urls.py
urlpatterns = format_suffix_patterns(urlpatterns)
urlpatterns = [
url(r'', include('secureDash.dash.urls')),
url(r'^dash/', include('secureDash.dash.urls')),
url(r'^admin/', admin.site.urls),
url(r'^', include('secureDash.api.urls')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Ok, you want to replace the value of the "get_queryset" method of the generic view "generics.RetrieveUpdateDestroyAPIView", which accesses the objects using the "uid" field setting like lookup_field to make the modification later, so I recommend that when you overwrite it the "get_queryset" method replaces them with the following:
def get_queryset(self):
return self.get_queryset().filter(is_thumbnail=True)
The issue that I was having was that I had a CreateView class that implemented generics.ListCreateAPIView. This view has r/w endpoints so my GET calls were never getting to my DetailsView. I need to adjust my views, but for now this works to only show is_thumbnail=True Videos.
class CreateView(generics.ListCreateAPIView):
"""This class defines the (GET & POST) behavior of the rest api."""
serializer_class = VideosSerializer
permission_classes = (permissions.IsAuthenticated, )
def perform_create(self, serializer):
"""Save the post data when creating a new Videos object."""
serializer.save(owner=self.request.user)
def get_queryset(self):
queryset = Videos.objects.all()
return queryset.filter(is_thumbnail=True)
class DetailsView(generics.RetrieveUpdateDestroyAPIView):
"""This class handles the http GET, PUT and DELETE requests."""
queryset = Videos.objects.all()
serializer_class = VideosSerializer
permission_classes = (permissions.IsAuthenticated, IsOwner)
lookup_field = 'uid'

Django-Rest-Framework Relationships & Hyperlinked API issues

I am having a go at the django-rest-framework. It was all going fine until I got to the Relationships & Hyperlinked API part of the tutorial. The error I am getting now after messing with it for a bit is:
ImproperlyConfigured at /api/users/ "^\.(?P<format>[a-z0-9]+)\.(?P<format>[a-z0-9]+)$" is not a valid regular expression: redefinition of group name u'format' as group2; was group 1
I tried doing some research into this but can't seem to find anything and more I mess with it the more that goes wrong
Heres my code:
modules.py
class Home(models.Model):
user = models.ForeignKey(User)
#address ect
serializers.py
class UserSerializer(serializers.HyperlinkedModelSerializer):
username = serializers.HyperlinkedRelatedField(many=True, read_only=True, view_name='home-detail')
class Meta:
model = User
fields = ('url', 'username', 'home')
class HomeSerializer(serializers.HyperlinkedModelSerializer):
owner = serializers.Field(source='owner.username')
highlight = serializers.HyperlinkedIdentityField(view_name='home-highlight', read_only=True, format='html')
class Meta:
model = Home
fields = ('url', 'owner', 'postcode')
api.py
#api_view(('GET',))
def api_root(request, format=None):
return Response({
'users': reverse('user-list', request=request, format=format),
'homes': reverse('home-list', request=request, format=format)
})
class UserList(generics.ListCreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
class UserDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
class HomeList(generics.ListCreateAPIView):
queryset = Home.objects.all()
serializer_class = HomeSerializer
class HomeDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Home.objects.all()
serializer_class = HomeSerializer
class HomeHighlight(generics.GenericAPIView):
queryset = Home.objects.all()
renderer_classes = (renderers.StaticHTMLRenderer,)
def get(self, request, *args, **kwargs):
snippet = self.get_object()
return Response(snippet.highlighted)
urls.py
urlpatterns = format_suffix_patterns([
url(r'^$', api.api_root),
url(r'^users/$',
api.UserList.as_view(),
name='user-list'),
url(r'^users/(?P<pk>[0-9]+)/$',
api.UserDetail.as_view(),
name='user-detail'),
url(r'^home/$',
api.HomeList.as_view(),
name='home-list'),
url(r'^home/(?P<pk>[0-9]+)/$',
api.HomeDetail.as_view(),
name='home-detail'),
url(r'^home/(?P<pk>[0-9]+)/highlight/$',
api.HomeHighlight.as_view(),
name='home-highlight')
])
urlpatterns += [
url(r'^api-auth/', include('rest_framework.urls',
namespace='rest_framework')),
]
urlpatterns = format_suffix_patterns(urlpatterns)
You are calling format_suffix_patterns twice, so Django has no idea how to parse the URL because there are two format groups.
You shouldn't need the first call, as the second call takes care of it for you (and allows for TokenAuthentication to still have the suffixes).
Atleast change router.DefaultRouter() to router.SimpleRouter() in urls.py file

How to find object by its id in Django Rest Framework

I can't have object by its id in Django Rest Framework. I have a such model:
class BlogPost(models.Model):
title = models.CharField(max_length=128)
content = models.TextField()
created = models.DateTimeField(auto_now_add=True)
Then I write a serializer:
class BlogPostSerializer(serializers.ModelSerializer):
class Meta:
model = BlogPost
fields = ('title', 'content', 'created')
In my views.py I have this:
class BlogPostListFilter(dajngo_filter.FilterSet):
blog_post_id = django_filters.NumerFilter(name = 'id')
class Meta:
model = BlogPost
fiields = ['blog_post_id']
class BlogPostList(generics.ListCreateAPIView):
queryset = BlogPost.objects.all()
serializer_class = BlogPostSerializer
permission_classes = (AllowAny,)
filter_class = BlogPostListFilter
paginate_by = 100
And such code in my urls:
url(r'^blogpost/$', ListCreateAPIView.as_view(model=BlogPost), name='blogpost-list'),
But when I write in browser http://example.com/blogpost/?blog_post_id=1 I have all objects
If you really want to use ListCreateAPIView. You need to make some changes:
urls.py:
url(r'^blogpost/(?P<post_id>\w+)$', views.BlogPostList.as_view(),name='blogpost-list'),
views.py
class BlogPostList(generics.ListCreateAPIView):
serializer_class = BlogPostSerializer
permission_classes = (AllowAny,)
filter_class = BlogPostListFilter
paginate_by = 100
def get_queryset(self):
queryset = BlogPost.objects.filter(pk=self.kwargs['post_id'])
return queryset
But I think that Django Rest Framework provides better Class Based Views for your use case, such as RetrieveAPIView. As far as I understand, it seems that you just want to get an object, and this generic view is for a list of objects.
In my case, I stumbled upon this question looking to access the object id while overriding a ModelViewSet's retrieve method. After some research and experimentation, I discovered that the object id is stored in a dictionary called self.kwargs in the 'pk' key.
I am using djangorestframework==3.11.0.
class MealItemViewSet(viewsets.ModelViewSet):
queryset =MyModel.objects.all()
serializer_class = serializers.MyModelSerializer
def retrieve(self, request, *args, **kwargs):
# The Primary Key of the object is passed to the retrieve method through self.kwargs
object_id = self.kwargs['pk']
I hope this answer helps another forlorn StackOverflow wanderer at some point!

Categories