Reputation: 1411
In Django Rest Framework ListSerializer when I want to save the validated data to the database by calling instance.save() I'm getting an error saying queryset object has no attribute save.
ListSerializer class:
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
Serializer Class
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
Basically in ListSerializer I'm checking if the row is chosen already in the DB. If True then I just append the instance data to a dictionary else I want to update the data to the DB and append the updated data to a list and return it.
Here in the ListSerializer I'm passing filtered queryset from the APIView class as instance and validated_data is a list of validated data.
Sample JSON data which I will pass to the APIView class:
[
{
"id": 1,
"row_chosen": true,
"user_working": "John"
},
{
"id": 1,
"row_chosen": true,
"user_working": "David"
},
]
When I pass the JSON data, it will properly filter out the rows from DB and pass the queryset as instance and JSON data to the serializer class.
# 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,
)
The ListSerializer update() is working but when it runs else block and tries to update the data it gives me an error queryset object has no attribute save. Whereas in the serializer's update() it runs the instance.save() and updates the data for the single object. I'm not sure where I'm making the mistake. Please help me with this.
Update:
I changed instance.save() to instance[index].save() in ListSerializer class. Now the queryset object has no attribute save has been fixed. Even though when I use instance[index].save() I'm unable to save the data in the data base.
Models:
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)
Here in the NoAccessDetail model, I've kept user_working null true because the data to this model will be coming from a different source. Initially while importing the data the user_working will be null. While updating the data from an API call, I'm validating the JSON data.
Upvotes: 2
Views: 4530
Reputation: 1304
To call the .save() method, you have to call it on an instance of a Model, not on a QuerySet of the model. According to DRF Docs,
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
You can see they are using a book_mapping. This is creating a dictionary where the key is the book's id and the value is the instance of the book.
Hope this helps!
EDIT Check the line just below the 'else:' block. You see you need to use .get() to get the object of the model you want to update, and then use the .save() method.
RE-EDIT Using instance[index].save() should also work. I think you need to call obj.save() before appending to 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
I updated the snippet according to docs.
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
Upvotes: 0