【发布时间】:2021-07-01 12:29:02
【问题描述】:
我有两个模型。小说模型可以是任何电影、书籍、电视剧或类似的东西。我有另一个模型,它是包含类似评论的字段的评论,例如:评论者、评级、描述。 我想要做的是以下内容: 在虚构模型中有两个额外的字段:
- 评论数
- 平均评分
我能够将它们添加为整数和浮点字段,然后在添加、编辑或删除新评论时更改它们,但有两个问题。
- 不会考虑添加来自管理员的评论
- 我只是觉得这不是最好的方法,而且我觉得有一种更明智的方法可以从审查模型中自动填充字段。 这是两个模型以及我如何实现 api 视图。
小说模型.py
from django.db import models
from stream.models import StreamPlatform
class Fiction(models.Model):
"""
Model that encopasses a Movie, TV Series, book or similar
"""
MOVIE = 1
TV_SERIES = 2
BOOK = 3
PODCAST = 4
TYPE = (
(MOVIE, 'Movie'),
(TV_SERIES, 'TV-Series'),
(BOOK, 'Book'),
(PODCAST, 'Podcast')
)
title = models.CharField(max_length=50)
description = models.CharField(max_length=200)
active = models.BooleanField(default=True)
created = models.DateTimeField(auto_now_add=True)
avg_rating = models.FloatField(default=0)
number_rating = models.IntegerField(default=0)
platform = models.ForeignKey(
StreamPlatform,
on_delete=models.SET_NULL,
related_name='fictions',
null = True
)
type = models.PositiveSmallIntegerField(
choices = TYPE,
default = MOVIE
)
def __str__(self):
return self.title
查看 models.py
from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator
from django.contrib.auth.models import User
from fiction.models import Fiction
class Review(models.Model):
"""
model for fiction reviews from users
"""
reviewer = models.ForeignKey(User, on_delete=models.CASCADE)
rating = models.PositiveSmallIntegerField(validators=[MinValueValidator(1), MaxValueValidator(5)])
fiction = models.ForeignKey(Fiction, on_delete=models.CASCADE, related_name="reviews")
description = models.CharField(max_length=200, null = True, blank =True)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
def __str__(self):
return str(self.rating) + " | " + str(self.fiction)
class Meta:
unique_together = ['reviewer', 'fiction']
ordering = ['-created']
查看views.py
from watchmate.permissions import IsAdminOrReadOnly, IsOwnerOrReadOnly
from rest_framework import generics, mixins
from rest_framework.exceptions import ValidationError
from rest_framework.permissions import IsAuthenticatedOrReadOnly
from django.db.utils import IntegrityError
from fiction.models import Fiction
from .models import Review
from .serializers import ReviewSerializer
class ReviewList(generics.ListCreateAPIView):
permission_classes = [IsAdminOrReadOnly]
# queryset = Review.objects.all()
serializer_class = ReviewSerializer
def get_queryset(self):
pk = self.kwargs['pk']
return Review.objects.filter(fiction=pk)
def perform_create(self, serializer):
pk = self.kwargs.get('pk')
fiction = Fiction.objects.get(pk=pk)
reviewer = self.request.user
# check if user has already reviewd this fiction
# review = Review.objects.filter(fiction=fiction, reviewer=reviewer)
# if review.exists():
# raise ValidationError("You have already reviewed this fiction")
try:
serializer.save(fiction=fiction, reviewer=reviewer)
if fiction.number_rating == 0:
fiction.avg_rating = serializer.validated_data['rating']
else:
fiction.avg_rating = (serializer.validated_data['rating']+fiction.avg_rating)/2
fiction.number_rating += 1
fiction.save()
except IntegrityError:
raise ValidationError("You have already reviewed this fiction")
class ReviewDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Review.objects.all()
serializer_class = ReviewSerializer
permission_classes = [IsOwnerOrReadOnly]
def perform_update(self, serializer):
instance = self.get_object()
pk = self.kwargs.get('fiction_pk')
fiction = Fiction.objects.get(pk=pk)
# calculate overall rating sums
total_rating = fiction.avg_rating*fiction.number_rating
# subtract old rating
total_rating -= instance.rating
# add new rating
total_rating += serializer.validated_data['rating']
# calculate new fiction avg rating and save it
fiction.avg_rating = total_rating/fiction.number_rating
fiction.save()
super().perform_update(serializer)
def perform_destroy(self, instance):
instance = self.get_object()
pk = self.kwargs.get('fiction_pk')
fiction = Fiction.objects.get(pk=pk)
# calculate overall rating sums
total_rating = fiction.avg_rating*fiction.number_rating
# subtract old rating
total_rating -= instance.rating
# decrease fiction reviews by one
fiction.number_rating -= 1
# calculate new fiction avg rating and save it
fiction.avg_rating = total_rating/fiction.number_rating
fiction.save()
super().perform_destroy(instance)
添加新评论的逻辑不是 100% 正确,但没关系。我只是想知道如何能够在模型和字段级别实现此逻辑,因此也可以使用管理员添加评论并且仍然能够看到更改
【问题讨论】:
-
请不要使用字段。您可以使用属性或注释,但不要具体化数据库中的聚合数据。
-
是否可以告诉我如何添加这样的属性或注释?我真的很感激
标签: python django django-models django-rest-framework django-views