0

I was searching the net and found the similar problem but it doesn't work in my case, Idk why. I try to put some extra data to context in serializer, but get only 3 default fields: request, view and format and no mention for my custom data.

My model:

class Match(models.Model):

sender = models.ForeignKey(
    Client,
    on_delete=models.CASCADE,
    related_name='senders'
)
recipient = models.ForeignKey(
    Client,
    on_delete=models.CASCADE,
    related_name='recipients'
)

class Meta:
    app_label = 'clients'
    db_table = 'matches'
    verbose_name = 'match'
    verbose_name_plural = 'matches'
    constraints = [
        models.UniqueConstraint(
            fields=['sender', 'recipient'],
            name='unique_match'
        )
    ]

My Serializer:

class MatchSerializer(serializers.ModelSerializer):

sender = serializers.HiddenField(default=serializers.CurrentUserDefault())

def validate(self, data):
    if data['sender'] == self.context['recipient']:
        raise serializers.ValidationError('You cannot match yourself')
    return data

def create(self, validated_data):

    return Match.objects.create(
        sender=validated_data['sender'],
        recipient=self.context['recipient']
    )

class Meta:
    model = Match
    fields = ['sender']`

My ModelViewSet:

class MatchMVS(ModelViewSet):
    queryset = Match.objects.all()
    serializer_class = MatchSerializer
    http_method_names = ['post']
    permission_classes = [IsAuthenticated]

    # without and with doesn't work
    def get_serializer_context(self):
        context = super(MatchMVS, self).get_serializer_context()
        context.update({
            "recipient": Client.objects.get(pk=23)
            # extra data
        })
        return context

    @action(detail=True, methods=['POST'], name='send_your_match')
    def match(self, request, pk=None):
        sender = request.user
        recipient = Client.objects.get(pk=pk)
        serializer = MatchSerializer(context={'request': request, 'recipient': recipient},
                                     data=request.data)

        data_valid = serializer.is_valid(raise_exception=True)
        if data_valid:
            recipient = serializer.save()

        is_match = Match.objects.filter(sender=recipient, recipient=sender).exists()
        if is_match:
            send_mail(
                f'Hello, {sender.first_name}',
                f'You got match with {recipient.first_name}! '
                f'Your`s partner email: {recipient.email}',
                settings.EMAIL_HOST_USER,
                [sender.email],
                fail_silently=False,
            )
            send_mail(
                f'Hello, {recipient.first_name}',
                f'You got match with {sender.first_name}! '
                f'Your`s partner email: {sender.email}',
                settings.EMAIL_HOST_USER,
                [recipient.email],
                fail_silently=False,
            )

            return Response(recipient.email, 201)
        else:
            return Response(f'Your sympathy has been sent to {recipient.username}.', 201)

I was debugging it through PyCharm and Postman but still can't understand where is my mistake. Hope you will help me to fix it.

max_bstr
  • 15
  • 8
  • I think this should already be working as expected. Could you print `self.context` inside serializer's validate method before if statement? – annonymous Mar 28 '22 at 03:34
  • As expected: ```{'request': , 'format': None, 'view': }``` – max_bstr Mar 28 '22 at 11:20
  • What happens if you print `serializer.context` before `serializer.is_valid` in `match` method of your view. – annonymous Mar 28 '22 at 11:21
  • The program does not reach this point, apparently for the reason that ```serializer = MatchSerializer(context={'request': request, 'recipient': recipient}, data=request.data)``` calls the create method, which now prescribes the use of context – max_bstr Mar 28 '22 at 11:33
  • Maybe there is some way to transfer data via `serializer.data`? – max_bstr Mar 28 '22 at 11:34
  • I think you call the wrong API. It should reach that line since `.create` method of a serializer is called when you call `serializer.save()`. – annonymous Mar 28 '22 at 11:35
  • My url: ```path('clients/get/', views.MatchMVS.as_view({'post': 'match'}), name='get_client'``` I'm trying to debug `match` function logic but but pycharm does not respond to breakpoint – max_bstr Mar 28 '22 at 11:46
  • From your previous comment where you print `self.context` in `validate` method, the request object is ``. Shouldn't you call this API `/api/clients/get/23/match` instead? – annonymous Mar 28 '22 at 11:50
  • I tryied to use `ViewSet` ```serializer = MatchSerializer(data={'sender': sender, 'recipient': recipient})``` `Serializer` ```class MatchSerializer(serializers.ModelSerializer): def validate(self, data): if data['sender'] == data['recipient']: raise serializers.ValidationError('You cannot match yourself') return data class Meta: model = Match fields = ['sender', 'recipient']``` – max_bstr Mar 28 '22 at 11:55
  • For this I send POST request to ```api/clients/23/match``` and get response from Postman ```{"sender": ["This field is required."], "recipient": ["This field is required."]}``` – max_bstr Mar 28 '22 at 11:56
  • Could you try the one I've told in the previous comment first, before modifying the serializer? – annonymous Mar 28 '22 at 11:57
  • Response is 404 for `/api/clients/get/23/match` I use `url` instead of `router` – max_bstr Mar 28 '22 at 12:02
  • If you type http://localhost:8000/api/ in your browser, what does it show? – annonymous Mar 28 '22 at 12:13
  • I found an error, it turned out that I tied the wrong model view set to the url, it turned out very stupidly, thanks for the help, now everything works – max_bstr Mar 28 '22 at 12:15
  • No problem. I'm glad I can help. :) – annonymous Mar 28 '22 at 12:16

2 Answers2

0

Your code is right.It should work. But i don't know where is the issue.You can try the modified code as below.

class MatchSerializer(serializers.ModelSerializer):

    def validate(self, data):
        if data['sender'] == data['recipient']:
            raise serializers.ValidationError('You cannot match yourself')
        return data
    class Meta:
        model = Match

        fields = ['sender', 'recipient']

class MatchMVS(ModelViewSet):
    queryset = Match.objects.all()
    serializer_class = MatchSerializer
    http_method_names = ['post']
    permission_classes = [IsAuthenticated]

    @action(detail=True, methods=['POST'], name='send_your_match')
    def match(self, request, pk=None):
        sender = request.user
        recipient = Client.objects.get(pk=pk)
        serializer = MatchSerializer(data={**request.data, 'recipient': recipient.id})

        data_valid = serializer.is_valid(raise_exception=True)
        if data_valid:
            recipient = serializer.save()

        is_match = Match.objects.filter(sender=recipient, recipient=sender).exists()
        if is_match:
            send_mail(
                f'Hello, {sender.first_name}',
                f'You got match with {recipient.first_name}! '
                f'Your`s partner email: {recipient.email}',
                settings.EMAIL_HOST_USER,
                [sender.email],
                fail_silently=False,
            )
            send_mail(
                f'Hello, {recipient.first_name}',
                f'You got match with {sender.first_name}! '
                f'Your`s partner email: {sender.email}',
                settings.EMAIL_HOST_USER,
                [recipient.email],
                fail_silently=False,
            )

            return Response(recipient.email, 201)
        else:
            return Response(f'Your sympathy has been sent to {recipient.username}.', 201)
NANDHA KUMAR
  • 465
  • 4
  • 11
  • I tried your method but it still doesn't work. Why u deleted fields in serializers? How we will send data there ```serializer = MatchSerializer(data={**request.data, 'recipient': recipient.id})``` and in my case I need to send client instance in order not to make an additional request to the database – max_bstr Mar 28 '22 at 11:19
  • I thought to do without `context`, but then I need to make a `field` in the serializer under recipient, but what type I do not know and whether it will work – max_bstr Mar 28 '22 at 11:24
  • Can u update the code currently you have modified and checked? – NANDHA KUMAR Mar 28 '22 at 11:34
  • I used your code for the test, but postman gives this message `{"sender": ["This field is required."], "recipient": ["This field is required."]}` I guess he doesn't like that I specified fields in `fields`, but apparently it means that I should create them in the serializer – max_bstr Mar 28 '22 at 11:43
  • Please add your postman screenshot if you are okay. – NANDHA KUMAR Mar 28 '22 at 11:59
  • Sending match from id: 24 to id: 1 `"Your sympathy has been sent to max_bstr."` Sending match from id: 1 to id: 24 ```Your sympathy has been sent to mail_test``` – max_bstr Mar 28 '22 at 12:36
0

The problem was my own inattention, the wrong viewset was linked to the url

urlpatterns = [
    path('clients/<int:pk>/match', views.MatchCreateAPIView.as_view(), name='get_match')
]

instead of:

urlpatterns = [
    path('clients/<int:pk>/match', views.MatchMVS.as_view({'post': 'match'}), name='get_match')
]
max_bstr
  • 15
  • 8