Django 1.11.2 serializer nested json array - django

I am new to both Python and Django and I would appreciate some guidance with a problem I'm having with Django REST, nested JSON and the serializer.
I wish to post:
{ "Server": [
{
"serverSerialNumber": "0000",
"serverUniqueKey": "2222"
},
{
"serverSerialNumber": "0001",
"serverUniqueKey": "2223"
}
]
}
This is my serializer:
from django.contrib.auth.models import User, Group
from rest_framework import serializers
from .models import Api
class ApiSerializer(serializers.ModelSerializer):
"""Serializer to map the Model instance into JSON format."""
class Meta:
"""Meta class to map serializer's fields with the model fields."""
model = Api
fields = ('id', 'serverSerialNumber', 'serverUniqueKey', 'date_created', 'date_modified')
read_only_fields = ('date_created', 'date_modified')
depth = 1
I simply receive the following back:
{
"serverSerialNumber": [
"This field is required."
]
}
So I am not understanding how to use 'depth' or I'm doing something silly.
Adding View per request:
from django.shortcuts import render
from django.contrib.auth.models import User, Group
from rest_framework import viewsets
from rest_framework import generics
from .serializers import ApiSerializer
from .models import Api
class CreateView(generics.ListCreateAPIView):
"""This class defines the create behavior of our rest api."""
queryset = Api.objects.all()
serializer_class = ApiSerializer
def perform_create(self, serializer):
"""Save the post data when creating a new item."""
serializer.save()
Ok, so I've stumbled through some documentation and had another bash at this.
Still not working but the code seems to make more sense, here is the new code and error:
serializers.py
from django.contrib.auth.models import User, Group
from rest_framework import serializers
from .models import Blade, Servers
class BladeSerializer(serializers.ModelSerializer):
class Meta:
model = Blade
fields = ('id', 'serverSerialNumber', 'serverUniqueKey', 'date_created', 'date_modified')
read_only_fields = ('date_created', 'date_modified')
class ServersSerializer(serializers.ModelSerializer):
Server = BladeSerializer(many=True)
class Meta:
model = Servers
fields = ['Server']
def create(self, validated_data):
servers_data = validated_data.pop('Server')
srv = Servers.objects.create(**validated_data)
for server_data in servers_data:
Blade.objects.create(srv=srv, **server_data)
return srv
views.py
from django.shortcuts import render
from django.contrib.auth.models import User, Group
from rest_framework import viewsets
from api.serializers import UserSerializer, GroupSerializer
from rest_framework import generics
from .serializers import BladeSerializer, ServersSerializer
from .models import Blade, Servers
class CreateView(generics.ListCreateAPIView):
queryset = Servers.objects.all()
serializer_class = ServersSerializer
def perform_create(self, serializer):
serializer.save()
models.py
from django.db import models
from inventory.models import Server
class Blade(models.Model):
instanceId = models.CharField(max_length=255, blank=True, unique=False)
chassisUniqueKey = models.CharField(max_length=255, blank=True, unique=False)
serverUniqueKey = models.CharField(max_length=255, blank=True, unique=False)
serverSerialNumber = models.CharField(max_length=255, blank=False, unique=True)
date_created = models.DateTimeField(auto_now_add=True)
date_modified = models.DateTimeField(auto_now=True)
def __str__(self):
return "{}".format(self.name)
class Servers(models.Model):
Servers = models.CharField(max_length=10, blank=True, unique=False)
def __str__(self):
"""Return a human readable representation of the model instance."""
return "{}".format(self.name)
The error
Got AttributeError when attempting to get a value for field Server on serializer ServersSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the Servers instance.
Original exception text was: 'Servers' object has no attribute 'Server'.

Try this,
class CreateView(generics.ListCreateAPIView):
queryset = Api.objects.all()
serializer_class = ApiSerializer
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data, many=True)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
def perform_create(self, serializer):
serializer.save()

Related

How to POST multiple data in DRF and React with Axios

I have 2 models named Recipe and Step..
I have serialized both to make an API for GET request.. I want to know is there a way to create for POST request so that I can send both the data (steps and recipe) in the same request?
models.py:
from django.db import models
class Recipe(models.Model):
title = models.CharField( max_length=50)
uuid = models.CharField( max_length=100)
def __str__(self):
return f'{self.uuid}'
class Step(models.Model):
step = models.CharField(max_length=300)
uuid = models.ForeignKey(Recipe, on_delete=models.CASCADE)
def __str__(self):
return f'{self.step} - {self.uuid}'
serializers.py:
from rest_framework import serializers
from .models import *
class RecipeSerializer(serializers.ModelSerializer):
class Meta:
model = Recipe
fields = ['title', 'uuid']
class StepSerializer(serializers.ModelSerializer):
class Meta:
model = Step
fields = ['step', 'uuid']
views.py:
from django.shortcuts import render
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .serializers import *
from .models import *
#api_view(['GET'])
def apiOverview(request):
api_urls = {
'List':'/recipe-list/',
'Detail View':'/recipe-detail/<str:pk>/',
'Create':'/recipe-create/',
'Update':'/recipe-update/<str:pk>/',
'Delete':'/recipe-delete/<str:pk>/',
'Steps' : '/steps/<str:pk>'
}
return Response(api_urls)
#api_view(['GET'])
def recipeList(request):
recipes = Recipe.objects.all()
serializer = RecipeSerializer(recipes, many=True)
return Response(serializer.data)
#api_view(['GET'])
def recipeDetail(request, pk):
recipe = Recipe.objects.get(uuid=pk)
recipe_serializer = RecipeSerializer(recipe, many=False)
steps = Step.objects.filter(uuid=pk)
steps_serializer = StepSerializer(steps, many=True)
return Response({
'recipe' : recipe_serializer.data,
'steps' : steps_serializer.data
})
How can I create a view for POST and handle both the models?
Try:
from rest_framework import generics
from .models import *
class StepAndRecipe(generics.CreateAPIView):
queryset = Step.objects.all()
queryset = Recipe.objects.all()
serializer_class = StepSerializer
serializer_class = RecipeSerializer
Add in urls.py:
from django.urls import path
from .views import StepAndRecipe
urlpatterns = [
path('steprecipepost', StepAndRecipe.as_view(), name='steps_recipes')
This will only work with POST! And one more thing: take care with the raw data and the HTML form, maybe theses get a little confused since you are using two models in the same view.

I want to filter my product on the bases of rating, How can i do that?

My Models
from django.db import models
from django.contrib.auth.models import User
from django.core.validators import MaxValueValidator, MinValueValidator
# Create your models here.
class Product(models.Model):
title = models.CharField(max_length=100)
description = models.TextField()
def no_of_ratings(self):
ratings = Rating.objects.filter(product=self)
return len(ratings)
def avg_rating(self):
ratings = Rating.objects.filter(product=self)
sum=0
for rating in ratings:
sum += rating.rating
if len(ratings)>0:
return sum/len(ratings)
else:
return None
def __str__(self):
return self.title
class Rating(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
rating = models.IntegerField(validators=[MinValueValidator(1), MaxValueValidator(5)])
class Meta:
unique_together = (('user', 'product'),)
index_together = (('user', 'product'),)
serializer.py
from rest_framework import serializers
from django.contrib.auth.models import User
from rest_framework.authtoken.models import Token
from .models import Product, Rating
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'password')
extra_kwargs = {'password': {'write_only': True, 'required': True}}
def create(self, validated_data):
user = User.objects.create_user(**validated_data)
Token.objects.create(user=user)
return user
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields =('id', 'title', 'description', 'no_of_ratings', 'avg_rating')
class RatingSerializer(serializers.ModelSerializer):
class Meta:
model = Rating
fields = ('product', 'user', 'rating')
views.py
from django.shortcuts import render
from rest_framework import viewsets, status
from rest_framework.authentication import TokenAuthentication
from rest_framework.response import Response
from rest_framework.decorators import action
from django.contrib.auth.models import User
from .models import Rating, Product
from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.authtoken.models import Token
from rest_framework.permissions import IsAuthenticated, AllowAny
from .serializers import ProductSerializer, RatingSerializer
class ProductViewSet(viewsets.ModelViewSet):
queryset = Product.objects.filter(product__rating__gte = 4 )
serializer_class = ProductSerializer
authentication_classes = (TokenAuthentication,)
permission_classes = (AllowAny,)
class RatingViewSet(viewsets.ModelViewSet):
queryset = Rating.objects.filter(rating__gte = 4)
serializer_class = RatingSerializer
authentication_classes = (TokenAuthentication,)
permission_classes = (AllowAny,)
Hi, I'm new in Django Rest Framework, I want to filter my product on the bases of rating, but my filter doesn't work, I have two models classes Product and Rating every Product have a rating( Foreign key ), I want to list only +4 rated product, how can I achieve that, and can I filter results to get models with specific rating?
With queryset = Product.objects.filter(product__rating__gte = 4 ) you look up products and filter them by their field product but your product instances don't have this field or property.
In your views.ProducViewSet You should filter for something like
class ProductViewSet(viewsets.ModelViewSet):
queryset = Product.objects.filter(rating_set__rating__gte = 4 )
# ...
instead. rating_set is the default name to access foreign keys in the other direction (from the model where it's not defined in) You can use the related_name parameter in the ForeignKey to set this to a custom name like product_rating. (Notice the use of single _, it wouldn't work with double __)

Django Restful API Design Validation Logic

Here I have an endpoint to create media content for users. The endpoint works, but I have a feeling my design implementation is incorrect.
Should validation logic be contained in serializers create? Is this bad practice? I attempted to move validation logic to models.py, but ran into issues with accessing the model, specifically this line - self.model(user=user, category=category).
view.py
from rest_framework import status
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from .models import UserMedia
from .renderers import UserMediaSerializerJSONRenderer
from .serializers import UserMediaSerializer
class UserMediaCreateAPIView(APIView):
permission_classes = (IsAuthenticated,)
renderer_classes = (UserMediaSerializerJSONRenderer,)
serializer_class = UserMediaSerializer
def post(self, request):
userMedia = request.data.get('userMedia', {})
serializer = self.serializer_class(data=userMedia)
serializer.is_valid(raise_exception=True)
serializer.save(user=request.user, category=userMedia['category'])
return Response(serializer.data, status=status.HTTP_201_CREATED)
serializers.py
from rest_framework import serializers
from .models import UserMedia
class UserMediaSerializer(serializers.ModelSerializer):
category = serializers.CharField(allow_blank=False, required=True)
class Meta:
model = UserMedia
fields = ('category',)
read_only_fields = ('category',)
def get_category(self, obj):
if obj.category:
return obj.category
return 'N/A'
def create(self, validated_data):
if validated_data['user'] is None:
raise TypeError('User media must have a user')
if validated_data['category'] is None:
raise TypeError('User media must have a category.')
if validated_data['category'] not in dict(UserMedia.CATEGORY_CHOICES):
raise TypeError('User media category is not available.')
userMedia = UserMedia(**validated_data)
userMedia.save()
return userMedia
models.py
from django.db import models
class UserMedia(models.Model):
user = models.ForeignKey('authentication.User', on_delete=models.CASCADE, related_name='media')
MUSIC = 'M'
VIDEO = 'V'
CATEGORY_CHOICES = (
(MUSIC, 'Music'),
(VIDEO, 'Video'),
)
category = models.CharField(max_length=1, choices=CATEGORY_CHOICES, blank=False)
The validation should be done in your view. The serializers should just be for serializing data. The validation should be done in your view then the serializer is called from your view. As far as this line self.model(user=user, category=category) is concerned it does not appear that you ever import user any where.

Django Rest Framework - "Got a `TypeError` when calling `Note.objects.create()'"

When i am trying to POST using the browsable API of DRF, i get the following error:
Got a TypeError when calling Note.objects.create(). This may be
because you have a writable field on the serializer class that is not
a valid argument to Note.objects.create(). You may need to make the
field read-only, or override the NoteSerializer.create() method to
handle this correctly.
I don't know what is generating this error or how to overcome it. Literally spent hours google searching or changing the code. Can someone explain how to overcome this error? (Please note i am new to Django and DRF!)
Here is the models.py:
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
import uuid
class Stock(models.Model):
'''
Model representing the stock info.
'''
user = models.ForeignKey(User)
book_code = models.CharField(max_length=14, null=True, blank=True)
def __str__(self):
return self.book_code
class Note(models.Model):
'''
Model representing the stock note.
'''
user = models.ForeignKey(User)
note = models.TextField(max_length=560)
stock = models.ForeignKey(Stock, related_name='notes')
date_note_created = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.note
This is the views.py:
from rest_framework import generics
from stocknoter.models import Stock, Note
from api.serializers import StockSerializer, NoteSerializer
# Create your views here.
class StockList(generics.ListCreateAPIView):
serializer_class = StockSerializer
def get_queryset(self):
user = self.request.user
return Stock.objects.filter(user=user)
def perform_create(self, serializer):
serializer.save(user=self.request.user)
def perform_update(self, serializer):
serializer.save(user=self.request.user)
class NoteList(generics.ListCreateAPIView):
serializer_class = NoteSerializer
def get_queryset(self):
user = self.request.user
return Note.objects.filter(user=user)
def perform_create(self, serializer):
serializer.save(data=self.request.data)
def perform_update(self, serializer):
serializer.save(data=self.request.data)
This is the serializers.py:
from stocknoter.models import Stock, Note
from rest_framework import serializers
class StockSerializer(serializers.ModelSerializer):
user = serializers.HiddenField(default=serializers.CurrentUserDefault())
notes = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = Stock
fields = ('id', 'user', 'book_code', 'notes')
class NoteSerializer(serializers.ModelSerializer):
user = serializers.HiddenField(default=serializers.CurrentUserDefault())
class Meta:
model = Note
fields = ('user', 'note', 'stock')
I think this is because you are providing keyword argument data to save method. This is unnecessary. save() dont have such argument. Try just this:
def perform_create(self, serializer):
serializer.save()
take a print() of serializer_data in the view.py
to see what you actually sending to model and create.
then in your serializer.py override the create method..
for example:
def post(self, request):
print(serializer_data.validated_data)
def create(self, validated_data):
del validated_data['c']
return X.objects.create(a=validated_data['a'],
b=validated_data['b'],
)

Django rest framework sending a string with single quotes back instead of JSON with double quotes

I am sending JSON via POST to a view. The response I get back is only part JSON and the rest has changed from double quotes to single quotes.
My question is how to I make sure that I am sending all this back as JSON in a get? As an added bonus to my problem, I don't know what fields will be sent to leasee.
The POST looks like:
{"leasee":
{"profession":{"permanentMakeup":true,"esthetician":true,"hairStylist":true},
"compare":{"uniqueVsChic":"56"},
"name":"dfasdfasdf",
"contact":{"text":true,"facebook":true}}}
But GET gives me:
{"leasee":
"{'compare': {'uniqueVsChic': '56'},
'profession': {'hairStylist':True,
'esthetician': True,
'permanentMakeup': True},
'name':'dfasdfasdf',
'contact':{'facebook': True,'text': True}}",
"created_at":"2015-05-22T23:27:13.598686Z",
"updated_at":"2015-05-22T23:27:13.609893Z"}
my views.py:
from rest_framework import viewsets
from sitepages.models import SalonSubmission, StylistSubmission
from sitepages.serializers import SalonSerializer, StylistSerializer
from django.db.models.signals import post_save
from django.core.mail import send_mail
from django.dispatch import receiver
class SalonViewSet(viewsets.ModelViewSet):
queryset = SalonSubmission.objects.order_by('-created_at')
serializer_class = SalonSerializer
def perform_create(self, serializer):
instance = serializer.save()
post_save.send(sender=self.__class__, name=instance.business_name, phone=instance.phone)
return super(SalonViewSet, self).perform_create(serializer)
class StylistViewSet(viewsets.ModelViewSet):
queryset = StylistSubmission.objects.order_by('-created_at')
serializer_class = StylistSerializer
def perform_create(self, serializer):
instance = serializer.save()
post_save.send(sender=self.__class__, stylist=instance.leasee)
return super(StylistViewSet, self).perform_create(serializer)
#receiver(post_save, sender=SalonViewSet)
def send_email(sender, **kwargs):
send_mail(kwargs['name'], kwargs['phone'], 'from#example.com',
['to#example.com'], fail_silently=False)
#receiver(post_save, sender=StylistViewSet)
def send_email(sender, **kwargs):
send_mail('aName', kwargs['stylist'], 'from#example.com',
['to#example.com'], fail_silently=False)
My serializers.py:
from rest_framework import serializers
from sitepages.models import SalonSubmission, StylistSubmission
class SalonSerializer(serializers.ModelSerializer):
class Meta:
model = SalonSubmission
fields = ('business_name', 'phone', 'created_at', 'updated_at')
read_only_fields = ('created_at', 'updated_at')
class StylistSerializer(serializers.ModelSerializer):
class Meta:
model = StylistSubmission
fields = (
'leasee',
'created_at',
'updated_at'
)
read_only_fields = ('created_at', 'updated_at')
My models.py:
from django.db import models
class SalonSubmission(models.Model):
business_name = models.CharField(max_length=250)
phone = models.CharField(max_length=250)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class StylistSubmission(models.Model):
leasee = models.CharField(max_length=2000)
file = models.FileField(upload_to="/media/")
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
It appears that leasee = models.CharField(max_length=2000) is a charfield. So Django is simply returning the string stored in this field. Also it is not storing valid JSON string neither because JSON only allows double quote.
{'compare': {'uniqueVsChic': '56'},
'profession': {'hairStylist':True,
'esthetician': True,
'permanentMakeup': True},
'name':'dfasdfasdf',
'contact':{'facebook': True,'text': True}}
looks like stringified dict.
So you can try to parse leasee as a dict in your serializer.