3

I am working on a simple performance management system with react on frontend and django on the backend. They are supervisors who can give reviews to supervisees and supervisees can respond. I want all employees to receive email when they receive reviews from their supervisors and all the supervisors to receive email when their reviews are responded. For reviews and responses I am using two different serializers but same model.

Serializer for Response is:

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

For sending mail I am using send_mail method from django.core And I am using Viewsets for Reviews and Responses.

Now Response operation will always be an update operation because Response will always be used to update existing Review object in which response_text field will be updated.

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)

So, the problem that I am facing is that when I try to send mail with update method in ResponseViewset as shown above. I get the following error:

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'

So, what is happening is due to some reason, all the fields of that particular review are getting set to null as soon as I try to update and hence getting NoneType object has no attribute. I have checked in database table(MySQL), all the fields are getting set to null. Can anyone tell me why is this happening ? Where am I going wrong ? And what is the correct way to do it?

1 Answer 1

2

Finally I found my solution by changing the method update to partial_update. Apparently update method updates all the field while in above case I am attempting the field called response_text in the Review model which are setting other fields to null if they could be. Also after doing that I had to change the request from PUT to PATCH in frontend. Also I had to do some other minor coding changes like removing supervisor and supervisee fields from read_only_fields from ResponseSerializer. Updated code for ResponseViewset is shown below:

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)
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.