fromurllib.parseimporturlparsefromdjango.core.urlresolversimportresolvefromdjango.utilsimportsixfromrest_frameworkimportserializersclassGenericRelatedField(serializers.StringRelatedField):""" A custom field to use for serializing generic relationships. """def__init__(self,serializer_dict,*args,**kwargs):super(GenericRelatedField,self).__init__(*args,**kwargs)self.serializer_dict=serializer_dictforsinself.serializer_dict.values():s.bind('',self)defto_representation(self,instance):# find a serializer correspoding to the instance classforkeyinself.serializer_dict.keys():ifisinstance(instance,key):# Return the result of the classes serializerreturnself.serializer_dict[key].to_representation(instance=instance)return'{}'.format(instance)defto_internal_value(self,data):# If provided as string, must be url to resource. Create dict containing just urliftype(data)==str:data={'url':data}# Existing resource can be specified as urlif'url'indata:# Extract details from the url and grab real objectresolved_func,unused_args,resolved_kwargs=resolve(urlparse(data['url']).path)object=resolved_func.cls.queryset.get(pk=resolved_kwargs['pk'])else:# If url is not specified then object is new and must have a 'type' field to allow us to create correct object from list of serializersforkeyinself.serializer_dict.keys():ifdata['type']==key.__name__:object=key()# Deserialize data into attributes of object and applyobj_internal_value=self.serializer_dict[object.__class__].to_internal_value(data)fork,vinobj_internal_value.items():setattr(object,k,v)# Save object to store new or any updated attributesobject.save()returnobjectclassTypeField(serializers.Field):""" Read only Field which displays the object type from the class name """def__init__(self,*args,**kwargs):kwargs['source']='__class__.__name__'kwargs['read_only']=Truesuper(TypeField,self).__init__(*args,**kwargs)defto_representation(self,value):returnvalue
fromurllib.parseimporturlparsefromdjango.core.urlresolversimportresolvefromrest_frameworkimportserializersfromrest-gm2m-fieldimportTypeField,GenericRelatedFieldclassGenericHyperlinkedSerializer(serializers.HyperlinkedModelSerializer):""" Serializer for models with generic relations. Includes a type field to allow GenericRelatedFields specified in the serializer to know how to serialize correctly. Derived from hyperlinked serializer and the url field must be present on the serializer """type=TypeField()defcreate(self,validated_data):# Remove generic fields from validated data and add to separate dictrelated={}forfieldinself._fields.keys():iftype(self._fields[field])==serializers.ManyRelatedField:iftype(self._fields[field].child_relation)==GenericRelatedField:related[field]=validated_data.pop(field)# Create instance of serializers Meta.modelinstance=self.Meta.model.objects.create(**validated_data)# For all related fields attach the listed objectsforfieldinrelated.keys():forobjectinrelated[field]:attr=getattr(instance,field)attr.add(object)returninstancedefupdate(self,instance,validated_data):# Create a dict of updatable fields fields={}generics={}forfieldinself._fields.keys():# Exclude read only fieldsifnotself._fields[field].read_only:iftype(self._fields[field])!=serializers.ManyRelatedField:fields[field]=self._fields[field]else:# Exclude generics but add to separate dictiftype(self._fields[field].child_relation)!=GenericRelatedField:fields[field]=self._fields[field]else:generics[field]=self._fields[field]# Set all valid attributes of the instance to the validated dataforfieldinfields.keys():setattr(instance,field,validated_data.get(field,getattr(instance,field)))# Add any new generic relationsforgeneric_attringenerics.keys():attr=getattr(instance,generic_attr)attr_objects=list(attr.all())forobjectinvalidated_data.get(generic_attr,getattr(instance,generic_attr)):ifobjectnotinattr_objects:# If the object is not in the list of existing generic relations then add itattr.add(object)else:# If the object is already related then remove it from the list so we end up with a list of missing generic relationsattr_objects.remove(object)# Remove any missing generic relations if not a partial update (PUT but not PATCH)ifnotself.partial:forobjectinattr_objects:attr.remove(object)instance.save()returninstance
Comments (0)
HTTPSSSH
You can clone a snippet to your computer for local editing.
Learn more.