Reputation: 147
I have m2m related entities:
class Tag(models.Model):
name = models.CharField(
max_length=80,
blank=False,
unique=True
)
class File(models.Model):
tags = models.ManyToManyField(Tag)
uploader = models.ForeignKey(User, on_delete=models.DO_NOTHING)
name = models.CharField(
max_length=255,
blank=False,
unique=True,
)
and serializers for them:
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = ('id', 'name')
extra_kwargs = {
'name': {
'validators': [],
},
}
class FileSerializer(serializers.ModelSerializer):
tags = TagSerializer(many=True, read_only=False)
class Meta:
model = File
fields = ('id', 'name', 'tags')
def update(self, instance, validated_data):
tags_data = validated_data.pop('tags')
if 'name' in validated_data:
instance.name = validated_data['name']
instance_tags = instance.tags.all()
new_tag_list = []
# add new tags
for tag in tags_data:
tag, created = Tag.objects.get_or_create(name=tag['name'])
new_tag_list.append(tag)
if tag not in instance_tags:
instance.tags.add(tag)
# remove old tags
for instance_tag in instance_tags:
if instance_tag not in new_tag_list:
instance.tags.remove(instance_tag)
return instance
as you can see I turned off all validation for Tag.name field in TagSerializer by this code setting: extra_kwargs = {'name': {'validators': []}}
This is because of validation errors during saving updated File:
serializer = FileSerializer(file, data=request.data, partial=True)
if serializer.is_valid():
...
If you have File with list of tags and just want to add or remove some tags, you'll have error 'tag with such name already exists'.
So this spike is working, but for now I don't have unique check for Tag.name. I tried to write something like what in FileSerializer:
class FileSerializer(serializers.ModelSerializer):
class Meta:
model = File
extra_kwargs = {
'tags': {
'validators': [],
},
}
but of course it's not working. Question is about any possibilities to not validate Tag entities at all when I add/remove its bindings with file.
Upvotes: 4
Views: 6606
Reputation: 11
You can also get tags from instance, no need to send whole request through context.
tags_data = self.initial_data.get('tags', [])
Upvotes: 0
Reputation:
You can try, this way for update
, but if this solution correct for you, you need also override the create
method.
class FileSerializer(serializers.ModelSerializer):
tags = TagSerializer(many=True, read_only=True)
# ^^^^^^
class Meta:
model = File
fields = ('id', 'name', 'tags')
def update(self, instance, validated_data):
# Override tags_data = validated_data.pop('tags')
# this way
request = self.context['request']
tags_data = request.data.get('tags', [])
# Next your current code
in serializer initial add extra-context , when you use views from drf
context added by get_serializer
method.
serializer = FileSerializer(file, data=request.data, partial=True, context={'request': request})
Upvotes: 2