如何在 Django Rest Framework 的视图集中使用更新方法并执行一些任务(如发送邮件)?

How to use update method in viewsets in Django Rest Framework and perform some task(like sending mail) along with it?

我正在开发一个简单的性能管理系统,前端有反应,后端有 django。他们是主管,可以对受监管者进行评论,受监管者可以做出回应。我希望所有员工在收到主管的评论时都能收到电子邮件,并且所有主管在他们的评论得到回复时都能收到电子邮件。对于评论和回复,我使用了两个不同的序列化器,但模型相同。

响应的序列化器是:

class ResponseSerializer(serializers.ModelSerializer):
    supervisor_name = serializers.SerializerMethodField('get_supervisor_name')
    supervisor_email = serializers.SerializerMethodField('get_supervisor_email')
    supervisee_name = serializers.SerializerMethodField('get_supervisee_name')
    supervisee_email = serializers.SerializerMethodField('get_supervisee_email')

    class Meta:
        model = Review
        fields = (
            'id', 'review_text', 'response_text', 'date_of_review', 'date_of_response', 'supervisor', 'supervisor_name',
            'supervisor_email', 'supervisee', 'supervisee_name', 'supervisee_email')
        read_only_fields = ('review_text', 'date_of_review', 'supervisor', 'supervisee')

    def get_supervisor_name(self, obj):
        return obj.supervisor.first_name + " " + obj.supervisor.last_name

    def get_supervisor_email(self, obj):
        return obj.supervisor.email

    def get_supervisee_name(self, obj):
        return obj.supervisee.first_name + " " + obj.supervisee.last_name

    def get_supervisee_email(self, obj):
        return obj.supervisee.email

为了发送邮件,我正在使用 django.core 中的 send_mail 方法,并且我正在使用视图集进行评论和回复。

现在 Response 操作将始终是更新操作,因为 Response 将始终用于更新 response_text 字段将被更新的现有 Review 对象。

class ResponseViewSet(viewsets.ModelViewSet):
    queryset = Review.objects.all()
    permission_classes = [
        # permissions.IsAuthenticated,
        permissions.AllowAny,
    ]
    serializer_class = ResponseSerializer

    def update(self, request, *args, **kwargs):
        serializer = ResponseSerializer(data=request.data)

        if serializer.is_valid():
            supervisor = serializer.data["supervisor_name"]
            supervisee = serializer.data["supervisee_name"]
            query = serializer.save()
            mail_text = "Hi {}\n\nYou got a response for your 1:1 from {}.\n\nClick below to see the response:\n\n{}".format(
                supervisor,
                supervisee,
                "https://example.com/#/pms/reviewsBySupervisor",
            )
            try:
                if not settings.DEFAULT_EMAIL_RECIPIENTS:
                    settings.DEFAULT_EMAIL_RECIPIENTS.append(
                        str(serializer.data["supervisor_email"])
                    )
                send_mail(
                    subject="New Response Received",
                    message=mail_text,
                    from_email=settings.DEFAULT_FROM_EMAIL,
                    recipient_list=settings.DEFAULT_EMAIL_RECIPIENTS,
                    fail_silently=False,
                )
            except (SMTPRecipientsRefused, SMTPSenderRefused):
                LOGGER.exception("There was a problem submitting the form.")
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

所以,我面临的问题是,当我尝试在 ResponseViewset 中使用 update 方法发送邮件时,如上所示。我收到以下错误:

Internal Server Error: /UMS/api/responses/38/
Traceback (most recent call last):
  File "/home/shishir/Projects/performance_management/performance_management/reviews/serializers.py", line 77, in get_supervisor_name
    return obj.supervisor.first_name + " " + obj.supervisor.last_name
AttributeError: 'NoneType' object has no attribute 'first_name'

所以,发生的事情是由于某种原因,当我尝试更新并因此获得 NoneType object has no attribute 时,该特定评论的所有字段都设置为 null。我检查了数据库 table(MySQL),所有字段都设置为空。谁能告诉我为什么会这样?我哪里错了?正确的做法是什么?

最后我通过将方法 update 更改为 partial_update 找到了解决方案。显然,更新方法更新了所有字段,而在上述情况下,我正在尝试 Review 模型中名为 response_text 的字段,如果可能的话,将其他字段设置为 null。同样在这样做之后,我不得不在前端将请求从 PUT 更改为 PATCH。此外,我还必须进行一些其他较小的编码更改,例如从 ResponseSerializer 中删除 read_only_fields 中的 supervisorsupervisee 字段。 ResponseViewset 的更新代码如下所示:

class ResponseViewSet(viewsets.ModelViewSet):
    queryset = Review.objects.all()
    permission_classes = [
        permissions.IsAuthenticated,
        #permissions.AllowAny,
    ]
    serializer_class = ResponseSerializer

    def partial_update(self, request,*args, **kwargs):
        obj = self.get_object()
        serializer = self.serializer_class(obj, data=request.data, partial=True)
        if serializer.is_valid():
            serializer.save()
            mail_text = "Hi {},\n\nYou got a response for your 1:1 from {}.\n\nClick below to see the response:\n\n{}".format(
                serializer.data["supervisor_name"],
                serializer.data["supervisee_name"],
                get_product_link("UMS/reviewsForDR"),
            )
            try:
                if not settings.DEFAULT_EMAIL_RECIPIENTS:
                    settings.DEFAULT_EMAIL_RECIPIENTS.append(
                        # supervisor_email
                        serializer.data["supervisor_email"]
                    )

                send_mail(
                    subject="New Response Received",
                    message=mail_text,
                    from_email=settings.DEFAULT_FROM_EMAIL,
                    recipient_list=settings.DEFAULT_EMAIL_RECIPIENTS,
                    fail_silently=False,
                )
                settings.DEFAULT_EMAIL_RECIPIENTS = []
            except (SMTPRecipientsRefused, SMTPSenderRefused):
                LOGGER.exception("There was a problem submitting the form.")
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)