I'm having trouble in using patch method inside a ModelViewSet for my custom User model. Here is the code:
views.py
@permission_classes([IsAdminUser])
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
http_method_names = ['get', 'post', 'patch', 'head', 'delete']
serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id',
'email',
'password',
'is_active',
'is_staff',
'is_admin')
extra_kwargs = {'password': {'write_only': True, 'required': True},
'is_active': {'default': True},
'is_staff': {'default': False},
'is_admin': {'default': False}}
def create(self, validated_data):
self.is_valid(raise_exception=True)
if validated_data['is_admin']:
validated_data['is_staff'] = True
user = User.objects.create_user(**validated_data)
return user
def update(self, instance, validated_data):
instance.email = validated_data.get('email')
instance.is_active = validated_data.get('is_active')
instance.is_staff = validated_data.get('is_staff')
instance.is_admin = validated_data.get('is_admin')
if instance.is_admin:
instance.is_staff = True
instance.save()
return instance
I set a permission in order to allow only superuser to access this view. The problem comes when trying to perform a patch. For example, if I try to change is_staff attribute to False of a User using Postman with PATCH and "http://localhost:8000/users/USER_ID", I get the following error:
django.db.utils.IntegrityError: null value in column "email" violates not-null constraint DETAIL: Failing row contains (1, pbkdf2_sha256$180000$6jePqBspO6xK$tCQ9zZk1uOdJD2F7L9BQRFFDbz1GXJ..., 2020-04-18 20:02:57.052639+00, null, t, t, t).
It seems like django asks me to provide every single field fo User model (email, is_active, is_staff, is_admin), which it doesn't make sense in my opinion givien that it's a PATCH request. To overcome the issue, I've modified the update method of my UserSerializer like that:
def update(self, instance, validated_data):
if validated_data.get('email') is not None:
instance.email = validated_data.get('email')
if validated_data.get('is_active') is not None:
instance.is_active = validated_data.get('is_active')
if validated_data.get('is_staff') is not None:
instance.is_staff = validated_data.get('is_staff')
if validated_data.get('is_admin') is not None:
instance.is_admin = validated_data.get('is_admin')
if instance.is_admin:
instance.is_staff = True
instance.save()
return instance
It works now, but I'd like to know whether this is a good solution. In my opinion, it is not a "canonical" way for solving the problem. I've also tried to override partial_update method (following others StackOverflow's answers) inside my UserViewSet, with no results.