instance.save () не сохраняет модель в ListSerializer Django Rest Framework - PullRequest
0 голосов
/ 28 сентября 2019

В Django Rest Framework ListSerializer, когда я хочу сохранить проверенные данные в базу данных, вызвав instance.save (), я получаю сообщение о том, что объект набора запросов не имеет атрибута save .

Класс ListSerializer:

class NoAccessDetailsListSerializer(serializers.ListSerializer):

    # This will be called when there is list of objects
    #here instance is list of queryset and validated_data is the list of json object
    def update(self, instance, validated_data):

        ret = []
        for index, data in enumerate(validated_data):

            #checking if row is already chosen
            if(instance[index].row_chosen):

                # do not update the info to db
                # just append the data to ret
                ret.append(instance[index])

            else:
                instance.id = instance[index].id
                instance.row_chosen = validated_data[index].get(
                    'row_chosen')
                instance.user_working = validated_data[index].get(
                    'user_working')

                ret.append(instance)

                instance.save()

        return ret

Класс сериализатора

class NoAccessDetailsSerializer(serializers.ModelSerializer):


    id = serializers.IntegerField(read_only=False)

    class Meta:
        model = NoAccessDetails
        list_serializer_class = NoAccessDetailsListSerializer
        fields = ("id", "row_chosen",
                  "user_working")

    def update(self, instance, validated_data):
        instance.id = instance.id
        instance.row_chosen = validated_data.get(
            'row_chosen')
        instance.user_working = validated_data.get(
            'user_working ')

        instance.save()
        return instance

В основном в ListSerializer я проверяю, выбрана ли строка уже в БД.Если True, тогда я просто добавляю данные экземпляра в словарь, иначе я хочу обновить данные в БД, добавить обновленные данные в список и вернуть его.

Здесь, в ListSerializer, я передаю отфильтрованный набор запросовиз класса APIView в качестве экземпляра и validated_data представляет собой список проверенных данных.

Образцы данных JSON, которые я передам в класс APIView:

[
        {
            "id": 1,
            "row_chosen": true,
            "user_working": "John"
        },
        {
            "id": 1,
            "row_chosen": true,
            "user_working": "David"
        },
]

Когда я передаю данные JSON, онправильно отфильтрует строки из БД и передаст набор запросов как экземпляр и данные JSON классу сериализатора.

# here list_of_id is the ids which are there in the JSON object. i.e [1,2]

filtered_id_data= NoAccessDetails.objects.filter(
                id__in=list_of_id)

            serializer = NoAccessDetailsSerializer(filtered_id_data,
                                                   data=request.data,
                                                   many=True,
                                                   )

Функция ListSerializer update () работает, но когда он запускает блок else и пытается обновить данныеэто выдает ошибку объект набора запросов не имеет атрибута save .Принимая во внимание, что в update () сериализатора он запускает instance.save () и обновляет данные для одного объекта.Я не уверен, где я делаю ошибку.Пожалуйста, помогите мне с этим.

Обновление:

Я изменил instance.save () на instance [index] .save () в классе ListSerializer.Теперь объект запроса не имеет атрибута. Исправлено .Хотя при использовании instance [index] .save () я не могу сохранить данные в базе данных.

Модели:

class NoAccessDetails(models.Model):
    20 CharFields
    ...
    ...
    user_working = models.ForeignKey(
        UserProfile, on_delete=models.CASCADE, blank=True, null=True)
    row_chosen = models.BooleanField(default=False)

class UserProfile(models.Model):
    user_id = models.CharField(primary_key=True, max_length=10)
    user_name = models.CharField(max_length=100)
    user_email = models.EmailField()
    is_admin = models.BooleanField(default=False)

Здесьв модели NoAccessDetail я сохранил значение user_working равным true, поскольку данные для этой модели будут поступать из другого источника.Первоначально при импорте данных user_working будет нулевым.При обновлении данных из вызова API я проверяю данные JSON.

1 Ответ

0 голосов
/ 28 сентября 2019

Чтобы вызвать метод .save (), вы должны вызывать его для экземпляра модели, а не для QuerySet модели.Согласно документам DRF,

class BookListSerializer(serializers.ListSerializer):
  def update(self, instance, validated_data):
    # Maps for id->instance and id->data item.
    book_mapping = {book.id: book for book in instance}
    data_mapping = {item['id']: item for item in validated_data}

    # Perform creations and updates.
    ret = []
    for book_id, data in data_mapping.items():
        book = book_mapping.get(book_id, None)
        if book is None:
            ret.append(self.child.create(data))
        else:
            ret.append(self.child.update(book, data))

    # Perform deletions.
    for book_id, book in book_mapping.items():
        if book_id not in data_mapping:
            book.delete()

    return ret

Вы можете видеть, что они используют book_mapping.Это создает словарь, в котором ключ - это идентификатор книги, а значение - экземпляр книги.

Надеюсь, это поможет!

РЕДАКТИРОВАТЬ Проверьте строку чуть нижеблок else:Вы видите, что вам нужно использовать .get (), чтобы получить объект модели, которую вы хотите обновить, а затем использовать метод .save ().

RE-EDIT Использование instance [index] .save () также должно работать.Я думаю, что вам нужно вызвать obj.save () перед добавлением ret.

class NoAccessDetailsListSerializer(serializers.ListSerializer):
    def update(self, instance, validated_data):
        ...
        ...
        else:
            obj = NoAccessDetails.objects.get(id=instance[index].id)
            # or obj = instance[index]
            obj.row_chosen = validated_data[index].get(
                'row_chosen')
            obj.user_working = validated_data[index].get(
                'user_working')

            print('Instance data ',
                  obj.row_chosen,
                  obj.user_working)
            obj.save() # call this before appending to ret
            ret.append(obj)


    return ret

RE-RE-EDIT

Я обновил фрагмент в соответствии с документами.

class NoAccessDetailsListSerializer(serializers.ListSerializer):

# This will be called when there is list of objects
# here instance is list of queryset and validated_data is the list of json object
def update(self, instance, validated_data):

    ret = []

    obj_mapping = {obj.id: obj for obj in instance}
    data_mapping = {item['id']: item for item in validated_data}

    for obj_id, data in data_mapping.items():
        obj = obj_mapping.get(obj_id, None)
        if not obj:
            continue

        if obj.row_chosen:
            ret.append(obj)
        else:
            obj.row_chosen = data['row_chosen']
            obj.user_working = data['user_working']
            obj.save()
            ret.append(obj)

    return ret
...