Package: skip-django-GDPR
Django-GDPRThis library enables you to store user's consent for data retention easily
and to anonymize/deanonymize user's data accordingly.For brief overview you can check example app intestsdirectory.QuickstartInstall django-gdpr with pip:pipinstallskip-django-gdprAdd gdpr to yourINSTALLED_APPS:INSTALLED_APPS=[# Django apps...'gdpr',]Imagine having a customer model:# app/models.pyfromdjango.dbimportmodelsfromgdpr.mixinsimportAnonymizationModelclassCustomer(AnonymizationModel):# these fields will be used as basic keys for pseudoanonymizationfirst_name=models.CharField(max_length=256)last_name=models.CharField(max_length=256)birth_date=models.DateField(blank=True,null=True)personal_id=models.CharField(max_length=10,blank=True,null=True)phone_number=models.CharField(max_length=9,blank=True,null=True)fb_id=models.CharField(max_length=256,blank=True,null=True)last_login_ip=models.GenericIPAddressField(blank=True,null=True)You may want a consent to store all user's data for two years and consent to store first and last name for 10 years.
For that you can simply add new consent purposes like this.# app/purposes.pyfromdateutil.relativedeltaimportrelativedeltafromgdpr.purposes.defaultimportAbstractPurposeGENERAL_PURPOSE_SLUG="general"FIRST_AND_LAST_NAME_SLUG="first_and_last"classGeneralPurpose(AbstractPurpose):name="Retain user data for 2 years"slug=GENERAL_PURPOSE_SLUGexpiration_timedelta=relativedelta(years=2)fields="__ALL__"# Anonymize all fields defined in anonymizerclassFirstAndLastNamePurpose(AbstractPurpose):"""Store First & Last name for 10 years."""name="retain due to internet archive"slug=FIRST_AND_LAST_NAME_SLUGexpiration_timedelta=relativedelta(years=10)fields=("first_name","last_name")The fieldfieldsspecify to which fields this consent applies to.Some more examples:fields=("first_name","last_name")# Anonymize only two fields# You can also add nested fields to anonymize fields on related objects.fields=("primary_email_address",("emails",("email",)),)# Some more advanced configs may look like this:fields=("__ALL__",("addresses","__ALL__"),("accounts",("__ALL__",("payments",("__ALL__",)))),("emails",("email",)),)Now when we have the purpose(s) created we also have to make ananonymizerso the library knows which fields to
anonymize and how. This is fairly simple and is quite similar to Django forms.# app/anonymizers.pyfromgdprimportanonymizersfromtests.modelsimportCustomerclassCustomerAnonymizer(anonymizers.ModelAnonymizer):first_name=anonymizers.MD5TextFieldAnonymizer()last_name=anonymizers.MD5TextFieldAnonymizer()primary_email_address=anonymizers.EmailFieldAnonymizer()birth_date=anonymizers.DateFieldAnonymizer()personal_id=anonymizers.PersonalIIDFieldAnonymizer()phone_number=anonymizers.PhoneFieldAnonymizer()fb_id=anonymizers.CharFieldAnonymizer()last_login_ip=anonymizers.IPAddressFieldAnonymizer()classMeta:model=CustomerNow you can fully leverage the system:You can create/revoke consent:fromgdpr.modelsimportLegalReasonfromtests.modelsimportCustomerfromtests.purposesimportFIRST_AND_LAST_NAME_SLUGcustomer=Customer(first_name="John",last_name="Smith")customer.save()# Create consentLegalReason.objects.create_consent(FIRST_AND_LAST_NAME_SLUG,customer)# And now you can revoke itLegalReason.objects.deactivate_consent(FIRST_AND_LAST_NAME_SLUG,customer)In case your model uses theAnonymizationModelMixinorAnonymizationModelyou can create and revoke consents even
easier.fromtests.modelsimportCustomerfromtests.purposesimportFIRST_AND_LAST_NAME_SLUGcustomer=Customer(first_name="John",last_name="Smith")customer.save()# Create consentcustomer.create_consent(FIRST_AND_LAST_NAME_SLUG)# And now you can revoke itcustomer.deactivate_consent(FIRST_AND_LAST_NAME_SLUG)Expired consents are revoked by running the following command. You should invoke it repeatedly, for example by cron.
The invocation interval depends on your circumstances - how fast you want to expire consents after their revocation,
the amount of consents to expire in the interval, server load, and last but not least, legal requirements.fromgdpr.modelsimportLegalReasonLegalReason.objects.expire_old_consents()FieldAnonymizersFunctionAnonymizer- in place lambda/function anonymization method (e.g.secret_code = anonymizers.FunctionFieldAnonymizer(lambda x: x**2))DateFieldAnonymizerCharFieldAnonymizerDecimalFieldAnonymizerIPAddressFieldAnonymizerCzechAccountNumberFieldAnonymizer- for czech bank account numbersIBANFieldAnonymizerJSONFieldAnonymizerEmailFieldAnonymizerMD5TextFieldAnonymizerSHA256TextFieldAnonymizerHashTextFieldAnonymizer- anonymization using given hash algorithm (e.g.secret_code = anonymizers.HashTextFieldAnonymizer('sha512'))StaticValueFieldAnonymizer- anonymization by replacing with static value (e.g.secret_code = anonymizers.StaticValueFieldAnonymizer(42))