I'm following this tutorial (Django Rest Framework) and I'm facing a problem when I try to use serializers. It returns a empty result, instead the 49086 records that it should to return. My query it's ok because when I try to show the data without serializers it's shows these data. Please, what I'm doing wrong?
models.py
# coding=utf-8
from __future__ import unicode_literals
from django.db import models
class Workers(models.Model):
chapa = models.IntegerField(primary_key=True)
emp_cod = models.IntegerField(primary_key=False)
class Meta:
managed = False
db_table = 'WORKERS'
serializers.py
from rest_framework import serializers
from .models import Workers
class WorkersSerializer(serializers.ModelSerializer):
class Meta:
model = Workers
fields = '__all__'
views.py
...
#api_view(['GET'])
#permission_classes((permissions.AllowAny,))
def get_all_workers(request):
data = Workers.objects.using('rh').all().order_by('emp_cod')
print(data.count()) # Returns 49086
serializer = WorkersSerializer(data)
print(serializer.data) # Returns {}
json = JSONRenderer().render(serializer.data)
return Response(json) # Returns Django Rest standard page with "{}" data
You should use many=True serializer's argument to serialize multiple objects. Also you can pass serializer.data directly as Response argument:
#api_view(['GET'])
#permission_classes((permissions.AllowAny,))
def get_all_workers(request):
data = Workers.objects.using('rh').all().order_by('emp_cod')
serializer = WorkersSerializer(data, many=True)
return Response(serializer.data)
Since your view return so many objects at once, I suggest you to add pagination:
from rest_framework.pagination import PageNumberPagination
#api_view(['GET'])
#permission_classes((permissions.AllowAny,))
def get_all_workers(request):
data = Workers.objects.using('rh').all().order_by('emp_cod')
paginator = PageNumberPagination()
paginator.page_size = 10
result_page = paginator.paginate_queryset(data, request)
serializer = WorkersSerializer(result_page, many=True)
return Response(serializer.data)
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.
Given a Django model with a JSONField, what is the correct way of serializing and deserializing it using Django Rest Framework?
I've already tried crating a custom serializers.WritableField and overriding to_native and from_native:
from json_field.fields import JSONEncoder, JSONDecoder
from rest_framework import serializers
class JSONFieldSerializer(serializers.WritableField):
def to_native(self, obj):
return json.dumps(obj, cls = JSONEncoder)
def from_native(self, data):
return json.loads(data, cls = JSONDecoder)
But when I try to updating the model using partial=True, all the floats in the JSONField objects become strings.
If you're using Django Rest Framework >= 3.3, then the JSONField serializer is now included. This is now the correct way.
If you're using Django Rest Framework < 3.0, then see gzerone's answer.
If you're using DRF 3.0 - 3.2 AND you can't upgrade AND you don't need to serialize binary data, then follow these instructions.
First declare a field class:
from rest_framework import serializers
class JSONSerializerField(serializers.Field):
""" Serializer for JSONField -- required to make field writable"""
def to_internal_value(self, data):
return data
def to_representation(self, value):
return value
And then add in the field into the model like
class MySerializer(serializers.ModelSerializer):
json_data = JSONSerializerField()
And, if you do need to serialize binary data, you can always the copy official release code
In 2.4.x:
from rest_framework import serializers # get from https://gist.github.com/rouge8/5445149
class WritableJSONField(serializers.WritableField):
def to_native(self, obj):
return obj
class MyModelSerializer(serializers.HyperlinkedModelSerializer):
my_json_field = WritableJSONField() # you need this.
If and only if you know the first-level style of your JSON content (List or Dict), you can use DRF builtin DictField or ListField.
Ex:
class MyModelSerializer(serializers.HyperlinkedModelSerializer):
my_json_field = serializers.DictField()
It works fine, with GET/PUT/PATCH/POST, including with nested contents.
serializers.WritableField is deprecated. This works:
from rest_framework import serializers
from website.models import Picture
class PictureSerializer(serializers.HyperlinkedModelSerializer):
json = serializers.SerializerMethodField('clean_json')
class Meta:
model = Picture
fields = ('id', 'json')
def clean_json(self, obj):
return obj.json
Mark Chackerian script didn't work for me, I'd to force the json transform:
import json
class JSONSerializerField(serializers.Field):
""" Serializer for JSONField -- required to make field writable"""
def to_internal_value(self, data):
json_data = {}
try:
json_data = json.loads(data)
except ValueError, e:
pass
finally:
return json_data
def to_representation(self, value):
return value
Works fine. Using DRF 3.15 and JSONFields in Django 1.8
For the record, this "just works" now if you are using PostgreSQL, and your model field is adjango.contrib.postgres.JSONField.
I'm on PostgreSQL 9.4, Django 1.9, and Django REST Framework 3.3.2.
I have previously used several of the other solutions listed here, but was able to delete that extra code.
Example Model:
class Account(models.Model):
id = UUIDField(primary_key=True, default=uuid_nodash)
data = JSONField(blank=True, default="")
Example Serializer:
class AccountSerializer(BaseSerializer):
id = serializers.CharField()
class Meta:
model = Account
fields = ('id','data')
Example View:
class AccountViewSet(
viewsets.GenericViewSet,
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.ListModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin
):
model = Account
queryset = Account.objects.all()
serializer_class = AccountSerializer
filter_fields = ['id', 'data']
Thanks by the help. This is the code i finally use for render it
class JSONSerializerField(serializers.Field):
"""Serializer for JSONField -- required to make field writable"""
def to_representation(self, value):
json_data = {}
try:
json_data = json.loads(value)
except ValueError as e:
raise e
finally:
return json_data
def to_internal_value(self, data):
return json.dumps(data)
class AnyModelSerializer(serializers.ModelSerializer):
field = JSONSerializerField()
class Meta:
model = SomeModel
fields = ('field',)
If you're using mysql (haven't tried with other databases), using both DRF's new JSONField and Mark Chackerian's suggested JSONSerializerField will save the json as a {u'foo': u'bar'} string.
If you rather save it as {"foo": "bar"}, this works for me:
import json
class JSONField(serializers.Field):
def to_representation(self, obj):
return json.loads(obj)
def to_internal_value(self, data):
return json.dumps(data)
DRF gives us inbuilt field 'JSONField' for binary data, but JSON
payload is verified only when you set 'binary' flag True then it convert into utf-8 and load the JSON payload, else it only
treat them as string(if invalid json is sent) or json and validate both
without error even though you cretaed JSONField
class JSONSerializer(serializers.ModelSerializer):
"""
serializer for JSON
"""
payload = serializers.JSONField(binary=True)
To serialize a data from a request you can use the serializers.ModelSerializer
serializers.py
from rest_framwork import serializers
class FinalSerializer(serializers.ModelSerializer):
class Meta:
model=Student
fields='__all__'
views.py
import io
from yourappname.serializers import FinalSerializer #replace your app name
from rest_framework.parsers import JSONParser
from rest_framework.views import APIView
from rest_framework.parsers import JSONParser,MultiPartParser,FormParser
from rest_framework.response import Response
class DataList(APIView):
parser_classes = (JSONParser,MultiPartParser,FormParser) #If you are using postman
renderer_classes = (JSONRenderer,)
#Serialize
def get(self,request,format=None):
all_data=Student.objects.all()
serializer=FinalSerializer(all_data,many=True)
return Response(serializer.data)#Will return serialized json data,makes sure you have data in your model
#Deserialize
#Not tried this function but it will work
#from django documentation
def djson(self,request,format=None):
stream = io.BytesIO(json)
data = JSONParser().parse(stream)
serializer = FinalSerializer(data=data)
serializer.is_valid()
serializer.validated_data
If you want JSONField for mysql this is done in django-mysql and serializer was fixed some day ago [1], is not yet in any release.
[1] https://github.com/adamchainz/django-mysql/issues/353
setting.py
add:
'django_mysql',
models.py
from django_mysql.models import JSONField
class Something(models.Model):
(...)
parameters = JSONField()
I want to serialize the data
class Main(models.Model):
title = models.CharField()
class ForeignKey(models.Model):
main = models.ForeignKey(Main,on_related='foreign_key')
I want to get the result like
{'title':'hello', 'foreign_key':'['foreign_key1','foreign_key2','foreign_key3']'
I first thought I could make it work in the 'views.py', but some documents told me that there is a thing like 'listField' in serializer, which has too little examples..
What would be the way here?
Write your serializer as ,
#serializers.py
from rest_framework import serializers
class ForeignKeySerializer(serializers.ModelSerializer):
class Meta:
model = ForeignKey
fields = '__all__'
class MainSerializer(serializers.ModelSerializer):
foreign_key = ForeignKeySerializer(source='foreignkey_set', many=True)
class Meta:
model = Main
fields = ("title", "foreign_key")
then write your views as,
#views.py
from rest_framework.decorators import api_view
from rest_framework.response import Response
#api_view()
def sample_view(request):
queryset = Main.objects.all()
serializer = MainSerializer(queryset, many=True)
return Response(data=serializer.data)
References
DRF-Serializer
DRF-views
I'm trying to create a new entry in my database with Django REST framework and I am able to successfully send a request however validated_data is empty:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.shortcuts import render
from rest_framework import viewsets
from django.shortcuts import get_object_or_404, get_list_or_404
from .models import GetPurchases, CarFax
from .serializers import PurchasesSerializer
from .serializers import CarFaxSerializer
from rest_framework.response import Response
# Create your views here.
class GetCarFax(viewsets.ModelViewSet):
''' This view will be used for POSTing new carfax reports to the database '''
queryset = CarFax.objects.all()
serializer_class = CarFaxSerializer
# authentication_classes = []
permission_classes = []
#print('TEST')
def list(self, request):
# accessed at url: ^api/v1/carfax/$
queryset = CarFax.objects.all()
serializer = CarFaxSerializer(queryset, many=True)
return Response(serializer.data)
def retrieve(self, request, pk=None, *args, **kwargs):
# accessed at url: ^api/v1/retrieve/{pk}/$
queryset = CarFax.objects.all()
record = get_list_or_404(queryset, vin__exact=pk)
serializer = CarFaxSerializer(record, many=True)
return Response(serializer.data)
def create(self, request, **validated_data):
print('TEST')
print(request.data)
print(validated_data)
serializer = CarFaxSerializer(CarFax.objects.create(**validated_data))
headers = self.get_success_headers(serializer.data)
print(serializer.data)
print(headers)
return Response(serializer.data, headers=headers)
serializers.py
class CarFaxSerializer(serializers.ModelSerializer):
class Meta:
model = CarFax
fields = ('vin', 'structural_damage', 'total_loss',
'accident', 'airbags', 'odometer', 'recalls',
'last_updated')
When I print request.data I can see the data I sent. However validated_data is empty, which is the dict I'm using to create the models.
The accepted answer is a nice refactoring but it doesn't answer the question, and thus may not help future readers (like myself, who just went through this). The real reason validated data was empty was because the when you override .create on the ModelViewSet you wipe out the serializer model field bindings and have to explicitly specify them as you would on a regular serializer.
CarFax serializer should have been defined like so:
class CarFaxSerializer(serializers.ModelSerializer):
vin = serializers.CharField(23),
structural_damage = serializers.BooleanField(),
total_loss = serializers.BooleanField(),
accident = serializers.BooleanField(),
airbags = serializers.BooleanField(),
odometer = serializers.IntegerField(),
recalls = serialziers.BooleanField(),
last_updated = serializers.DateField(),
class Meta:
model = CarFax
fields = ('vin', 'structural_damage', 'total_loss',
'accident', 'airbags', 'odometer', 'recalls',
'last_updated')
First of all, your GetCarFax class seems like messy :( because you'd override most of the methods, but it doesn't provide any advantages to the view. So change your GetCarFax view class to below,
class GetCarFax(viewsets.ModelViewSet):
"""
This view will be used for POSTing new carfax reports to the database
"""
queryset = CarFax.objects.all()
serializer_class = CarFaxSerializer
# authentication_classes = []
permission_classes = []
lookup_field = 'myfieldname'
This few lines of code will handle all the CRUD operations for you :)
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