{"id":40355071,"url":"https://github.com/regiohelden/django-scrubber","last_synced_at":"2026-01-20T10:01:34.557Z","repository":{"id":37706643,"uuid":"138311281","full_name":"RegioHelden/django-scrubber","owner":"RegioHelden","description":"Data anonymizer for Django","archived":false,"fork":false,"pushed_at":"2026-01-19T17:44:14.000Z","size":403,"stargazers_count":27,"open_issues_count":4,"forks_count":10,"subscribers_count":3,"default_branch":"main","last_synced_at":"2026-01-19T23:31:21.604Z","etag":null,"topics":["anonymization","development","django","python","security"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/RegioHelden.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2018-06-22T14:20:09.000Z","updated_at":"2026-01-19T17:44:17.000Z","dependencies_parsed_at":"2022-07-14T04:10:42.499Z","dependency_job_id":"1a695b31-b624-4ffa-8814-520aff9c569e","html_url":"https://github.com/RegioHelden/django-scrubber","commit_stats":{"total_commits":170,"total_committers":12,"mean_commits":"14.166666666666666","dds":0.6941176470588235,"last_synced_commit":"8b771a7451a97516fa3b2fce6cd51dc64547bacd"},"previous_names":[],"tags_count":46,"template":false,"template_full_name":null,"purl":"pkg:github/RegioHelden/django-scrubber","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RegioHelden%2Fdjango-scrubber","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RegioHelden%2Fdjango-scrubber/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RegioHelden%2Fdjango-scrubber/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RegioHelden%2Fdjango-scrubber/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RegioHelden","download_url":"https://codeload.github.com/RegioHelden/django-scrubber/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RegioHelden%2Fdjango-scrubber/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28601284,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-20T09:39:28.479Z","status":"ssl_error","status_checked_at":"2026-01-20T09:38:10.511Z","response_time":117,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["anonymization","development","django","python","security"],"created_at":"2026-01-20T10:01:21.144Z","updated_at":"2026-01-20T10:01:34.543Z","avatar_url":"https://github.com/RegioHelden.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Django Scrubber\n\n[![Build Status](https://github.com/RegioHelden/django-scrubber/workflows/Build/badge.svg)](https://github.com/RegioHelden/django-scrubber/actions)\n[![PyPI](https://img.shields.io/pypi/v/django-scrubber.svg)](https://pypi.org/project/django-scrubber/)\n[![Downloads](https://pepy.tech/badge/django-scrubber)](https://pepy.tech/project/django-scrubber)\n\n`django_scrubber` is a django app meant to help you anonymize your project's database data. It destructively alters data\ndirectly on the DB and therefore **should not be used on production**.\n\nThe main use case is providing developers with realistic data to use during development, without having to distribute\nyour customers' or users' potentially sensitive information.\nTo accomplish this, `django_scrubber` should be plugged in a step during the creation of your database dumps.\n\nSimply mark the fields you want to anonymize and call the `scrub_data` management command. Data will be replaced based\non different *scrubbers* (see below), which define how the anonymous content will be generated.\n\nIf you want to be sure that you don't forget any fields in the ongoing development progress, you can use the \nmanagement command `scrub_validation` in your CI/CD pipeline to check for any missing fields.\n\n## Installation\n\nSimply run:\n\n```\npip install django-scrubber\n```\n\nAnd add `django_scrubber` to your django `INSTALLED_APPS`. I.e.: in `settings.py` add:\n\n```\nINSTALLED_APPS = [\n  ...\n  'django_scrubber.apps.DjangoScrubberConfig',\n  ...\n]\n```\n\n## Scrubbing data\n\nIn order to scrub data, i.e.: to replace DB data with anonymized versions, `django-scrubber` must know which models and\nfields it should act on, and how the data should be replaced.\n\nThere are a few different ways to select which data should be scrubbed, namely: explicitly per model field; or globally\nper name or field type.\n\nAdding scrubbers directly to model, matching scrubbers to fields by name:\n\n```python\nclass MyModel(Model):\n    somefield = CharField()\n\n    class Scrubbers:\n        somefield = scrubbers.Hash('somefield')\n```\n\nAdding scrubbers globally, either by field name or field type:\n\n```python\n# (in settings.py)\n\nSCRUBBER_GLOBAL_SCRUBBERS = {\n    'name': scrubbers.Hash,\n    EmailField: scrubbers.Hash,\n}\n```\n\nModel scrubbers override field-name scrubbers, which in turn override field-type scrubbers.\n\nTo disable global scrubbing in some specific model, simply set the respective field scrubber to `None`.\n\nScrubbers defined for non-existing fields will raise a warning but not fail the scubbing process.\n\nWhich mechanism will be used to scrub the selected data is determined by using one of the provided scrubbers\nin `django_scrubber.scrubbers`. See below for a list.\nAlternatively, values may be anything that can be used as a value in a `QuerySet.update()` call (like `Func` instances,\nstring literals, etc), or any `callable` that returns such an object when called with a `Field` object as argument.\n\nBy default, `django_scrubber` will affect all models from all registered apps. This may lead to issues with third-party\napps if the global scrubbers are too general. This can be avoided with the `SCRUBBER_APPS_LIST` setting. Using this, you\nmight for instance split your `INSTALLED_APPS` into multiple `SYSTEM_APPS` and `LOCAL_APPS`, then\nset `SCRUBBER_APPS_LIST = LOCAL_APPS`, to scrub only your own apps.\n\nFinally just run `./manage.py scrub_data` to **destructively** scrub the registered fields.\n\n### Arguments to the scrub_data command\n\n`--model` Scrub only a single model (format \u003capp_label\u003e.\u003cmodel_name\u003e)\n\n`--keep-sessions` Will NOT truncate all (by definition critical) session data.\n\n`--remove-fake-data` Will truncate the database table storing preprocessed data for the Faker library.\n\n## Built-In scrubbers\n\n### Empty/Null\n\nThe simplest scrubbers: replace the field's content with the empty string or `NULL`, respectively.\n\n```python\nclass Scrubbers:\n    somefield = scrubbers.Empty\n    someother = scrubbers.Null\n```\n\nThese scrubbers have no options.\n\n### Keeper\n\nWhen running the validation or want to work in strict mode, you maybe want to actively decide to keep certain data\ninstead of scrubbing them. In this case, you can just define `scrubbers.Keep`.\n\n```python\nclass Scrubbers:\n    non_critical_field = scrubbers.Keep\n```\n\nThese scrubber doesn't have any options.\n\n### Hash\n\nSimple hashing of content:\n\n```python\nclass Scrubbers:\n    somefield = scrubbers.Hash  # will use the field itself as source\n    someotherfield = scrubbers.Hash('somefield')  # can optionally pass a different field name as hashing source\n```\n\nCurrently, this uses the MD5 hash which is supported in a wide variety of DB engines. Additionally, since security is\nnot the main objective, a shorter hash length has a lower risk of being longer than whatever field it is supposed to\nreplace.\n\n### Lorem\n\nSimple scrubber meant to replace `TextField` with a static block of text. Has no options.\n\n```python\nclass Scrubbers:\n    somefield = scrubbers.Lorem\n```\n\n### IfNotEmpty\n\nWrapper around another single scrubber that only cleans the field if it already contains data before cleaning.\n\n```python\nclass Scrubbers:\n    somefield = scrubbers.IfNotEmpty(scrubbers.Lorem)\n\nclass Scrubbers:\n    somefield = scrubbers.IfNotEmpty(scrubbers.Concat(scrubbers.Faker('city'), models.Value('@'), scrubbers.Faker('domain_name'), output_field=models.TextField()))\n```\n\n### Concat\n\nWrapper around `django.db.functions.Concat` to enable simple concatenation of scrubbers. This is useful if you want to\nensure a fields uniqueness through composition of, for instance, the `Hash` and `Faker` (see below) scrubbers.\n\nWhen using different input field types, make sure to explicitly state an `output_field` type.\n\nThe following will generate random email addresses by hashing the user-part and using `faker` for the domain part:\n\n```python\nclass Scrubbers:\n    email = scrubbers.Concat(scrubbers.Hash('email'), models.Value('@'), scrubbers.Faker('domain_name'), output_field=models.EmailField())\n```\n\n### Faker\n\nReplaces content with the help of [faker](https://pypi.python.org/pypi/Faker).\n\n```python\nclass Scrubbers:\n    first_name = scrubbers.Faker('first_name')\n    last_name = scrubbers.Faker('last_name')\n    past_date = scrubbers.Faker('past_date', start_date=\"-30d\", tzinfo=None)\n```\n\nThe replacements are done on the database-level and should therefore be able to cope with large amounts of data with\nreasonable performance.\n\nThe `Faker` scrubber requires at least one argument: the faker provider used to generate random data.\nAll [faker providers](https://faker.readthedocs.io/en/latest/providers.html) are supported, and you can also register\nyour own custom providers.\u003cbr /\u003e\nAny remaining arguments will be passed through to that provider. Please refer to the faker docs if a provider accepts\narguments and what to do with them.\n\n#### Locales\n\nFaker will be initialized with the current django `LANGUAGE_CODE` and will populate the DB with localized data. If you\nwant localized scrubbing, simply set it to some other value.\n\n#### Idempotency\n\nBy default, the faker instance used to populate the DB uses a fixed random seed, in order to ensure different scrubbings\nof the same data generate the same output. This is particularly useful if the scrubbed data is imported as a dump by\ndevelopers, since changing data during troubleshooting would otherwise be confusing.\n\nThis behaviour can be changed by setting `SCRUBBER_RANDOM_SEED=None`, which ensures every scrubbing will generate random\nsource data.\n\n#### Limitations\n\nScrubbing unique fields may lead to `IntegrityError`s, since there is no guarantee that the random content will not be\nrepeated. Playing with different settings for `SCRUBBER_RANDOM_SEED` and `SCRUBBER_ENTRIES_PER_PROVIDER` may alleviate\nthe problem.\nUnfortunately, for performance reasons, the source data for scrubbing with faker is added to the database, and\narbitrarily increasing `SCRUBBER_ENTRIES_PER_PROVIDER` will significantly slow down scrubbing (besides still not\nguaranteeing uniqueness).\n\nWhen using `django \u003c 2.1` and working on `sqlite` a bug within django causes field-specific scrubbing (\ne.g. `date_object`) to fail. Please consider using a different database backend or upgrade to the latest django version.\n\n## Scrubbing third-party models\n\nSometimes you just don't have control over some code, but you still want to scrub the data of a given model.\n\nA good example is the Django user model. It contains sensitive data, and you would have to overwrite the whole model\njust to add the scrubber metaclass.\n\nThat's the way to go:\n\n1. Define your Scrubber class **somewhere** in your codebase (like a `scrubbers.py`)\n\n```python\n# scrubbers.py\nclass UserScrubbers:\n    scrubbers.Faker('de_DE')\n    first_name = scrubbers.Faker('first_name')\n    last_name = scrubbers.Faker('last_name')\n    username = scrubbers.Faker('uuid4')\n    password = scrubbers.Faker('sha1')\n    last_login = scrubbers.Null\n    email = scrubbers.Concat(first_name, models.Value('.'), last_name, models.Value('@'),\n                             models.Value(settings.SCRUBBER_DOMAIN))\n````\n\n2. Set up a mapping between your third-party model and your scrubber class\n\n```python\n# settings.py\nSCRUBBER_MAPPING = {\n    \"auth.User\": \"apps.account.scrubbers.UserScrubbers\",\n}\n```\n\n## Settings\n\n### `SCRUBBER_GLOBAL_SCRUBBERS`:\n\nDictionary of global scrubbers. Keys should be either field names as strings or field type classes. Values should be one\nof the scrubbers provided in `django_scrubber.scrubbers`.\n\nExample:\n\n```python\nSCRUBBER_GLOBAL_SCRUBBERS = {\n    'name': scrubbers.Hash,\n    EmailField: scrubbers.Hash,\n}\n```\n\n### `SCRUBBER_RANDOM_SEED`:\n\nThe seed used when generating random content by the Faker scrubber. Setting this to `None` means each scrubbing will\ngenerate different data.\n\n(default: `42`)\n\n### `SCRUBBER_ENTRIES_PER_PROVIDER`:\n\nNumber of entries to use as source for Faker scrubber. Increasing this value will increase the randomness of generated\ndata, but decrease performance.\n\n(default: `1000`)\n\n### `SCRUBBER_SKIP_UNMANAGED`:\n\nDo not attempt to scrub models which are not managed by the ORM.\n\n(default: `True`)\n\n### `SCRUBBER_APPS_LIST`:\n\nOnly scrub models belonging to these specific django apps. If unset, will scrub all installed apps.\n\n(default: `None`)\n\n### `SCRUBBER_ADDITIONAL_FAKER_PROVIDERS`:\n\nAdd additional fake providers to be used by Faker. Must be noted as full dotted path to the provider class.\n\n(default: `{*()}`, empty set)\n\n### `SCRUBBER_FAKER_LOCALE`:\n\nSet an alternative locale for Faker used during the scrubbing process.\n\n(default: `None`, falls back to Django's default locale)\n\n### `SCRUBBER_MAPPING`:\n\nDefine a class and a mapper which does not have to live inside the given model. Useful, if you have no control over the\nmodels code you'd like to scrub.\n\n````python\nSCRUBBER_MAPPING = {\n    \"auth.User\": \"my_app.scrubbers.UserScrubbers\",\n}\n````\n\n(default: `{}`)\n\n### `SCRUBBER_STRICT_MODE`:\n\nWhen strict mode is activated, you have to define a scrubbing policy for every field of every type defined in\n`SCRUBBER_REQUIRED_FIELD_TYPES`. If you have unscrubbed fields and this flag is active, you can't run\n`python manage.py scrub_data`.\n\n(default: `False`)\n\n### `SCRUBBER_REQUIRED_FIELD_TYPES`:\n\nDefaults to all text-based Django model fields. Usually, privacy-relevant data is only stored in text-fields, numbers\nand booleans (usually) can't contain sensitive personal data. These fields will be checked when running\n`python manage.py scrub_validation`.\n\n(default: `(models.CharField, models.TextField, models.URLField, models.JSONField, models.GenericIPAddressField,\n           models.EmailField,)`)\n\n### `SCRUBBER_REQUIRED_FIELD_MODEL_WHITELIST`:\n\nWhitelists a list of models which will not be checked during `scrub_validation` and when \nactivating the strict mode. Defaults to the non-privacy-related Django base models.\nItems can either be full model names (e.g. `auth.Group`) or regular expression patterns matching\nagainst the full model name (e.g. `re.compile(auth.*)` to whitelist all auth models).\n\n(default: `('auth.Group', 'auth.Permission', 'contenttypes.ContentType', 'sessions.Session', 'sites.Site', \n'django_scrubber.FakeData', 'db.TestModel',)`)\n\n(default: {})\n\n## Logging\n\nScrubber uses the default django logger. The logger name is ``django_scrubber.scrubbers``.\nSo if you want to log - for example - to the console, you could set up the logger like this:\n\n````\nLOGGING['loggers']['django_scrubber'] = {\n    'handlers': ['console'],\n    'propagate': True,\n    'level': 'DEBUG',\n}\n````\n\n## Making a new release\n\nThis project makes use of [RegioHelden's reusable GitHub workflows](https://github.com/RegioHelden/github-reusable-workflows). \\\nMake a new release by manually triggering the `Open release PR` workflow.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fregiohelden%2Fdjango-scrubber","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fregiohelden%2Fdjango-scrubber","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fregiohelden%2Fdjango-scrubber/lists"}