向Django API发出PUT请求以更新具有多对多关系的嵌套对象列表

时间:2018-11-09 12:55:07

标签: django reactjs django-rest-framework axios

我有一个与另一个模型具有ManyToMany关系的用户模型

class User(AbstractUser):
    countries = models.ManyToManyField(Country, blank=True)
    count = models.IntegerField(blank=True, default=0)

    def save(self, *args, **kwargs):
        # Must save model before Many To Many relationship can be used.
        super(User, self).save(*args, **kwargs)
        self.count = self.countries.count()
        super(User, self).save(*args, **kwargs)

我的国家/地区模型是一个相当大的模型:

class Country(models.Model):
    '''
    Describes the countries, as well as territories of the world.
    '''
    name = models.CharField(max_length=255, null=True, blank=True)
    top_level_domain = JSONField(null=True, blank=True)
    alpha2code = models.CharField(max_length=255, null=True, blank=True)
    alpha3code = models.CharField(max_length=255, null=True, blank=True)
    calling_codes = JSONField(null=True, blank=True)
    capital = models.CharField(max_length=255, null=True, blank=True)
    alt_spellings = JSONField(null=True, blank=True)
    region = models.CharField(max_length=255, null=True, blank=True)
    subregion = models.CharField(max_length=255, null=True, blank=True)
    population = models.IntegerField(null=True, blank=True)
    latlng = JSONField(null=True, blank=True)
    demonym = models.CharField(max_length=255, null=True, blank=True)
    area = models.FloatField(null=True, blank=True)
    gini = models.FloatField(null=True, blank=True)
    timezones = JSONField(null=True, blank=True)
    borders = JSONField(null=True, blank=True)
    native_name = models.CharField(max_length=255, null=True, blank=True)
    numeric_code= models.CharField(max_length=255, null=True, blank=True)
    currencies = models.ManyToManyField(Currency)
    languages = models.ManyToManyField(Language)
    flag = models.CharField(max_length=255, null=True, blank=True)
    regional_blocs = models.ManyToManyField(RegionalBloc, blank=True)
    cioc = models.CharField(max_length=255, null=True, blank=True)

    def __str__(self):
        return self.name

我正在尝试从前端发出放置请求,以更新与之关联的国家/地区列表(数据以来自axios的React作为对象数组的形式发送)。我收到错误消息“ AssertionError:默认情况下,.update()方法不支持可写的嵌套字段。 为串行器.update()写一个显式的api.serializers.UserDetailSerializer方法,或在嵌套的串行器字段上设置read_only=True。”由于显然我无法将国家/地区设为read_only = True,因此我试图添加序列化程序的更新方法。

class UserDetailSerializer(UserDetailsSerializer):
    countries = CountrySerializer(many=True)
    count = serializers.IntegerField(read_only=True)

    class Meta:
        model = User
        fields = ('pk', 'username', 'email', 'count', 'countries')

    # Update the instance upon Put Request from frontend.
    def update(self, instance, validated_data):
        instance.countries = validated_data['countries']
        instance.save()
        return instance

此更新方法给我一个错误消息:“禁止直接分配给多对多集的前端。改用country.set()。”

当我将更新方法更改为

  instance.countries.set(validated_data['countries'])

我收到错误信息'TypeError:unhashable type:'collections.OrderedDict'。开始感到有点迷茫,就像我在圈子里奔跑一样。如何正确编写更新方法?

edit:我想做的只是将一个Country对象添加/删除到“用户国家/地区”列表中。我实际上并不想编辑任何国家对象。

我发布的国家/地区数据将是这样的国家/地区对象列表

{
    "id": 6,
    "currencies": [
        {
            "code": "EUR",
            "name": "European Euro",
            "symbol": "€"
        }
    ],
    "languages": [
        {
            "iso639_1": "ca",
            "name": "Catalan",
            "native_name": "Català"
        }
    ],
    "regional_blocs": [],
    "name": "Andorra",
    "top_level_domain": [
        ".ad"
    ],
    "alpha2code": "AD",
    "alpha3code": "AND",
    "calling_codes": [
        "376"
    ],
    "capital": "Andorra la Vella",
    "alt_spellings": [
        "AD",
        "Principality of Andorra",
        "Principat d'Andorra"
    ],
    "region": "Europe",
    "subregion": "Southern Europe",
    "population": 78014,
    "latlng": [
        42.5,
        1.5
    ],
    "demonym": "Andorran",
    "area": 468.0,
    "gini": null,
    "timezones": [
        "UTC+01:00"
    ],
    "borders": [
        "FRA",
        "ESP"
    ],
    "native_name": "Andorra",
    "numeric_code": "020",
    "flag": "https://restcountries.eu/data/and.svg",
    "cioc": "AND"
}

1 个答案:

答案 0 :(得分:1)

您得到TypeError: unhashable type: 'collections.OrderedDict'的原因是您试图将collections.OrderedDict类型的对象分配给一个国家,即ManyToManyField

请执行以下操作:

def update(self, instance, validated_data):
    country_names = [cdata['name'] for cdata in validated_data['countries']]
    countries = Country.objects.filter(name__in=country_names)
    instance.countries.set(countries)
    return instance