I am creating a simple model with a many-to-many field. The model works fine and I can create model through the admin panel, and I can make a get request to see that model (except that it only returns user IDs instead of the user models/objects). My problem is when creating a post request to create said model.
I get one of the two errors depending on the changes I make, The serializer field might be named incorrectly and not match any attribute or key on the 'str' instance.
or AssertionError: You cannot call '.save()' on a serializer with invalid data.
, either way it has something to do with my serializer. The following is my model,
class Schema(models.Model):
week = models.PositiveIntegerField(primary_key=True,
unique=True,
validators=[MinValueValidator(1), MaxValueValidator(53)],
)
users = models.ManyToManyField(MyUser, related_name="users")
class Meta:
ordering = ('week',)
My View,
class SchemaView(APIView):
permission_classes = (SchemaPermissions,)
def get(self, request):
schemas = Schema.objects.all()
serializer = SchemaSerializer(schemas, many=True)
return Response(serializer.data)
def post(self, request):
data = request.data
serializer = SchemaSerializer(data=data)
serializer.is_valid()
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)
And my serializer,
class SchemaSerializer(serializers.ModelSerializer):
class Meta:
model = Schema
fields = ('week', 'users')
def create(self, validated_data):
users_data = validated_data.pop('users')
users = MyUser.objects.filter(id__in=users_data)
schema = Schema.objects.create(week=validated_data.week, users=users)
return schema
def update(self, instance, validated_data):
users_data = validated_data.pop('users')
users = MyUser.objects.filter(id__in=users_data)
instance.users.clear()
instance.users.add(*users)
instance.saver()
return instance
The idea is that if a week number already exists then it should call the update() function and then it should simply overwrite the users related to that week number, otherwise it should call create() and create a new week number with relations to the given users. The following is the result of printing the serializer after initializing it in the view.
SchemaSerializer(data={'week': 32, 'users': [1, 2, 3]}):
week = IntegerField(max_value=53, min_value=1, validators=[<UniqueValidator(queryset=Schema.objects.all())>])
users = PrimaryKeyRelatedField(allow_empty=False, many=True, queryset=MyUser.objects.all())
It seems to me that the serializer should be valid for the given model? I am perhaps missing some concepts and knowledge about Django and DRF here, so any help would be greatly appreciated!
CodePudding user response:
First you need set the field for saving users
in the SchemaSerializer
. And you don't need to customize the create
and update
method because the logic could be coded in the views
.
class SchemaSerializer(serializers.ModelSerializer):
users = UserSerializer(read_only = True, many = True)
user_ids = serializers.ListField(
child = serializers.IntegerField,
write_only = True
)
class Meta:
model = Schema
fields = ('week', 'users', 'user_ids',)
# remove create and update method
And in views.py,
class SchemaView(APIView):
permission_classes = (SchemaPermissions,)
def get(self, request):
...
def post(self, request):
data = request.data
serializer = SchemaSerializer(data=data)
if serializer.is_valid():
input_data = serializer.validated_data
week = input_data.get('week')
user_ids = input_data.get('user_ids')
if Schema.objects.filter(week = week).count() > 0:
schema = Schema.objects.get(week = week).first()
else:
schema = Schema.objects.create(week = week)
schema.users.set(user_ids)
schema.save()
return Response(SchemaSerializer(schema).data, status=status.HTTP_200_OK)
else:
print(serializer.errors)
return Response(serializer.errors, status = status.HTTP_400_BAD_REQUEST)
And of course, the payload data should be
{'week': 23, 'user_ids': [1,2,3]}