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:
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
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)