Create on Primary and Update on Nested: Attribute Error on update_or_create_reverse_relations
So I am successfully creating objects, but now I need to be able to update them from a response. The scenario is that I have a multi-level nested serialize where I need to create the first two levels and then update the bottom level. I've added a mixin to populate the pk and instance on the bottom level, but I am getting AttributeError: 'category' object has no attribute 'items' which is spawning from update_or_create_reverse_relations. The mixin uses additional attributes to identify the existing records. See below example.
Models:
class category(models.Model):
name = models.CharField(max_length=255, help_text="",blank=True)
short_name = models.CharField(max_length=255, help_text="",blank=True)
external_id = models.IntegerField(help_text="",blank=True,null=True)
sales_channel = models.ForeignKey('sales_channel',null=True,blank=True)
purchase_channel = models.ForeignKey('purchase_channel',null=True,blank=True)
def __str__(self):
return self.name
class response_log_detail(models.Model):
category = models.ManyToManyField(category)
response_log = models.OneToOneField('response_log', null = True, blank = True)
class response_log(models.Model):
timestamp = models.DateTimeField()
status = models.CharField(max_length = 25, null=True, blank=True)
category_id = models.ManyToManyField(category)
Mixin:
class AddPkInstanceMixin(s.ModelSerializer):
def __init__(self, **kwargs):
object_identifier = kwargs.pop('object_identifier', None)
object_identifier_source = kwargs.pop('object_identifier_source', None)
super(AddPkInstanceMixin, self ).__init__(**kwargs)
def to_internal_value(self,data):
model = self.Meta.model
id_value = data[self.object_identifier]
if model is not None and id_value is not None:
try:
pk = model.objects.values('pk').get(**{self.object_identifier_source : id_value})
if pk is not None:
data['pk']=pk['pk']
instance = model.objects.get(pk = pk['pk'])
return model.objects.get(pk = pk['pk'])
except:
pass
return super().to_internal_value(data)
Serializers:
class categoryArray(AddPkInstanceMixin, s.ModelSerializer):
object_identifier = 'categoryId'
object_identifier_source = 'external_id'
# Fields #
categoryId = s.IntegerField(source='external_id')
categoryName = s.CharField(source='name')
categoryBriefName = s.CharField(source='short_name')
class Meta:
model = category
fields = (
'pk',
'categoryId',
'categoryName',
'categoryBriefName',
)
class getCategoriesResponseSerializer(WritableNestedModelSerializer):
# Fields #
categoryArray = categoryArray(many=True, source='category', partial=True)
# errorMessage = bonanzaErrorMessage()
# warnings = bonanzaWarnings()
class Meta:
model = response_log_detail
fields = (
#'pk',
'categoryArray',
# 'errorMessage',
# 'warnings',
)
class responseSerializer(WritableNestedModelSerializer):
# Fields #
getCategoriesResponse = getCategoriesResponseSerializer(source='response_log_detail' )
ack = s.CharField(source='status')
timestamp = s.DateTimeField()
# errorMessage = bonanzaErrorMessage()
# warnings = bonanzaWarnings()
class Meta:
model = response_log
fields = (
#'pk',
'getCategoriesResponse',
'ack',
'timestamp',
)
Run these to create the categories:
response = {'ack': 'Success',
'getCategoriesResponse': {'categoryArray': [{'categoryBriefName': 'Collectibles',
'categoryId': 1,
'categoryLevel': 1,
'categoryName': 'Collectibles',
'leafCategory': 'false',
'traitCount': 1},
{'categoryBriefName': 'Everything Else',
'categoryId': 99,
'categoryLevel': 1,
'categoryName': 'Everything Else',
'leafCategory': 'false',
'traitCount': 5}]},
'timestamp': '2017-10-12T12:00:40.000Z',
'version': '1.0'}
serializer = responseSerializer(data = response)
serializer.is_valid(raise_exception=True)
response_log = serializer.save()
Run these to try and update the categories: Changed name on Collectibles.
response = {'ack': 'Success',
'getCategoriesResponse': {'categoryArray': [{'categoryBriefName': 'Collectibles - Name Change',
'categoryId': 1,
'categoryLevel': 1,
'categoryName': 'Collectibles - Name Change',
'leafCategory': 'false',
'traitCount': 1},
{'categoryBriefName': 'Everything Else',
'categoryId': 99,
'categoryLevel': 1,
'categoryName': 'Everything Else',
'leafCategory': 'false',
'traitCount': 5}]},
'timestamp': '2017-10-12T12:00:40.000Z',
'version': '1.0'}
serializer = responseSerializer(data = response)
serializer.is_valid(raise_exception=True)
response_log = serializer.save()
Same as https://github.com/beda-software/drf-writable-nested/issues/28
I believe this fixes it: https://github.com/beda-software/drf-writable-nested/pull/29
Hello @beautifulDrifter! The latest version 4.0.2 contains the fix of the problem with OneToOne updating without specifying id. I will appreciate if you check your code with the latest version of the package and leave feedback if everything is OK.