Search code examples
curldjango-modelsrequestdjango-rest-frameworkupdatemodel

How to request.method==PUT, POST on Django rest framework


I am working with Django REST Framework serializing some models.

This is my Match serializer model

class MatchSerializer(serializers.ModelSerializer):

    field = serializers.SlugRelatedField(queryset=Field.objects.all(),slug_field='name')

    class Meta:
        model = Match
        fields = ('url', 'id', 'home_team', 'away_team', 'field', 'match_date', 'check_match_away_team', 'status_challenge', 'home_team_players_accept', 'away_team_players_accept', 'home_team_players_cancel', 'away_team_players_cancel', 'fichaje_players_match',)

In the Match model, the fields are such as follow:

  • home_team, away_team attributes, are a ForeingKey's to my Team model and not null

  • field attribute is a ForeignKey too and not null.

  • status_challenge is a CharField and not null

  • home_team_players_accept, away_team_players_accept, home_team_players_cancel, away_team_players_cancel, and fichaje_players_match fields have a ManyToMany relationship to my custom User model. These were defined of this way in my Match model such as follow:

    class Match(models.Model):
    
        home_team_players_accept = models.ManyToManyField(
            settings.AUTH_USER_MODEL,
            related_name='home_team_players_accept',
            blank=True,)
    
        away_team_players_accept = models.ManyToManyField(
            settings.AUTH_USER_MODEL,
            related_name='away_team_players_accept',
            blank=True,)
    
        home_team_players_cancel = models.ManyToManyField(
            settings.AUTH_USER_MODEL,
            related_name='home_team_players_cancel',
            blank=True,)
    
        away_team_players_cancel = models.ManyToManyField(
            settings.AUTH_USER_MODEL,
            related_name='away_team_players_cancel',
            blank=True,)
    
        fichaje_players_match = models.ManyToManyField(
            settings.AUTH_USER_MODEL,
            related_name='fichaje_players_match',
            blank=True,)
    

My situation is when I am update one Match object via curl and through my mobile iOS app client, I have the following case:

I have a record Match of this way:

{
    "url": "https://myserver/api/matchs/29/",
    "id": 29,
    "home_team": "Manchester United",
    "away_team": "Sempiternos FC",
    "field": "Joga Bonito",
    "match_date": "2017-01-02T09:00:05Z",
    "check_match_away_team": true,
    "status_challenge": "Aceptado",
    "home_team_players_accept": [
        "paoc",
        "nuevo"
    ],
    "away_team_players_accept": [
        "mike",
        "giraldoesteban"
    ],
    "home_team_players_cancel": [
        "erick"
    ],
    "away_team_players_cancel": [
        "Bluse"
    ],
    "fichaje_players_match": [
        "Oliver"
    ]
}

When I have PUT operation via curl tool command to this record, perform my update just in the status_challenge field changing their value from Aceptado to Pendiente this update PUT is done, with my home_team_players_accept, away_team_players_accept, home_team_players_cancel, away_team_players_cancel, and fichaje_players_match array fields are set to blank or empty, their values are remove:

bgarcial@elpug : ~
[0] % curl -X PUT https://myserver/api/matchs/29/ -d "home_team=Manchester United&away_team=Sempiternos FC&field=Joga Bonito&match_date=2017-01-2T09:00:05Z&check_match_away_team=true&status_challenge=Pendiente" 
{"url":"https://myserver/api/matchs/29/","id":29,"home_team":"Manchester United","away_team":"Sempiternos FC","field":"Joga Bonito","match_date":"2017-01-02T09:00:05Z","check_match_away_team":true,"status_challenge":"Pendiente","home_team_players_accept":[],"away_team_players_accept":[],"home_team_players_cancel":[],"away_team_players_cancel":[],"fichaje_players_match":[]}%           
bgarcial@elpug : ~
[0] % 

Currently my Match record is of this way:

enter image description here

This also occur when I perform the update via my mobile application which consume the API

I try work with the case when request.method == 'PUT' of this way in my viewset:

class MatchViewSet(viewsets.ModelViewSet):

    queryset = Match.objects.all()
    serializer_class = MatchSerializer
    filter_fields = ('home_team','away_team', 'status_challenge', 'fichaje_players_match', )

    @api_view(['PUT'])
    def match_detail(request, pk):
        if request.method == 'PUT':
            serializer = MatchSerializer()
            if serializer.is_valid():
                serializer.save()
                return Response(serializer)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

And the result is the same, I unknown in this moment how to address this situation and the guide is not enough clear for me

How to can I update the status_challenge field of the model serializer Match without that the players field referenced above stayed in blank or remove their content?


UPDATE

The transitivity is of some way such as follow:

There is my custom User model

class User(AbstractBaseUser, PermissionsMixin):
    ... other attributes ...   

    team = models.ForeignKey(
        'games_information.Team',
        null=True,
        blank=True,
        verbose_name='Equipo en el que juega',    
    )

The User serializer:

class UserSerializer(serializers.ModelSerializer):
    username = serializers.CharField(validators=[UniqueValidator(queryset=User.objects.all(), message='Lo sentimos, existe un fichaje con este nombre de usuario')])
    email = serializers.EmailField(validators=[UniqueValidator(queryset=User.objects.all(), message='Lo sentimos, alguien ya ha sido fichado con este correo electrónico')])

    def create(self, validated_data):
        password = validated_data.pop('password', None)
        instance = self.Meta.model(**validated_data)
        if password is not None:
            instance.set_password(password)
        instance.save()
        return instance

    def update(self, instance, validated_data):
        for attr, value in validated_data.items():
            if attr == 'password':
                instance.set_password(value)
            else:
                setattr(instance, attr, value)
        instance.save()
        return instance

    class Meta:
        model = User
        fields = ('url', 'username', 'password', 'first_name','last_name',
                  'age', 'sex', 'photo', 'email', 'is_player', 'team',
                  'position', 'is_staff', 'is_active', 'is_superuser',
                  'is_player', 'weight', 'height', 'nickname',
                  'number_matches', 'accomplished_matches',
                  'time_available', 'leg_profile', 'number_shirt_preferred',
                  'team_support', 'player_preferred', 'last_login',)

The Team model have a ManyToMany field to User model:

class Team(models.Model):

    ... other fields ...

    players = models.ManyToManyField(
        settings.AUTH_USER_MODEL,
        related_name='players',
        blank=True,
    )

This is my TeamSerializer

class TeamSerializer(serializers.ModelSerializer):

    name = serializers.CharField(validators=[UniqueValidator(queryset=Team.objects.all(), message='Lo sentimos, ya existe un equipo con este nombre')])
    place_origin = serializers.SlugRelatedField(queryset=Field.objects.all(),slug_field='name')

    class Meta:
        model = Team
        fields = ('url', 'name', 'image', 'players', 'modality', 'branch', 'category', 'category_name', 'place_origin', 'game_day',)

The Match model, have the home_team_players_accept, away_team_players_accept, home_team_players_cancel, away_team_players_cancel and fichaje_players_match fields, all in ManyToMany relationship with the User model (settings.AUTH_USER_MODEL)

class Match(models.Model):

       ... other fields ...
       STATUS_CHALLENGE_CHOICES = (
           (ACCEPTED_CHALLENGE, u'Aceptado'),
           (PENDING_CHALLENGE, u'Pendiente'),
           (CANCELLED_CHALLENGE, u'Cancelado'),
           (FICHAJE, u'Fichaje'),
       ) 
       status_challenge = models.CharField(
           choices=STATUS_CHALLENGE_CHOICES,
           max_length=40,
           default=True,
           blank=False,
           verbose_name='Estado del desafío'
        )
        home_team_players_accept = models.ManyToManyField(
            settings.AUTH_USER_MODEL,
            related_name='home_team_players_accept',
            blank=True,)

        away_team_players_accept = models.ManyToManyField(
            settings.AUTH_USER_MODEL,
            related_name='away_team_players_accept',
            blank=True,)

        home_team_players_cancel = models.ManyToManyField(
            settings.AUTH_USER_MODEL,
            related_name='home_team_players_cancel',
            blank=True,)

        away_team_players_cancel = models.ManyToManyField(
            settings.AUTH_USER_MODEL,
            related_name='away_team_players_cancel',
            blank=True,)

        fichaje_players_match = models.ManyToManyField(
            settings.AUTH_USER_MODEL,
            related_name='fichaje_players_match',
            blank=True,)

This is my Match serializer:

class MatchSerializer(serializers.ModelSerializer):

        field = serializers.SlugRelatedField(queryset=Field.objects.all(),slug_field='name')

        class Meta:
            model = Match
            fields = ('url', 'id', 'home_team', 'away_team', 'field', 'match_date', 'check_match_away_team', 'status_challenge', 'home_team_players_accept', 'away_team_players_accept', 'home_team_players_cancel', 'away_team_players_cancel', 'fichaje_players_match',)

The inconvenient is that I described initially above. When I apply to update to Match endpoint record, in the field status_challenge the records in the home_team_players_accept, away_team_players_accept, home_team_players_cancel, away_team_players_cancel and fichaje_players_match fields are removed or set to blank


Solution

  • You need partial update. PUT calls update() and PATCH calls partial_update(). The only difference is partial=True (PATCH) or partial=False (PUT). And you can set partial yourself when initializing the serializer in your views.

    You can do it like in Django REST framework code (mixins.py), to perform partial updates with PUT:

    def update(self, request, *args, **kwargs):
        kwargs['partial'] = True
        return super(YourCustomView, self).update(request, *args, **kwargs)