Django:CreateView内部的查询和计算

Django: Queries and calculations inside CreateView

我对 Django 比较陌生,所以我不确定我问的问题是否可行。

我正在建立一个网站,该网站具有对用户进行评分和撰写评论的功能。我有用户模型(具有平均评分字段)和评论模型(具有 authoruser_profilegradereview 字段)。我正在使用 CreateView 进行评论。

我正在尝试执行以下操作:

  1. 进行查询以获取该人以前的所有成绩(来自 Reviews 模型)。

  2. 进行计算(将所有以前的成绩相加,加上新成绩和所有除以成绩数(包括新成绩))

  3. 将新的平均成绩保存到 UserProfile 模型

  4. 将评论保存到 Reviews 模型

  5. 将用户重定向到当前详细信息视图

Models.py

class UserProfile(models.Model):
    ...
    avg_grade = models.FloatField(blank=True, null=True)
    ...

class Reviews(models.Model):
    user_profile = models.ForeignKey(UserProfile, on_delete=models.CASCADE)
    grade = models.PositiveIntegerField()
    review = models.CharField(max_length=256, null=True, blank=True)
    author = models.CharField(max_length=256)

views.py 中,我设法查询了该用户的成绩,但不确定在哪里计算新的平均成绩(如果这在 Class-Based-View 中是可能的) :

class CreateReview(LoginRequiredMixin, CreateView):
    form_class = Forma_recenzije
    success_url = reverse_lazy('detail')
    template_name = 'accounts/recenzija.html'

    def get_queryset(self):
        u = UserProfile.objects.get(id=int(self.kwargs['pk']))
        return Reviews.objects.filter(user_profile=u)

    def form_valid(self, form):
        form.instance.author = self.request.user
        form.instance.user_profile = UserProfile.objects.get(id=int(self.kwargs['pk']))
        return super(CreateReview, self).form_valid(form)

网址模式:

[...
    url(r'^dadilje/(?P<pk>[-\w]+)/$', views.DadiljaDetailView.as_view(), name="detail"),
    url(r'^dadilje/(?P<pk>[-\w]+)/recenzija$', views. CreateReview.as_view(), name="recenzije")
...
]

您可以在调用 super() 之后计算新的平均值,然后再 return 响应。

def form_valid(self, form):
    form.instance.author = self.request.user
    user_profile = UserProfile.objects.get(id=int(self.kwargs['pk']))
    form.instance.user_profile = user_profile`
    response = super(CreateReview, self).form_valid(form)
    avg_grade = Review.objects.filter(user_profile=user_profile).aggregate(Avg('grade'))['grade__avg']
    user_profile.avg_grade = avg_grade
    user_profile.save()
    return response

或者,如果您发现调用 super() 很难看清发生了什么,您可以显式保存表单并改为重定向:

def form_valid(self, form):
    form.instance.author = self.request.user
    user_profile = UserProfile.objects.get(id=int(self.kwargs['pk']))
    form.instance.user_profile = user_profile`
    review = form.save()
    avg_grade = Review.objects.filter(user_profile=user_profile).aggregate(Avg('grade'))['grade__avg']
    user_profile.avg_grade = avg_grade
    user_profile.save()
    return HttpResponseRedirect(self.success_url)

请注意,您可能不必将 avg_grade 存储在用户配置文件中——您可以在需要时使用 annotate 来计算平均值。

我认为最好的解决方案是使用 django signals

因此,您将视图和域逻辑分开,您的计算将在每次更改后应用(不仅是视图的更改)

此外,如果您的计算会花费大量时间,您可以轻松地将此功能移动到异步作业中(例如 celery

对于您想做的事情,Django 有您可以监听的信号。

例如,您可以使用一个函数来侦听 UserProfile 何时保存,从而清除与该配置文件相关的缓存键。

这些函数通常会添加到您的应用程序中的 signals.py 中,或者在您定义模型后添加到 models.py 文件中。

信号必须在你的模型之后加载,所以如果使用 signals.py 我倾向于这样做的方式是 apps.py;

class MyAppConfig(AppConfig):
    """App config for the members app. """
    name = 'my app'
    verbose_name = _("My App")

    def ready(self):
        """ Import signals once the app is ready """
        # pylint: disable=W0612
        import myapp.signals  # noqa

这是一个信号接收器的示例,pre_save 发生在对象被保存之前,因此您可以 运行 此时您的计算;

@receiver(pre_save, sender=UserProfile)
def userprofile_pre_save(sender, instance, **kwargs):
    """
    Calc avg score
    """
    reviews = Reviews.objects.filter(user_profile=instance).aggregate(Avg('grade'))

    instance.avg_grade = reviews['grade_avg']

您可能希望接收器处于 Review 变化,但上面是一个简单的例子!!

如果您是 django 的新手,这可能有点复杂,但请阅读一下; https://simpleisbetterthancomplex.com/tutorial/2016/07/28/how-to-create-django-signals.html