How to set 0, instead of null? - django

I have 'average_rating' field that takes values from field 'rates' and calculate average value. But when 'rates' field is empty, its shows null. views.py:
class BooksView(viewsets.ModelViewSet):
serializer_class = BooksSerializer
queryset = BooksModel.objects.all()
filter_backends = [filters.DjangoFilterBackend, filtersrest.SearchFilter, filtersrest.OrderingFilter,]
filterset_class = Myfilter
filterset_fields = ('genres','date',)
search_fields = ['title']
def get_queryset(self):
return BooksModel.objects.all().annotate(average_rating=models.ExpressionWrapper(Round(Avg('rates__rate'), 2), output_field=models.FloatField()), _sum_users_cart=Count('users__user'))
In the output i have this:
"id": 18,
"authors": [
{
"author": "Андрей курпатов"
}
],
"age": [],
"tags": [],
"users": [],
"genres": [],
"rates": [],
"average_rating": null,
"sum_users_cart": 0,
"title": "Мозг и бизнес",
"desc": "123",
"url_photo": "https://s1.livelib.ru/boocover/1007572198/o/fde7/Andrej_Kurpatov__Mozg_i_biznes.jpeg",
"is_published": true,
"date": "Декабрь 29, 2022",
"time": "20:20:02"
And i need something like this:
"id": 18,
"authors": [
{
"author": "Андрей курпатов"
}
],
"age": [],
"tags": [],
"users": [],
"genres": [],
"rates": [],
"average_rating": 0,
"sum_users_cart": 0,
"title": "Мозг и бизнес",
"desc": "123",
"url_photo": "https://s1.livelib.ru/boocover/1007572198/o/fde7/Andrej_Kurpatov__Mozg_i_biznes.jpeg",
"is_published": true,
"date": "Декабрь 29, 2022",
"time": "20:20:02"

you can add a default value for the average_ratingin your models.py
for example:
from django.db import models
class BooksModel(models.Model):
...
average_rating = models.DecimalField(max_digits=12, decimal_places=2, default=0, null=False)
...
by doing that you ensure that whenever the value of average_rating is not set it will always be set to 0 instead of None.

You may try to do it like this:
from django.db.models import Case, When, FloatField
return BooksModel.objects.all().annotate(
average_rating=Case(
When(rates__isnull=True, then=0),
default=Round(Avg('rates__rate'), 2),
output_field=FloatField()
),
_sum_users_cart=Count('users__user')
)

Related

How can I create the associated OneToOne Field in Django automatically?

I have tried Django Annoying as mentioned in this answer
Create OneToOne instance on model creation
But I still get NOT NULL CONSTRAINT FAILED on the OneToOne Field when creating my object, which I suppose means that the associated object is not created, hence NULL
If I use A signal Handler, that will be activated every time the Model's save method will be called. I will end up creating a new Object even when updating the Parent object.
My Profile structure is as follows:
{
"id": 3,
"password": "pbkdf2_sha256$320000$5TQCdD5wIelYpO4ktpuuAk$oBC9xUT+8RjOxZHvE8C+eowS4PvdCT8vUAuS4Y2n7sM=",
"last_login": null,
"is_superuser": false,
"username": "hamad",
"first_name": "",
"last_name": "alahmed",
"email": "",
"is_staff": false,
"is_active": true,
"date_joined": "2022-06-17T13:04:51.927199Z",
"height": null,
"weight": null,
"createddate": "2022-06-17T13:04:52.362396Z",
"date": null,
"hobbies": null,
"dailyRoutine": {
"id": 8,
"steps": 0,
"diet": "No Specified Diet",
"maintained": 0
},
"groups": [],
"user_permissions": [],
"friends": [
{
"id": 1,
"password": "pbkdf2_sha256$320000$YRcHWSjLi1CMbfrolZ0W7W$9LgjH2m5c8emE66pjdExmgep47BAdKTrCJ7MBiJx74w=",
"last_login": "2022-06-17T17:50:48.410366Z",
"is_superuser": true,
"username": "super",
"first_name": "Super1",
"last_name": "Admin1",
"email": "super#gmail.com",
"is_staff": true,
"is_active": true,
"date_joined": "2022-06-17T12:27:37.575631Z",
"height": 160,
"weight": 75,
"createddate": "2022-06-17T12:27:37.789754Z",
"date": null,
"hobbies": "Football",
"dailyRoutine": 10,
"groups": [],
"user_permissions": [],
"friends": [
2,
3
]
}
]
}
The Object that I want to create automatically is DailyRoutine
The Routine code:
class Routine(models.Model):
steps = models.IntegerField(default=0)
diet = models.TextField(default='No Specified Diet')
maintained = models.IntegerField(default=0)
The Profile code
class Profile(AbstractUser):
height = models.IntegerField(blank=True, null=True)
weight = models.IntegerField(blank=True, null=True)
createddate = models.DateTimeField(auto_now_add=True)
date = models.DateField(blank=True, null=True)
hobbies = models.TextField(blank=True, null=True)
dailyRoutine = models.OneToOneField(Routine, related_name='dailyRoutine', on_delete=models.CASCADE)
friends = models.ManyToManyField("Profile", blank=True)
# def save(self, *args, **kwargs): Doing this will always create new DailyRoutine objects even when updating
# self.dailyRoutine = Routine.objects.create()
# super(AbstractUser, self).save(*args, **kwargs)

How to change DRF API SlugRelatedField Response template

I have managed to create a working model with 2 different serializers, depending on what we are doing. Right now, ReadTitleSerializer returns the below JSON object:
[
{
"id": 1,
"category": {
"id": 1,
"name": "Movies",
"slug": "movie"
},
"genres": [
{
"id": 1,
"name": "Drama",
"slug": "drama"
}
],
"name": "Drama Llama",
"year": "1998-02-02",
"description": null,
"rating": null
}
]
And this is the response from WriteTitleSerializer:
{
"id": 1,
"category": "movie",
"genres": [
"drama"
],
"name": "Drama Llama",
"year": "1998-02-02",
"description": null,
"rating": null
}
How can I make WriteTitleSerializer respond similarly to ReadTitleSerializer? I am using SlugRelatedField in WriteTitleSerializer because the JSON input should be a list of slugs.
Input JSON
{
"name": "Drama Llama",
"year": "1998-02-02",
"category": "movie",
"genres": [
"drama"
]
}
serializers.py
class ReadTitleSerializer(serializers.ModelSerializer):
category = CategorySerializer()
genres = GenreSerializer(many=True)
class Meta:
model = Title
fields = '__all__'
read_only_fields = ('category', 'genres')
class WriteTitleSerializer(serializers.ModelSerializer):
category = SlugRelatedField(
slug_field='slug',
queryset=Category.objects.all(),
required=True
)
genres = SlugRelatedField(
slug_field='slug',
queryset=Genre.objects.all(),
many=True,
required=True
)
class Meta:
model = Title
fields = '__all__'

Django filtering if multiple foreign key return , display only first foreign key data

I want to get all the quizArchive where qbank, qset match by ID.
But only those model that user's first created.
I have this model:
class QuizArchive(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="quizarchive")
questionset = models.ForeignKey(
"qbank.QuestionSet", on_delete=models.CASCADE, related_name="qarchive"
)
qbank = models.ForeignKey(
"qbank.QuestionBank",
on_delete=models.CASCADE,
null=True,
blank=True,
related_name="qbank",
)
wrong = models.ManyToManyField(Question, related_name="qarchivew", blank=True)
skipped = models.ManyToManyField(Question, related_name="qarchivesk", blank=True)
right = models.ManyToManyField(Question, related_name="qarchiver", blank=True)
selected_answer = models.JSONField(blank=True, null=True)
created_time = models.DateTimeField(auto_now_add=True)
updated_time = models.DateTimeField(auto_now=True)
def __str__(self):
return f"{self.user} takes {self.questionset}"
Serializer Code:
class LeaderboardSerializer(serializers.ModelSerializer):
class Meta:
model = QuizArchive
fields = ["user", "wrong", "right", "skipped", "updated_time","questionset"]
def to_representation(self,instance):
leaderboard = super().to_representation(instance)
return leaderboard
API Code:
class QSetLeaderBoard(views.APIView):
permission_classes = [AllowAny,]
def get(self,request,*args, **kwargs):
qset = QuestionSet.objects.filter(id=int(kwargs.get("qset"))).first()
qbank = QuestionBank.objects.filter(id=int(kwargs.get("qbank"))).first()
qarchive = QuizArchive.objects.filter(qbank=qbank,questionset=qset)
serializer = LeaderboardSerializer(qarchive,many=True)
data = serializer.data
return Response(data)
Result :
[
{
"user": 1,
"wrong": [],
"right": [],
"skipped": [
1
],
"updated_time": "2022-01-26T12:44:12.055967Z",
"questionset": 1
},
{
"user": 2,
"wrong": [
1
],
"right": [],
"skipped": [],
"updated_time": "2022-01-26T13:00:27.761721Z",
"questionset": 1
},
{
"user": 1,
"wrong": [
1
],
"right": [],
"skipped": [],
"updated_time": "2022-01-26T12:58:52.798367Z",
"questionset": 1
},
{
"user": 2,
"wrong": [],
"right": [
1
],
"skipped": [],
"updated_time": "2022-01-26T13:00:47.148016Z",
"questionset": 1
}
]
But I only want to unique user and their first quizarchive data by updated_time field like this:
[
{
"user": 1,
"wrong": [],
"right": [],
"skipped": [
1
],
"updated_time": "2022-01-26T12:44:12.055967Z",
"questionset": 1
},
{
"user": 2,
"wrong": [
1
],
"right": [],
"skipped": [],
"updated_time": "2022-01-26T13:00:27.761721Z",
"questionset": 1
}
]
SO filtered by unique user and first created quizarchive. Is this possible?
🙌
Filtering unique values could be done by using the .distinct() method of a QuerySet:
Django docs
You can try grouping by:
qarchive = QuizArchive.objects.filter(qbank=qbank,questionset=qset)
.order_by("user").value("user")

Django Rest Framework pagination and filtering

Hi guys i am trying to make filtering with pagination but i cannot get the result i want.
This is my function in views.py.
class OyunlarList(generics.ListAPIView):
# queryset = Oyunlar.objects.all()
pagination_class = StandardPagesPagination
filter_backends = [DjangoFilterBackend]
filterset_fields = ['categories__name', 'platform']
# serializer_class = OyunlarSerializer
def get_queryset(self):
queryset=Oyunlar.objects.all()
oyunlar=OyunlarSerializer.setup_eager_loading(queryset)
return oyunlar
def get(self,request,*args,**kwargs):
queryset = self.get_queryset()
serializer=OyunlarSerializer(queryset,many=True)
page=self.paginate_queryset(serializer.data)
return self.get_paginated_response(page)
This is my pagination class.
class StandardPagesPagination(PageNumberPagination):
page_size = 10
And this is the json i got but when i write localhost/api/games?platform=pc or localhost/api/games?categories=Action it is not working.
{
"count": 18105,
"next": "http://127.0.0.1:8000/api/oyunlar?categories__name=&page=2&platform=pc",
"previous": null,
"results": [
{
"game_id": 3,
"title": "The Savior's Gang",
"platform": "ps4",
"image": "https://store.playstation.com/store/api/chihiro/00_09_000/container/TR/en/999/EP3729-CUSA23817_00-THESAVIORSGANG00/1599234859000/image?w=240&h=240&bg_color=000000&opacity=100&_version=00_09_000",
"categories": [],
"release_date": null
},
{
"game_id": 8,
"title": "Spellbreak",
"platform": "ps4",
"image": "https://store.playstation.com/store/api/chihiro/00_09_000/container/TR/en/999/EP0795-CUSA18527_00-SPELLBREAK000000/1599612713000/image?w=240&h=240&bg_color=000000&opacity=100&_version=00_09_000",
"categories": [],
"release_date": null
},
{
"game_id": 11,
"title": "Marvel's Avengers",
"platform": "ps4",
"image": "https://store.playstation.com/store/api/chihiro/00_09_000/container/TR/en/999/EP0082-CUSA14030_00-BASEGAME0001SIEE/1599653581000/image?w=240&h=240&bg_color=000000&opacity=100&_version=00_09_000",
"categories": [],
"release_date": null
},
{
"game_id": 24,
"title": "The Suicide of Rachel Foster",
"platform": "ps4",
"image": "https://store.playstation.com/store/api/chihiro/00_09_000/container/TR/en/999/EP8923-CUSA19152_00-DAEEUTSORF000001/1599610166000/image?w=240&h=240&bg_color=000000&opacity=100&_version=00_09_000",
"categories": [
{
"category_id": 2,
"name": "Casual"
},
{
"category_id": 5,
"name": "İndie"
},
{
"category_id": 8,
"name": "Adventure"
}
],
"release_date": "2020-09-09"
},
{
"game_id": 25,
"title": "Takotan",
"platform": "ps4",
"image": "https://store.playstation.com/store/api/chihiro/00_09_000/container/TR/en/999/EP2005-CUSA24716_00-TKTN000000000000/1599610166000/image?w=240&h=240&bg_color=000000&opacity=100&_version=00_09_000",
"categories": [
{
"category_id": 1,
"name": "Action"
},
{
"category_id": 12,
"name": "Arcade"
},
{
"category_id": 13,
"name": "Shooter"
}
],
"release_date": "2020-09-09"...
Can you help me guys with this problem?I couldn't find any solution.
If you override the get method you have to make sure you know how the original implementation looks like.
def get(self,request,*args,**kwargs):
queryset = self.filter_queryset(self.get_queryset())
...
So filter_queryset is the missing piece here.

Django REST Meta data for M2M missing

On my json output I don't seem to get key value pairs on my m2m field attribute_answers. see the code below. How to I add in the attribute_answers fields?
json
{
"id": 20,
"name": "Jake",
"active": true,
"type": {
"id": 1,
"name": "Human",
"active": true,
"created": "2013-02-12T13:31:06Z",
"modified": null
},
"user": "jason",
"attribute_answers": [
1,
2
]
}
Serializer
class ProfileSerializer(serializers.ModelSerializer):
user = serializers.SlugRelatedField(slug_field='username')
attribute_answers = serializers.PrimaryKeyRelatedField(many=True)
class Meta:
model = Profile
depth = 2
fields = ('id', 'name', 'active', 'type', 'user', 'attribute_answers')
def restore_object(self, attrs, instance=None):
"""
Create or update a new snippet instance.
"""
if instance:
# Update existing instance
instance.name = attrs.get('name', instance.name)
instance.active = attrs.get('active', instance.active)
instance.type = attrs.get('type', instance.type)
instance.attribute_answers = attrs.get('attribute_answers', instance.attribute_answers)
return instance
# Create new instance
return Profile(**attrs)
If I understand your question correctly, you want a Nested Relationship. In your ProfileSerializer, you'll want:
attribute_answers = AttributeAnswerSerializer(many=True)
This will display all the attributes of each associated attribute_answer model and give you something like:
{
"id": 20,
"name": "Jake",
"active": true,
"type": {
"id": 1,
"name": "Human",
"active": true,
"created": "2013-02-12T13:31:06Z",
"modified": null
},
"user": "jason",
"attribute_answers": [
{"id": 1, "foo": "bar"},
{"id": 2, "foo": "bar2"}
]
}