2

I am trying to add tags in my model by taggit and taggit serializer. I making my API in rest framework. I followed the instruction(https://github.com/glemmaPaul/django-taggit-serializer) but it is still an issues:

"tags": [ "Invalid json list. A tag list submitted in string form must be valid json."]

/setting.py

INSTALLED_APPS = [
  [...]
'rest_framework',
'taggit',
'taggit_serializer',
]

/models.py

tags = TaggableManager(blank = True) 

/serializer.py

class JobSerializer(TaggitSerializer,serializers.HyperlinkedModelSerializer):

# tag serializer
tags = TagListSerializerField()

There is noting changed in the view.py.

thanks @ykh for helping me to solve this problem, but it comes to new situation with error:

when you want to initial the value:

hao,free

the outcome will be like that:

 "tags": [
    "hao",
    "free"
],

But when I intend to put updates into this value: it showed the Json file in rest framework automatically:

enter image description here

and if you put data to your api, the result will be:

enter image description here

The issue is that each time when I try to modify the instance which including the 'tag' in rest framework, brackets and line breaks were automatic attached added in 'tag' field, like the picture result shows


Update: It seems that I have solved this issue, the solution is overriding the create function in serializer:

using django-taggit-serializer. and

    def create(self, validated_data):
    tags = validated_data.pop('tags')
    instance = super(JobSerializer, self).create(validated_data)
    instance.tags.set(*tags)
    return instance
hsbzzhz
  • 79
  • 10

3 Answers3

4

try:

import six

class NewTagListSerializerField(TagListSerializerField):
    def to_internal_value(self, value):
        if isinstance(value, six.string_types):
            value = value.split(',')

        if not isinstance(value, list):
            self.fail('not_a_list', input_type=type(value).__name__)

        for s in value:
            if not isinstance(s, six.string_types):
                self.fail('not_a_str')

            self.child.run_validation(s)
        return value

class JobSerializer(TaggitSerializer,serializers.HyperlinkedModelSerializer):

    tags = NewTagListSerializerField()

post tags with 'tags1,tags2'

The original source code is :

def to_internal_value(self, value):
    if isinstance(value, six.string_types):
        if not value:
            value = "[]"
        try:
            value = json.loads(value)
        except ValueError:
            self.fail('invalid_json')

    if not isinstance(value, list):
        self.fail('not_a_list', input_type=type(value).__name__)

    for s in value:
        if not isinstance(s, six.string_types):
            self.fail('not_a_str')

        self.child.run_validation(s)

    return value

the error is caused by json.loads(value) is not success and i don't konw which data type is excepted.

Ykh
  • 7,567
  • 1
  • 22
  • 31
  • I still don't understand the logic TaggitSerializer used,so I change it to meet my own requirement. – Ykh Oct 08 '18 at 05:59
  • It works now, can you just briefly explain it why it doesn't work before? thx – hsbzzhz Oct 08 '18 at 06:00
  • Hi ykh, can u use to update(put) data? I got errors when using that – hsbzzhz Oct 10 '18 at 10:29
  • Thx for you patient, I have updated details into the question, general speaking, the issue is when I try to modify the instance, each time I put data, the brackets (Json format) will automatic attach on the instance, like the result shows – hsbzzhz Oct 11 '18 at 03:34
  • just remove the data drf automatically create and use the old format to update,like newtag1,newtag2 – Ykh Oct 11 '18 at 04:02
1

Correction to the answer above, because solution by Ykh works incorrect, when i want to update my tags in JSON format(i got \r \m \ symbols)

import json

class NewTagListSerializerField(TagListSerializerField):
    def to_internal_value(self, value):
        if isinstance(value, six.string_types):
            if not value:
                value = "[]"
            try:
                if(type(value) == str):
                    if(value.__contains__('"') == True):
                        value = json.loads(value)
                    else:
                        value = value.split(',')

            except ValueError:
                self.fail('invalid_json')

        if not isinstance(value, list):
            self.fail('not_a_list', input_type=type(value).__name__)

        for s in value:
            if not isinstance(s, six.string_types):
                self.fail('not_a_str')

            self.child.run_validation(s)

        return value
  • 1
    What was wrong with the answer above? What needed changed? Please include these details in your answer. Thanks – Taazar Nov 26 '19 at 11:36
0

I've just had this issue when writing unit tests. It's because the python dictionary that I used to store the POST data becomes internally converted into a QueryDict. This typically happens when Django considers the POST as content of type application/x-www-form-urlencoded, e.g. something sent via an HTML form.

I think TagListSerializerField is expecting json, so when its to_internal_value is called, incorrect data is being received and it fails with the "Invalid json list..." error.

In short, this is what I had to do.

data = {"tags": ["tag1", "tag2"]}
response = self.client.post('/api/endpoint/', json.dumps(data), content_type='application/json')

In other words, the data needs to be converted to json, and the content type has to be set. In my example, self.client is a APIClient from from rest_framework.test.

gmcc051
  • 390
  • 3
  • 17