My models have multiple attributes, all of which are being serialized.
According to the user's request, I want my API to return only limited information
This is my model's code
from django.db import models
class User(models.Model):
name = models.CharField(max_length=50)
age = models.PositiveSmallIntegerField()
description = models.CharField(max_length=200)
def __str__(self) -> str:
return self.name
This is my Serializer's code
from rest_framework import serializers
from .models import User
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = "__all__"
This is my View's code
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import User
from .serializers import UserSerializer
from rest_framework import serializers
from rest_framework import status
from django.http import JsonResponse
#api_view(['GET'])
def viewUsers(request):
users = User.objects.all()
serializer = UserSerializer(users, many=True)
return JsonResponse({'id': serializer.data['id'], 'name': serializer.data['name']})
The code below works if I want to return a single attribute/field
return JsonResponse(serializer.data['id'])
but when I try to fetch more than one attribute by the dictionary method, it throws this error
list indices must be integers or slices, not str
many=True passed to UserSerializer indicates that serializer.data will contain a list of dicts where each particular dict will be a result of serializing User instance to expected dict
Taking into account serializer.data is a list then if you try to execute serializer.data['id'] it raises
list indices must be integers or slices, not str
You may want to update the return statement to
return JsonResponse([{'id': item['id'] for item in serializer.data}])
or specify that you expect only id field to be returned by the serializer
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ("id",)
and then in the view
return JsonResponse(serializer.data)
Related
I was trying to do put request on Postman and it's successful, but when I try the get method to get same result one of the fields which is car_year shows null.
from django.shortcuts import render
from django.http import HttpResponse,JsonResponse
from rest_framework.parsers import JSONParser
from .models import Driver
from .serializers import DriverSerializer
from django.views.decorators.csrf import csrf_exempt
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
from rest_framework import generics
from rest_framework import mixins
from rest_framework.views import APIView
# Create your views here.
class GenericAPIView(generics.GenericAPIView,mixins.ListModelMixin,mixins.CreateModelMixin,mixins.UpdateModelMixin,mixins.RetrieveModelMixin,mixins.DestroyModelMixin):
serializer_class = DriverSerializer
queryset = Driver.objects.all()
lookup_field = 'id'
def get(self,request,id=None):
if id:
return self.retrieve(request)
else:
return self.list(request)
def post(self,request):
return self.create(request)
def put(self,request,id=None):
return self.update(request,id)
from django.db import models
# Create your models here.
class Driver(models.Model):
name=models.CharField(max_length=100)
email=models.EmailField(max_length=100)
phone_number=models.IntegerField(max_length=None)
car_model=models.CharField(max_length=100)
car_year=models.IntegerField(max_length=None)
def __str__(self):
return self.name
from rest_framework import serializers
from .models import Driver
class DriverSerializer(serializers.ModelSerializer):
class Meta:
model=Driver
fields=['id','name','email','phone_number','car_model','car_year'] ```
Instead of using that very GenericAPIView class that you've made, look into using a ModelViewSet. Besides that, you are missing the permissions_classes in your APIView.
Try this:
from rest_framework import viewsets, permissions
class DriverViewSet(viewsets.ModelViewSet):
queryset = Driver.objects.all()
serializer_class = DriverSerializer
permission_classes = [permissions.IsAuthenticated]
Also, your serializer looks fine.
car_year and phone_number are IntegerFields and you've set the max_length on them. That max_length=None should be removed.
Try the things I mentioned and it should would fine. I'm guessing it's most likely the view you are using but it could also be the integer field.
Also, if this is going to be a production app, the phone_number field shouldn't be an integer but rather a string, or better yet, you can use the django-phonenumber-field library.
I'm extending my current model to have some common properties from other base classes.
Before extending the model, everything was working fine. But after extending, I'm getting the assertion error while performing put and post Operation. I tried my best to resolve it by my own. But not getting where it is going wrong. Can anyone help me on this?
Please find my model and serializers below.
basemodel.py
from django.db import models
class BaseModel(models.Model):
created_at=models.DateTimeField(auto_now=True)
updated_at=models.DateTimeField(auto_now=True)
class Meta:
abstract = True
softdelete.py
from django.db import models
class SoftDeleteModel(models.Model):
is_deleted = models.BooleanField(default=False)
def delete(self):
self.is_deleted = True
self.save()
def restore(self):
self.is_deleted = False
self.save()
class Meta:
abstract = True
movies.py
from django.db import models
from cinimateApp.models.comman.softdelete import SoftDeleteModel
from cinimateApp.models.comman.basemodel import BaseModel
# Create your models here.
class Movies(SoftDeleteModel,BaseModel):
name=models.CharField(max_length=250)
description=models.CharField(max_length=250)
active=models.BooleanField(default=False)
def __str__(self):
return self.name
movieSerializer.py
#Model Serializer
from rest_framework import serializers
from cinimateApp.models.movies import Movies
class MovieSerializer(serializers.ModelSerializer):
class Meta:
model = Movies
fields = '__all__'
# fields=['id', 'name', 'description', 'active']
# exclude=['name']
# field Level Validation
def validate_name(self,value):
if(len(value)<3):
raise serializers.ValidationError('name is too short')
return value
#Objectlevel validation
def validate(self,data):
if(data['name']==data['description']):
raise serializers.ValidationError('name and description should be different')
return
#custome serializer field
name_length=serializers.SerializerMethodField()
def get_name_length(self,object):
return len(object.name)
views.py
from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework import status
from rest_framework.response import Response
from cinimateApp.serializers.movieSerializer import MovieSerializer
from cinimateApp.models.movies import Movies
# Create your views here.
class MovieList(APIView):
def get(self,request):
movies=Movies.objects.all()
serializer=MovieSerializer(movies,many=True)
return Response(serializer.data,status=status.HTTP_200_OK)
def post(self,request):
serialiser=MovieSerializer(data=request.data)
if(serialiser.is_valid()):
serialiser.save()
return Response(serialiser.data,status=status.HTTP_201_CREATED)
else:
return Response(serialiser.errors,status=status.HTTP_500_INTERNAL_SERVER_ERROR)
class MovieDetailsAV(APIView):
def get(self,request,id):
try:
movie=Movies.objects.get(pk=id)
serializer=MovieSerializer(movie)
return Response(serializer.data,status=status.HTTP_200_OK)
except Movies.DoesNotExist:
return Response({'Error':'Movie not found'},status=status.HTTP_404_NOT_FOUND)
def put(self,request,id):
try:
movie=Movies.objects.get(pk=id)
serializer=MovieSerializer(movie,data=request.data)
if(serializer.is_valid()):
serializer.save()
return Response(serializer.data,status=status.HTTP_200_OK)
return Response(serializer.errors,status=status.HTTP_500_INTERNAL_SERVER_ERROR)
except Movies.DoesNotExist:
return Response({'Error':'Movie not found'},status=status.HTTP_404_NOT_FOUND)
def delete(self,request,id):
try:
movie=Movies.objects.get(pk=id)
movie.delete()
return Response({'message' : 'Content deleted successfully'},status=status.HTTP_204_NO_CONTENT)
except Movies.DoesNotExist:
return Response({'Error':'Movie not found'},status=status.HTTP_404_NOT_FOUND)
def patch(self,request,id):
try:
movie=Movies.objects.get(pk=id)
serializer=MovieSerializer(movie,data=request.data,partial=True)
if(serializer.is_valid()):
serializer.save()
return Response(serializer.data,status=status.HTTP_200_OK)
return Response(serializer.errors,status=status.HTTP_500_INTERNAL_SERVER_ERROR)
except Movies.DoesNotExist:
return Response({'Error':'Movie not found'},status=status.HTTP_404_NOT_FOUND)
I'm getting following errors for put and post requests.
assert value is not None, '.validate() should return the validated data'
AssertionError: .validate() should return the validated data
validate() function should return data so:
def validate(self,data):
if(data['name']==data['description']):
raise serializers.ValidationError('name and description should be different')
return data
I want to add a class-based view to return users created from one date to another date. I have tried the following are there other ways to filter the user-created between two dates?
While doing this I get:
TypeError: get() missing 1 required positional argument: 'to_date'
views.py
class RegisteredUserFilter(APIView):
serializer = RegisteredUserFilterSerializer
def get(self, from_date, to_date):
userondate = User.objects.filter(created_at__range=[from_date, to_date]).values()
return Response({"User": userondate})
serializers.py
class RegisteredUserFilterSerializer(serializers.Serializer):
from_date = serializers.DateField()
to_date = serializers.DateField()
model = User
full code at: https://github.com/abinashkarki/rest_framework_authentication/tree/master/authapp
using Django filters
serilaizers.py
add the following
class RegisteredUserFilterSerializer(serializers.Serializer):
class Meta:
model = User
fields = ['username', 'created_at']
make file name it filters.py in same path of views.py
filters.py
from django_filters.rest_framework import FilterSet
import django_filters
from .models import User
from django_filters.widgets import RangeWidget
class UserFilter(FilterSet):
date_range = django_filters.DateFromToRangeFilter(label='Date Range', field_name='created_at',
widget=RangeWidget(attrs={'type': 'date'}))
class Meta:
model = User
fields = ['created_at']
Views.py
from .serializers import RegisteredUserFilterSerializer
from .filters import UserFilter
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.generics import ListAPIView
class RegisteredUserFilter(ListAPIView):
serializer_class = RegisteredUserFilterSerializer
filter_backends = [DjangoFilterBackend]
filterset_class = UserFilter
model = User
urls.py add
from django.urls import path
from .views import RegisteredUserFilter
urlpatterns = [
path('users-filter/', RegisteredUserFilter.as_view(), name="user-filter"),
]
finally do not forget to make
pip install django-filter
and add it to installed app in settings.py
INSTALLED_APPS = [
...
'django_filters',
]
for more information refer to https://django-filter.readthedocs.io/en/stable/guide/rest_framework.html
another approach
you can make this process manually by overriding the get method and getting query parameters manually
and return response after serializing the query set
I have model Product with field MoneyField in /products/models.py
class Product(SeoModel, ModelWithMetadata, PublishableModel):
name = models.CharField(max_length=128)
currency = models.CharField(
max_length=settings.DEFAULT_CURRENCY_CODE_LENGTH,
default=settings.DEFAULT_CURRENCY,
)
price_amount = models.DecimalField(
max_digits=settings.DEFAULT_MAX_DIGITS,
decimal_places=settings.DEFAULT_DECIMAL_PLACES,
)
price = MoneyField(amount_field="price_amount", currency_field="currency")
My views.py is:
from .models import Product
from .serializers import ProductListSerializer
from rest_framework import generics
class ProductList(generics.ListAPIView):
queryset = Product.objects.all()
serializer_class = ProductListSerializer
and serializers.py:
from rest_framework import serializers
from .models import Product
class ProductListSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ['name', 'id']
and when i go to url i have error:
AttributeError at /ru/products/api/
'MoneyField' object has no attribute 'serialize'
Request Method: GET
Request URL: http://127.0.0.1:8000/ru/products/api/
Django Version: 2.2.6
Exception Type: AttributeError
Exception Value:
'MoneyField' object has no attribute 'serialize'
Can you help me? Thank you!
DRF's ModelSerializer assumes fields that are on the model extend django.db.models.fields.Field, which MoneyField does not. This is a problem when ModelSerializer is collecting fields:
# rest_framework.utils.model_meta._get_fields
def _get_fields(opts):
fields = OrderedDict()
for field in [field for field in opts.fields if field.serialize and not field.remote_field]:
fields[field.name] = field
return fields
You can fix the problem by subclassing MoneyField:
from django_prices.models import MoneyField as BaseMoneyField
class MoneyField(BaseMoneyField):
serialize = True
This will probably lead to your next problem ;)
I am having trouble creating a customizable swagger schema in the Django Rest Framework. I have read pages of documentation, but have not found a clear cut example on how to generate swagger annotations in python.
I am aware that swagger/schema documentation is readily generated when using ViewSets in Django. However, I am solely using APIViews and want to write a customized schema. I have tried creating a CoreAPI schema but am unaware of how to implement it. I am enclosing some of my sample code and some screenshots as well. The screen shots go from what I have to what I want.
Sample code:
urls.py
from django.conf.urls import url, include
from rest_framework.urlpatterns import format_suffix_patterns
from Views import SampleView as sv
from rest_framework_swagger.views import get_swagger_view
from rest_framework.documentation import include_docs_urls
from rest_framework.renderers import CoreJSONRenderer
from rest_framework.schemas import get_schema_view
schema_view enter code here= get_swagger_view(
title='Sample API')
urlpatterns = [
url(r'^sample/$', pv.SampleList.as_view()),
url(r'^sample/(?P<id>[a-f\d]{24})/$', sv.SampleDetail.as_view()),
url('^schema/$', schema_view),
]
urlpatterns = format_suffix_patterns(urlpatterns)
views.py
from rest_framework.views import APIView
from Manager.SampleManager import SampleManager as sm
_sampleManager = sm()
class SampleList(APIView):
"""
get:
Return a list of all the existing samples.
post:
Create a new sample.
"""
def get(self, request, format=None):
return _sampleManager.getAll()
def post(self, request, format=None):
return _sampleManager.create( request)
class SampleDetail(APIView):
"""
get:
Get a sample.
put:
Update a sample.
delete:
Delete a sample.
"""
def get(self, request, id, format =None):
return _sampleManager.getById( id)
def put(self, request, id, format =None):
return _sampleManager.update( request, id)
def delete(self, request, id, format =None):
return _sampleManager.deleteById( id)
Serializers.py
from rest_framework_mongoengine.serializers import DocumentSerializer
from .modles import Sample, SampleInner
from Serializers.SampleInnerSerializer import SampleInnerSerializer
class SampleSerializer(DocumentSerializer):
other = SampleInnerSerializer(many=True)
class Meta:
model = Sample
fields = '__all__'
def create(self, validated_data):
samples = validated_data.pop('other')
created_instance = super(SampleSerializer, self).create(validated_data)
for sample_data in samples:
created_instance.other.append(SampleInner(**sample_data))
created_instance.save()
return created_instance
def update(self, instance, validated_data):
samples = validated_data.pop('other')
updated_instance = super(SampleSerializer, self).update(instance, validated_data)
for sample_data in samples:
updated_instance.other.append(SampleInner(**sample_data))
updated_instance.save()
return updated_instance
Schema.py
import coreapi
from rest_framework.decorators import api_view, renderer_classes
from rest_framework import renderers, response
schema = coreapi.Document(
title='Sample API',
content={
'sample': coreapi.Link(
url='/sample/',
action='post',
fields=[
coreapi.Field(
name='from',
required=True,
location='query',
description='City name or airport code.'
),
coreapi.Field(
name='to',
required=True,
location='query',
description='City name or airport code.'
),
coreapi.Field(
name='date',
required=True,
location='query',
description='Flight date in "YYYY-MM-DD" format.'
)
],
description='Create partner'
)
}
)
#api_view()
#renderer_classes([renderers.CoreJSONRenderer])
def schema_view(request):
return response.Response(schema)
There is two solution for you in this senario, One "go with the GenricApiView" Two "Create Custom row Schema"
let's go with
-- >solution one.
urls.py
schema_view = get_swagger_view(title='Test All API')
urlpatterns = [
path('swagger2/', schema_view),
]
view.py
class LoginAPIView(generics.GenericAPIView):
serializer_class = LoginSerializer
permission_classes = [permissions.AllowAny]
def post(self, request):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
return Response(serializer.data, status=status.HTTP_200_OK)
Result
--> Solution two:
urls.py
configuration is same as before
views.py
class BlacklistTokenAdding(APIView):
permission_classes = [permissions.AllowAny]
schema = ManualSchema(fields=[
coreapi.Field(
"first_field",
required=True,
location="body",
schema=coreschema.String()
),
coreapi.Field(
"second_field",
required=True,
location="body",
schema=coreschema.String()
),
])
def post(self, request, format='json'):
try:
refresh_token = request.data["refresh_token"]
token = RefreshToken(refresh_token)
token.blacklist()
return Response(status=status.HTTP_200_OK)
except Exception as e:
return Response(status=status.HTTP_400_BAD_REQUEST)
//Note the first_field & second_field is to demonstration you can add here as much field as you want.
Result