Package: porcupine-python
porcupine-pythonHi. I am a small and lovely porcupine 🦔. I am here to serialize your objects 💪!Usageporcupine-pythonis used for type and nested serialization of any objects with attributes into dictionaries.
It has a very good use, for example, in theDjango framework. SeeDjango compatibility.Simple usageFirst we need to create some simple class:classUser(object):def__init__(self,name=None,surname=None,age=None):self.name=nameself.surname=surnameself.age=ageSecond we create serializer for this type of class:fromporcupine.baseimportSerializerclassUserSerializer(Serializer):name:strsurname:strage:int=NoneFinal usage with different created instances looks like this:# User instance with all attributesuser=User('foo','bar',23)dictionary=UserSerializer(user).dict()# dictionary: {'name': 'foo', 'surname': 'bar', 'age': 23}# User instance with only required attributesuser=User('foo','bar')dictionary=UserSerializer(user).dict()# dictionary: {'name': 'foo', 'surname': 'bar', 'age': None}# User instance with all None attributesuser=User()dictionary=UserSerializer(user).dict()# raised ValidationErrorUsage of resolversFirst we need to create some class which instances will be resolved:classUser(object):def__init__(self,name=None,surname=None,age=None):self.name=nameself.surname=surnameSerializer for that class:fromporcupine.baseimportSerializerclassUserSerializer(Serializer):name:str=Nonesurname:str=Nonefull_name:str=None@staticmethoddefresolve_full_name(data):fullname=Noneifdata.nameanddata.surname:fullname=f'{data.name}{data.surname}'elifdata.name:fullname=data.nameelifdata.surname:fullname=data.surnamereturnfullnameSerialized user instance:user=User('foo','bar')dictionary=UserSerializer(user).dict()# dictionary: {'name': 'foo', 'surname': 'bar', 'full_name': 'foo bar'}Django compatibilityExample modelimportuuidfromdjango.dbimportmodelsfromdjango.utils.translationimportgettextas_classUser(models.Model):classUserStatus(models.TextChoices):WAITING='waiting',_('Waiting')APPROVED='approved',_('Approved')BLOCKED='blocked',_('Blocked')id=models.UUIDField(primary_key=True,default=uuid.uuid4)email=models.EmailField(null=False,unique=True,verbose_name=_('user_email'))nickname=models.CharField(max_length=100,null=False,unique=True,verbose_name=_('user_nickname'))name=models.CharField(null=True,max_length=30,verbose_name=_('user_name'))surname=models.CharField(null=True,max_length=150,verbose_name=_('user_surname'))amount=models.DecimalField(null=False,max_digits=10,decimal_places=2,default=0,verbose_name=_('user_amount'))status=models.CharField(max_length=15,null=False,choices=UserStatus.choices,default=UserStatus.WAITING,verbose_name=_('user_status'))is_active=models.BooleanField(null=False,default=True,verbose_name=_('user_is_active'))created_at=models.DateTimeField(auto_now_add=True)Example serializerfromtypingimportUnionfromuuidimportUUIDfromdatetimeimportdatetimefromdecimalimportDecimalfromporcupine.baseimportSerializerfromapps.core.modelsimportUserclassUserSerializer:classBase(Serializer):id:UUIDemail:strnickname:strname:str=Nonesurname:str=Nonefull_name:str=Noneamount:Decimalstatus:User.UserStatusis_active:boolcreated_at:datetime@staticmethoddefresolve_full_name(data,**kwargs)->Union[str,None]:# You can also use request in resolversrequest=kwargs.get('request')ifhasattr(data,'name')andhasattr(data,'surname'):full_name=f'{data.name}{data.surname}'else:full_name=Nonereturnfull_nameExample serializingfromdjango.viewsimportViewfromdjango.httpimportJsonResponsefromapps.core.modelsimportUserfromapps.core.serializers.userimportUserSerializerclassUserDetail(View):defget(self,request,user_id):try:user=User.objects.get(pk=user_id)exceptUser.DoesNotExist:raiseSome404Exceptionserialized_user=UserSerializer.Base(user,request=request).dict()# You can also make custom response with a serializer as an argumentreturnJsonResponse(serialized_user)Made with ❤ by Adam Žúrek &BACKBONE s.r.o.CHANGELOG0.6.0 : 2023-08-24🐸 Maintenance release (changed pydantic dependencies)0.5.0 : 2021-08-27🌵 Prefer resolvers to model property0.4.0 : 2021-02-02🎸 Support for nested serializers.🎸 Support for list of serializers.0.3.0 : 2020-11-27🦦 Implemented possibility to add custom arguments to serializers.🦦 Pydantic version requirement changed from1.4.*to1.7.*.0.2.0 : 2020-06-27Fixed Expected type 'Dict[str, Any]' warning when calling BaseSerializerImplemented test for nested objects.0.1.0 : 2020-05-26Initial release