Custom create method to prevent duplicates
<p>I would like to add some logic to my serializer.py.</p>

<p>Currently it creates duplicate tags (giving a new ID to the item, but often it will match a tag name already).</p>

<h1>In plain english</h1>

<pre><code>if exists:
   # Find the PK that matches the "name" field
   # "link" the key with Movie Class item
else:
   # Create the "name" inside of Tag class
   # "link" the key with Movie Class item
</code></pre>

<h1>The data being posted looks like this:</h1>

<pre><code>{
  "title": "Test",
  "tag": [
    {
      "name": "a",
      "taglevel": 1
    }
  ],
  "info": [

  ]
}
</code></pre>

<h1>Models.py</h1>

<pre><code>class Tag(models.Model):
    name = models.CharField("Name", max_length=5000, blank=True)
    taglevel = models.IntegerField("Tag level", blank=True)
    def __str__(self):
        return self.name

class Movie(models.Model):
    title = models.CharField("Whats happening?", max_length=100, blank=True)
    tag = models.ManyToManyField('Tag', blank=True)
    def __str__(self):
        return self.title
</code></pre>

<hr>

<h2>Serializers</h2>

<pre><code>class MovieSerializer(serializers.ModelSerializer):
    tag = TagSerializer(many=True, read_only=False)
    class Meta:
        model = Movie
        fields = ('title', 'tag', 'info')

    def create(self, validated_data):
        tags_data = validated_data.pop('tag')
        movie = Movie.objects.create(**validated_data)
        for tag_data in tags_data:
            movie.tag.create(**tag_data)
        return movie
</code></pre>

<p>This will probably solve your issue:</p>

<pre><code>tag = Tag.objects.get_or_create(**tag_data)[0]
movie.tag.add(tag)
</code></pre>

<p><code>get_or_create</code> function returns tuple <code>(instance, created)</code>, so you have to get <code>instance</code> with <code>[0]</code>.</p>

<p>So the full code is:</p>

<pre><code>def create(self, validated_data):
    tags_data = validated_data.pop('tag')
    movie = Movie.objects.create(**validated_data)
    for tag_data in tags_data:
        tag = Tag.objects.get_or_create(**tag_data)[0]
        movie.tag.add(tag)
    return movie
</code></pre>

<p>To make function case insensitive, you have to do "get or create" manually (the main part is to use <code>__iexact</code> in <code>filter</code>):</p>

<pre><code>tag_qs = Tag.objects.filter(name__iexact=tag_data['name'])
if tag_qs.exists():
    tag = tag_qs.first()
else:
    tag = Tag.objects.create(**tag_data)
movie.tag.add(tag)
</code></pre>

python django django-rest-framework