so I am building an API and i want to fetch based on multiple parameters.
Here is the code base.
The Url path:
path('<str:order_id>/consumers/', SingleConsumerTradeAPIView.as_view(), name="single-consumer-trade" ),
path('<str:order_id>/producers/', SingleProducerTradeAPIView.as_view(), name="single-producer-trade" ),
Models.py:
from django.db import models
from authApi.models import User
class Order(models.Model):
user = models.ForeignKey(User,related_name='user',null=True, on_delete=models.CASCADE)
date = models.DateField(auto_now_add=True)
class Trade(models.Model):
consumer = models.ForeignKey(User,related_name='consumer',on_delete=models.CASCADE)
producer = models.ForeignKey(User,related_name='producer',on_delete=models.CASCADE)
order = models.ForeignKey(Order, related_name='trades',on_delete=models.CASCADE)
price = models.DecimalField(max_digits=10, max_length=255, decimal_places=2)
location = models.CharField(max_length=255)
energyQuantity = models.DecimalField(max_digits=10, max_length=255, decimal_places=2)
startTime = models.DateField(auto_now_add=True)
stopTime = models.DateField(auto_now_add=True)
Serializers.py:
class TradeSerializer(serializers.ModelSerializer):
class Meta:
model = Trade
fields = ('id',
'order_id',
'startTime',
'stopTime',
'price',
'consumer_id',
'producer_id',
'location',
'energyQuantity',
)
class OrderSerializer(serializers.ModelSerializer):
trades = TradeSerializer(read_only=True, many= True)
class Meta:
model = Order
fields = ('id',
'trades',
'date',
)
What i tried:
Views
class SingleProducerTradeAPIView(ListCreateAPIView):
serializer_class=TradeSerializer
queryset = Trade.objects.all()
permission_classes = (permissions.IsAuthenticated,)
lookup_fields = ('order_id', 'producer_id')
def list(self, *args, **kwargs):
order_id = kwargs.get('order_id')
user = self.request.user
try:
trades = Trade.objects.filter(order_id=order_id,consumer_id=user)
except Trade.DoesNotExist:
return JsonResponse({'message': 'The user does not exist'}, status=status.HTTP_404_NOT_FOUND)
order_serializer = TradeSerializer(trades, many=True)
return JsonResponse(order_serializer.data, safe=False)
def perform_create(self, serializer):
return serializer.save(consumer_id=self.request.user)
I want to be able to fetch from the list of trades(via the trade model and serializers) using the order_id and the producer_id.
Here you can directly use the get_queryset method and you will definitely get the parameters from kwargs of the get_queryset method and you can remove the queryset variable from the class and directly hit the query in the get_queryset method. It will save time by hitting the database only once.
there is no need for lookup_fields too so you can remove it.
class SingleConsumerTradeAPIView(ListCreateAPIView):
serializer_class=TradeSerializer
permission_classes = (permissions.IsAuthenticated,)
lookup_fields = ('order_id', 'producer_id')
def get_queryset(self, *args, **kwargs):
return Trade.objects.filter(order_id=kwargs.get('order_id'),producer_id=
self.request.user.id)
Django will send the parameters from the URL into the view functions are keyword arguments (**kwargs)
class SingleConsumerTradeAPIView(ListCreateAPIView):
serializer_class=TradeSerializer
queryset = Trade.objects.all()
permission_classes = (permissions.IsAuthenticated,)
lookup_fields = ('order_id', 'producer_id')
def list(self, *args, **kwargs):
order_id = kwargs.get('order_id')
user_id = kwargs.get('user_id')
# do some work with order_id and user_id
def get_queryset(self):
return self.queryset.filter(order_id=self.request.user,producer_id=
self.request.user)
Related
As declared in question title, i got task to filter results by field not presented in model but calculated by serializer.
The model:
class Recipe(models.Model):
tags = models.ManyToManyField(
Tag,
related_name='recipe_tags'
)
author = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name='author_recipes'
)
ingredients = models.ManyToManyField(
Ingredient,
related_name='recipe_ingredients'
)
name = models.CharField(max_length=200)
image = models.ImageField()
text = models.TextField()
cooking_time = models.PositiveSmallIntegerField(
validators=[MinValueValidator(1)]
)
class Meta:
ordering = ("-id",)
verbose_name = "Recipe"
verbose_name_plural = "Recipes"
def __str__(self):
return self.name
Here is the view code:
class RecipeViewSet(ModelViewSet):
queryset = Recipe.objects.all()
permission_classes = [IsAdminOrAuthorOrReadOnly, ]
serializer_class = RecipeInSerializer
pagination_class = LimitPageNumberPagination
filter_backends = [DjangoFilterBackend]
filterset_fields = ['tags', ]
filter_class = RecipeFilter
Serializer:
class RecipeOutSerializer(serializers.ModelSerializer):
tags = ManyRelatedField(child_relation=TagSerializer())
author = CustomUserSerializer()
ingredients = serializers.SerializerMethodField()
is_favorite = serializers.SerializerMethodField()
is_in_shopping_cart = serializers.SerializerMethodField()
class Meta:
fields = '__all__'
model = Recipe
def get_ingredients(self, obj):
ingredients = IngredientAmount.objects.filter(recipe=obj)
return GetIngredientSerializer(ingredients, many=True).data
def get_is_favorite(self, obj):
request = self.context.get("request")
if request.user.is_anonymous:
return False
return Favorite.objects.filter(recipe=obj, user=request.user).exists()
def get_is_in_shopping_cart(self, obj):
request = self.context.get("request")
if not request or request.user.is_anonymous:
return False
return ShoppingCart.objects.filter(recipe=obj, user=request.user).exists()
And custom filter code:
class RecipeFilter(rest_framework.FilterSet):
tags = ModelMultipleChoiceFilter(
field_name='tags__slug',
to_field_name="slug",
queryset=Tag.objects.all()
)
favorite = BooleanFilter(field_name='is_favorite', method='filter_favorite')
def filter_favorite(self, queryset, name, value):
return queryset.filter(is_favorite__exact=True)
class Meta:
model = Recipe
fields = ['tags', ]
Target is is_favorited field that return boolean value. I tried writing func in custom filter class that return queryset but didnt work, neither documentation helped me with examples. Hope for your help.
We can use queryset annotate:
from django.db import models
from rest_framework import serializers
class RecipeViewSet(ModelViewSet):
def get_queryset(self):
user = self.request.user
user_id = user.id if not user.is_anonymous else None
return Recipe.objects.all().annotate(
total_favorite=models.Count(
"favorite",
filter=models.Q(favorite__user_id=user_id)
),
is_favorite=models.Case(
models.When(total_favorite__gte=1, then=True),
default=False,
output_field=BooleanField()
)
)
class RecipeOutSerializer(serializers.ModelSerializer)
is_favorite = serializers.BooleanField(read_only=True)
class Meta:
model = Recipe
fields = (
# ...
is_favorite,
)
class RecipeFilter(rest_framework.FilterSet):
favorite = BooleanFilter(field_name='is_favorite')
I'm building a leaderboard view for a REST API I'm designing in DRF. I need a bit of help in reducing an inefficiency.
views.py
class LeaderboardAPIView(ListAPIView):
serializer_class = UserSerializerLeaderboard
permission_classes = (IsAuthenticated,)
def get_queryset(self):
queryset = User.objects.all()
queryset = list(queryset)
queryset.sort(key=operator.attrgetter("total_karma"), reverse=True)
queryset = queryset[:10]
return queryset
serializers.py
class UserSerializerLeaderboard(serializers.ModelSerializer):
score = serializers.SerializerMethodField(read_only=True)
place = serializers.SerializerMethodField(read_only=True)
def get_score(self, obj):
return obj.total_karma
def get_place(self, obj):
return "1"
class Meta:
model = User
fields = ("score", "place")
models.py
#property
def total_karma(self):
return self.total_post_score() + self.total_comment_score()
def total_post_score(self):
relevant_votes = PostVote.objects.filter(post__user=self)
total = 0
for vote in relevant_votes:
total += vote.vote
return total
def total_comment_score(self):
relevant_votes = CommentVote.objects.filter(comment__user=self)
total = 0
for vote in relevant_votes:
total += vote.vote
return total
...
class PostVote(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
post = models.ForeignKey(Post, on_delete=models.SET_NULL, null=True)
vote = models.IntegerField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
unique_together = ("user", "post")
def __str__(self):
return str(self.id) + " " + str(self.vote)
...
class CommentVote(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
comment = models.ForeignKey(Comment, on_delete=models.SET_NULL, null=True)
vote = models.IntegerField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
unique_together = ("user", "comment")
def __str__(self):
return str(self.id) + " " + str(self.vote)
The get_place method currently returns a placeholder of 1. I'd like it to return the actual place the user is in, sorted by score. Now, I know I'm already calculating this in the queryset, so I don't want to repeat this inefficiently. How can I send the place of the user to the serializer directly, rather than repeating the calculation in the method?
You can use a window function to annotate each row in your queryset with it's position/row number
Change the total_karma property and the methods used to annotations in your queryset so that you can order by that field and access the calculated result from the returned objects without having to calculate it again
from django.db.models import Window, F, Sum
from django.db.models.functions import RowNumber
class LeaderboardAPIView(ListAPIView):
serializer_class = UserSerializerLeaderboard
permission_classes = (IsAuthenticated,)
def get_queryset(self):
queryset = User.objects.all()
queryset = queryset.annotate(
post_score=Sum('postvote__vote'),
comment_score=Sum('commentvote__vote')
)
queryset = queryset.annotate(
total_karma=F('post_score') + F('comment_score')
)
queryset = queryset.order_by(
'-total_karma'
).annotate(row_num=Window(
expression=RowNumber(),
order_by=F('total_karma').desc()
))[:10]
return queryset
Serializer
class UserSerializerLeaderboard(serializers.ModelSerializer):
score = serializers.SerializerMethodField(read_only=True)
place = serializers.SerializerMethodField(read_only=True)
def get_score(self, obj):
return obj.total_karma
def get_place(self, obj):
return obj.row_num
class Meta:
model = User
fields = ("score", "place")
Our project is a fictional webshop where you can put puppies in your cart and order them. The three main models that we have are Users, Orders and Puppies. Because every order has a list of puppies and every puppy has its own amount we needed an intermediary model for the two as a simple ManyToManyField apparently can't handle extra columns. This makes things much more complicated (at least for someone who is new to Django).
Also our authorization is via a JWT token, that's why the self.request.user apparently has to be manually set and isn't set automatically.
The only thing that should be possible to create via a POST are new orders. These should automatically get a date and the user posting it should also automatically be set. The total_price of an order should also be calculated so that the only things that need to be sent by the client are a list of puppies and their respective amounts.
POST in React:
async function createOrder(auth, puppies) {
auth = auth.token
puppies = puppies.map(element =>
element = {'id' : element.puppy.id, 'amount': element.amount}
)
const res = await fetch(REACT_APP_BACKEND_URL + `/shop/orders/`, {
method: 'POST',
headers: {
"Content-Type": "application/json",
"Authorization": "JWT " + auth
},
body: JSON.stringify({ puppies })
})
return res.json()
console.log results in: [{"id":2,"amount":1},{"id":3,"amount":1}]
models.py:
from django.db import models
from django.conf import settings
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
class Puppy(models.Model):
name = models.CharField(max_length=30)
price = models.DecimalField(max_digits=6, decimal_places=2)
image_url = models.CharField(max_length=200)
age = models.IntegerField(null=True)
weight = models.IntegerField(null=True)
description_de = models.CharField(max_length=500, null=True)
description_en = models.CharField(max_length=500, null=True)
def __str__(self):
return self.name
class Order(models.Model):
total_price = models.DecimalField(max_digits=9, decimal_places=2, null=True)
puppies = models.ManyToManyField(Puppy, through='PuppyOrder')
date = models.DateTimeField(auto_now_add=True, blank=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='orders')
def __str__(self):
return str(self.id)
class PuppyOrder(models.Model):
order = models.ForeignKey(Order, on_delete=models.CASCADE)
puppy = models.ForeignKey(Puppy, on_delete=models.CASCADE)
amount = models.IntegerField()
def __str__(self):
return str(self.id)
class Address(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
country = models.CharField(max_length=30, blank=True)
street = models.CharField(max_length=30, blank=True)
zip = models.CharField(max_length=10, blank=True)
city = models.CharField(max_length=30, blank=True)
#receiver(post_save, sender=User)
def create_user_address(sender, instance, created, **kwargs):
if created:
Address.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_address(sender, instance, **kwargs):
instance.address.save()
views.py:
from shop.models import Puppy, Order
from django.contrib.auth.models import User
from rest_framework import permissions, status, viewsets, generics
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.reverse import reverse
from rest_framework.exceptions import ValidationError
from rest_framework_jwt.serializers import VerifyJSONWebTokenSerializer
from .serializers import UserSerializer, UserSerializerWithToken, OrderSerializer, PuppySerializer
from .permissions import IsOwner
#api_view(['GET'])
def api_root(request, format=None):
return Response({
'users': reverse('user-list', request=request, format=format),
'orders': reverse('order-list', request=request, format=format),
'puppies': reverse('puppy-list', request=request, format=format),
})
class OrderList(generics.ListCreateAPIView):
queryset = Order.objects.all()
permission_classes = (permissions.IsAuthenticated,)
serializer_class = OrderSerializer
def get_queryset(self):
return Order.objects.all().filter(user=self.request.user)
def perform_create(self, serializer):
print('Creating new order...')
serializer.save(user=self.request.user)
return Response(serializer.data)
class OrderDetail(generics.RetrieveAPIView):
permission_classes = (permissions.IsAuthenticated,)
queryset = Order.objects.all()
serializer_class = OrderSerializer
def get_queryset(self):
return Order.objects.all().filter(user=self.request.user)
class UserList(generics.ListAPIView):
queryset = User.objects.all().select_related('address')
serializer_class = UserSerializer
class UserDetail(generics.RetrieveAPIView):
queryset = User.objects.all().select_related('address')
serializer_class = UserSerializer
class PuppyList(generics.ListAPIView):
permission_classes = (permissions.AllowAny,)
queryset = Puppy.objects.all()
serializer_class = PuppySerializer
class PuppyDetail(generics.RetrieveAPIView):
permission_classes = (permissions.AllowAny,)
queryset = Puppy.objects.all()
serializer_class = PuppySerializer
serializers.py:
from rest_framework import serializers
from rest_framework_jwt.settings import api_settings
from django.contrib.auth.models import User
from django.db import models
from .models import Order, Puppy, PuppyOrder
class UserSerializer(serializers.ModelSerializer):
orders = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = User
fields = ('id', 'username', 'orders')
class UserSerializerWithToken(serializers.ModelSerializer):
# Code to generate token on login ...
class PuppySerializer(serializers.ModelSerializer):
description = serializers.SerializerMethodField()
def get_description(self, puppy):
return {'DE': puppy.description_de, 'EN': puppy.description_en}
class Meta:
model = Puppy
fields = ('id', 'name', 'price', 'image_url', 'age', 'weight', 'description')
class PuppyOrderSerializer(serializers.ModelSerializer):
puppy = serializers.ReadOnlyField(source='puppy.id')
order = serializers.ReadOnlyField(source='order.id')
class Meta:
model = PuppyOrder
fields = ('order', 'puppy', 'amount')
class OrderSerializer(serializers.ModelSerializer):
user = serializers.ReadOnlyField(source='user.username')
puppies = PuppyOrderSerializer(source='puppyorder_set', many=True)
def create(self, validated_data):
print(validated_data)
puppies = validated_data.pop("puppyorder_set")
order = Order.objects.create(**validated_data)
total_price = 0
for puppy in puppies:
puppy_id = puppy.get("id")
amount = puppy.get("amount")
puppy_instance = Puppy.objects.get(pk=puppy_id)
total_price += puppy_instance.price
PuppyOrder(order=order, puppy=puppy_instance, amount=amount).save()
order.save()
return Order
class Meta:
model = Order
fields = ('id', 'total_price', 'puppies', 'date', 'user')
This code results in an error: "Direct assignment to the forward side of a many-to-many set is prohibited. Use puppies.set() instead."
Please tell me what I'm doing wrong
There are many things you're not getting quite right. First, the authentication. Please checkout DRF-JWT or similar libraries and use their authentication classes or atleast see how to create yours so that you don't have your authentication littered all over your code.
As for the perform_create in your view, you don't really have to check serializer.is_valid() there. It is already checked in create before perform_create is called. And that method is not meant to return an API response as far as I know. It's just meant to perform any other extra actions you want before calling serializer.save(). For most basic usages, it is never overriden. You can also pass in your user and the date there to the save method or do that in the serializer.
Now, in the serializer's create method, you do not need to access initial_data. All you need should be in validated_data. First, you should pop the puppies from validated_data before calling objects.create, else, it wouldn't be able to handle it. Then you can now create the PuppyOrder objects using the earlier on popped puppies.
This is how you add extra data to the serializer's validated_data in the view's perform_create:
serializer.save(date=datetime.today(), user=self.request.user, total_price=total_price)
You can do the same in the serializer's create method by just addind the items directly in the validated data. It's just a normal dictionary afterall:
validated_data['user'] = self.context['request'].user
validated_data['date'] = datetime.today()
validated_data['total_price'] = total_price
EDIT: Using a non model serializer for PuppyOrder
Since the order and PuppyOrder is not yet created at the time the request is being sent, the following line in the OrderSerializer would result to an error because the the serializer expects actual PuppyOrders
puppies = PuppyOrderSerializer(source='puppyorder_set', many=True)
Instead you can use non model serializer like this(assuming the puppy already exists):
class PuppyOrderCreateSerializer(serializers.Serializer):
puppy = serializers.PrimaryKeyRelatedField(queryset=Puppy.objects.all())
amount = serializers.IntegerField()
Then you can use this serializer in yuur OrderSerializer:
puppies_data = PuppyOrderCreateSerializer(many=True)
I'm trying to create an order by sending it as a POST request via my DRF API but I keep getting ""POST /shop/orders/ HTTP/1.1" 400 81" errors.
The error says "{"total_price":["This field is required."],"puppies":["This field is required."]}" even though I'm supplying it.
The GET is working fine but I only have orders that I manually wrote in the DB.
Our project is a fictional webshop where you can put dog babies in your cart and order them. The three main models that we have are Users, Orders and Puppies. Because every order has a list of puppies and every puppy has its own amount we needed an intermediary model for the two as a simple ManyToManyField apparently can't handle extra columns. This makes things much more complicated (at least for someone who is new to Django).
I can't find anything on the internet for this special case. I have been trying to get this to work for almost a week now and I don't see what I'm doing wrong.
models.py:
from django.db import models
from django.conf import settings
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
class Puppy(models.Model):
name = models.CharField(max_length=30)
price = models.DecimalField(max_digits=6, decimal_places=2)
image_url = models.CharField(max_length=200)
age = models.IntegerField(null=True)
weight = models.IntegerField(null=True)
description_de = models.CharField(max_length=500, null=True)
description_en = models.CharField(max_length=500, null=True)
def __str__(self):
return self.name
class Order(models.Model):
total_price = models.DecimalField(max_digits=9, decimal_places=2)
puppies = models.ManyToManyField(Puppy, through='PuppyOrder')
date = models.DateTimeField(auto_now_add=True, blank=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='orders')
def __str__(self):
return str(self.id)
class PuppyOrder(models.Model):
order = models.ForeignKey(Order, on_delete=models.CASCADE)
puppy = models.ForeignKey(Puppy, on_delete=models.CASCADE)
amount = models.IntegerField()
def __str__(self):
return str(self.id)
class Address(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
country = models.CharField(max_length=30, blank=True)
street = models.CharField(max_length=30, blank=True)
zip = models.CharField(max_length=10, blank=True)
city = models.CharField(max_length=30, blank=True)
#receiver(post_save, sender=User)
def create_user_address(sender, instance, created, **kwargs):
if created:
Address.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_address(sender, instance, **kwargs):
instance.address.save()
Fetch in react:
async function createOrder(order) {
order = order.map(element =>
element = {'puppy' : element.puppy.id, 'amount': element.amount}
)
let orderReq = {"total_price": "500.00", "puppies": order}
console.log(JSON.stringify(orderReq))
const res = await fetch(REACT_APP_BACKEND_URL + `/shop/orders/`, {
method: 'POST',
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ orderReq })
})
return res.json()
}
Console.log results in: {"total_price":"500.00","puppies":[{"puppy":2,"amount":3},{"puppy":4,"amount":4}]}
"500.00" is just a placeholder value. Next step will be finding a place to calculate the total_price.
views.py:
from shop.models import Puppy, Order
from django.contrib.auth.models import User
from rest_framework import permissions, status, viewsets, generics
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.reverse import reverse
from rest_framework.exceptions import ValidationError
from rest_framework_jwt.serializers import VerifyJSONWebTokenSerializer
from .serializers import UserSerializer, UserSerializerWithToken, OrderSerializer, PuppySerializer
#api_view(['GET'])
def api_root(request, format=None):
return Response({
'users': reverse('user-list', request=request, format=format),
'orders': reverse('order-list', request=request, format=format),
'puppies': reverse('puppy-list', request=request, format=format),
})
class OrderList(generics.ListCreateAPIView):
print("Test")
queryset = Order.objects.all()
permission_classes = (permissions.AllowAny,)
serializer_class = OrderSerializer
def get_queryset(self):
if self.request.user is None or self.request.user.is_anonymous:
self.request.user = get_request_user_and_validate_token(self)
return Order.objects.all().filter(user=self.request.user)
def perform_create(self, serializer):
print(self.request.data)
if self.request.user is None or self.request.user.is_anonymous:
self.reqest.user = get_request_user_and_validate_token(self)
if serializer.is_valid():
print('Creating new order...')
serializer.create(user=self.request.user, puppies=self.request.data)
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class OrderDetail(generics.RetrieveAPIView):
permission_classes = (permissions.AllowAny,)
queryset = Order.objects.all()
serializer_class = OrderSerializer
def get_queryset(self):
if self.request.user is None or self.request.user.is_anonymous:
self.request.user = get_request_user_and_validate_token(self)
return Order.objects.all().filter(user=self.request.user)
class UserList(generics.ListAPIView):
queryset = User.objects.all().select_related('address')
serializer_class = UserSerializer
class UserDetail(generics.RetrieveAPIView):
queryset = User.objects.all().select_related('address')
serializer_class = UserSerializer
class PuppyList(generics.ListAPIView):
permission_classes = (permissions.AllowAny,)
queryset = Puppy.objects.all()
serializer_class = PuppySerializer
class PuppyDetail(generics.RetrieveAPIView):
permission_classes = (permissions.AllowAny,)
queryset = Puppy.objects.all()
serializer_class = PuppySerializer
def get_request_user_and_validate_token(self):
print(self.request.user)
token = self.request.META.get('HTTP_AUTHORIZATION', " ").split(' ')[1]
data = {'token': token}
try:
valid_data = VerifyJSONWebTokenSerializer().validate(data)
print(valid_data['user'])
return valid_data['user']
except ValidationError as v:
print("validation error", v)
serializers.py
from rest_framework import serializers
from rest_framework_jwt.settings import api_settings
from django.contrib.auth.models import User
from django.db import models
from .models import Order, Puppy, PuppyOrder
class UserSerializer(serializers.ModelSerializer):
orders = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = User
fields = ('id', 'username', 'orders')
class UserSerializerWithToken(serializers.ModelSerializer):
# Code dealing with JWT token creation ...
class PuppySerializer(serializers.ModelSerializer):
description = serializers.SerializerMethodField()
def get_description(self, puppy):
return {'DE': puppy.description_de, 'EN': puppy.description_en}
class Meta:
model = Puppy
fields = ('id', 'name', 'price', 'image_url', 'age', 'weight', 'description')
class PuppyOrderSerializer(serializers.ModelSerializer):
puppy = serializers.ReadOnlyField(source='puppy.id')
class Meta:
model = PuppyOrder
fields = ('puppy', 'amount')
class OrderSerializer(serializers.ModelSerializer):
puppies = PuppyOrderSerializer(source='puppyorder_set', many=True)
user = serializers.ReadOnlyField(source='user.username')
total_price = "100.00"
class Meta:
model = Order
fields = ('id', 'total_price', 'puppies', 'date', 'user')
Any help would be much appreciated!
I can't get a valid response, always get this error
{"required": "This field is required.", "null": "This field may not be null.", "not_a_list": "Expected a list of items but got type \"{input_type}\".", "empty": "This list may not be empty."}
Here is some code (models, serializers, views)
models
class Task(models.Model):
name = models.CharField(max_length=50)
description = models.CharField(max_length=500, blank=True, default='')
pub_datetime = models.DateTimeField()
priority = models.CharField(choices=PRIORITY_CHOICES, max_length=1)
status = models.BooleanField(default=False)
owner = models.ForeignKey('auth.User', on_delete=models.CASCADE)
agenda = models.ForeignKey(Agenda, on_delete=models.CASCADE, blank=True, default=None, null=True)
def __str__(self):
return self.name
serializers
class TaskSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
agenda = serializers.ReadOnlyField(source='agenda.name')
class Meta:
model = Task
fields = ('id', 'name', 'description', 'pub_datetime', 'priority', 'status', 'owner', 'agenda')
views
class TaskListView(generics.ListCreateAPIView):
serializer_class = TaskSerializer
permission_classes = (permissions.IsAuthenticated,)
renderer_classes = [JSONRenderer, TemplateHTMLRenderer]
template_name = 'task_list.html'
def get(self, request, *args, **kwargs):
queryset = Task.objects.filter(owner=self.request.user)
if self.request.accepted_renderer.format == 'html':
return Response({'tasks': queryset})
else:
serializer = TaskSerializer(data=queryset, many=True)
if serializer.is_valid():
return JsonResponse(serializer.data, safe=False)
else:
return JsonResponse(serializer.error_messages)
def post(self, request, *args, **kwargs):
name = request.data['name']
description = request.data['description']
priority = request.data['priority']
new_task = Task.objects.create(name=name, description=description, pub_datetime=datetime.datetime.now(),
priority=priority, status=False, owner=self.request.user)
new_task.save()
return redirect('task-list')
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
I'm not sure, at what place I'm wrong. Can someone help?
just text to avoid errors
The queryset or object instance should be passed to serializer as instance, not data, so:
queryset = Task.objects.filter(owner=self.request.user)
serializer = TaskSerializer(instance=queryset, many=True)
Also, I believe that serializer can't validate instance, so in this case you should be able to just return return JsonResponse(serializer.data)