首页 > 解决方案 > 使用多对多记录链接到创建时的现有记录

问题描述

我在两个模型(地点和标签)之间有多对多的关系。但是,标签表中似乎存在不必要的重复。

例如,当使用现有标签创建新地点时,它会创建一个具有相同名称的重复标签并将其链接,而不是链接现有标签。

我认为使模型上的名称字段唯一会有所帮助,但在运行发布请求时会导致以下错误,这是可以理解的:

{"tags":[{"name":["tags with this name already exists."]},{"name":["tags with th
is name already exists."]}]}

串行器

class PlaceTagSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Tags
        fields = ('name',)


class PlaceSerializerPost(serializers.ModelSerializer):
    tags = PlaceTagSerializer(many=True)

    class Meta:
        model = Place
        fields = ('title', 'lat', 'lon', 'tags', 'featured_image_url', 'created_at')

    def create(self, validated_data):
        tags_data = validated_data['tags']

        """
        Tags need to be deleted otherwise the place object will not save
        as they need to be handled separately.
        """
        del validated_data['tags']

        place = Place.objects.create(**validated_data)

        for tag_data in tags_data:
            place.tags.get_or_create(place=place, **tag_data)

        return place

模型

    class Tags(models.Model):
    name = models.CharField(max_length=200, unique=True)

    def __str__(self):
        return self.name

    class Meta:
        ordering = ('name',)


class Place(models.Model):
    title = models.CharField(max_length=800)
    lat = models.DecimalField(max_digits=20, decimal_places=10)
    lon = models.DecimalField(max_digits=20, decimal_places=10)
    featured_image_url = models.CharField(max_length=2000)
    created_at = models.DateTimeField(auto_now_add=True)
    tags = models.ManyToManyField(Tags)
    live = models.BooleanField(default=1)

    def __str__(self):
        return self.title

    class Meta:
        ordering = ('title',)

标签: pythondjangodjango-rest-framework

解决方案


问题出在你的get_or_create线路上。首先,您在现有place.tags.all()QuerySet 上调用该方法,并且由于您正在创建一个新位置,这将永远不会返回现有对象。

其次,get_or_create(defaults=None, **kwargs)有两组参数:

  • kwargs:要搜索的值,您在其中包含place冗余,因为您已经在place.tags. 但本质上你是在说:找到我现有Tag的具有相同tag_dataand的对象place
  • defaults:这些属性将为新对象设置,但不会被搜索。这里没有什么可设置的,因为Tag只有一个字段。

所以在你的情况下,你应该这样做:

tag, _ = Tag.objects.get_or_create(**tag_data)  
# I'm not sure what `tag_data` is, but it should be dictionary {'name': 'some tag name'}, if not:
tag, _ = Tag.objects.get_or_create(name=tag_data)
place.tags.add(tag)

推荐阅读