【问题标题】:Linking to existing record on create with a many to many record使用多对多记录链接到创建时的现有记录
【发布时间】:2019-03-02 11:19:42
【问题描述】:

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

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

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

{"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',)

【问题讨论】:

    标签: python django django-rest-framework


    【解决方案1】:

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

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

    • kwargs:要搜索的值,其中包含 place 冗余,因为您已经在 place.tags 上进行过滤。但实际上你是在说:找到我现有的具有相同 tag_dataplaceTag 对象。
    • 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)
    

    【讨论】:

    • 非常感谢您的帮助 - 这是有道理的。但是,我在place.tags.add(tag) 行出现错误“参数必须是字符串、类似字节的对象或数字,而不是'标签'”。格式也如你所说:{'name': 'some tag name'}。我使用的 curl 请求是:curl --request POST -H "Content-Type: application/json" --data '{"title": "valuey", "lat":"1.1", "lon":"1.2", "featured_image_url":"http://example.com/test.jpg", "tags":[{"name":"one"}, {"name":"two"}] }' http://127.0.0.1:8000/places.json
    • 让我感到困惑的是标签对象已成功创建(也在数据库中检查过),但似乎标签上的 add 方法有问题。我浏览了django many to many documentation,但没有跳出来
    • 更新了我的回复。 get_or_create() 返回一个元组 (obj, bool)
    • 太好了 - 完美运行,感谢您的帮助
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-27
    • 1970-01-01
    • 2023-03-09
    • 1970-01-01
    • 2021-03-02
    • 2016-06-27
    • 2016-10-12
    相关资源
    最近更新 更多