{"id":7170,"library":"django-encrypted-model-fields","title":"Django Encrypted Model Fields","description":"django-encrypted-model-fields provides a set of Django model fields that encrypt data before saving it to the database, leveraging the robust `cryptography` library. It's currently at version 0.6.5 and typically follows Django's release cycle for compatibility, with more frequent updates for bug fixes or features. It ensures data at rest is protected.","status":"active","version":"0.6.5","language":"en","source_language":"en","source_url":"https://github.com/JonathanStahl/django-encrypted-model-fields","tags":["django","encryption","security","models","fields","cryptography"],"install":[{"cmd":"pip install django-encrypted-model-fields","lang":"bash","label":"Install stable version"}],"dependencies":[{"reason":"Core framework dependency, requires >=2.0","package":"Django","optional":false},{"reason":"Provides the underlying encryption primitives","package":"cryptography","optional":false}],"imports":[{"symbol":"EncryptedCharField","correct":"from encrypted_model_fields.fields import EncryptedCharField"},{"symbol":"EncryptedTextField","correct":"from encrypted_model_fields.fields import EncryptedTextField"},{"symbol":"EncryptedIntegerField","correct":"from encrypted_model_fields.fields import EncryptedIntegerField"},{"symbol":"EncryptedJSONField","correct":"from encrypted_model_fields.fields import EncryptedJSONField"}],"quickstart":{"code":"import os\nfrom django.conf import settings\nfrom django.db import models\n\n# Minimal Django settings for standalone script testing\nif not settings.configured:\n    settings.configure(\n        DEBUG=True,\n        INSTALLED_APPS=[\n            'encrypted_model_fields' # Required for migrations/field registration\n        ],\n        DATABASES={\n            'default': {\n                'ENGINE': 'django.db.backends.sqlite3',\n                'NAME': ':memory:',\n            }\n        },\n        # CRUCIAL: Define encryption keys\n        ENCRYPTED_FIELD_KEYS={\n            'default': os.environ.get('ENCRYPTED_FIELD_DEFAULT_KEY', 'a_secret_key_for_dev_or_testing_ONLY_do_not_use_in_prod'),\n            'billing': os.environ.get('ENCRYPTED_FIELD_BILLING_KEY', 'another_key_for_billing_data')\n        },\n        # Required for any Django project\n        SECRET_KEY='django-insecure-testkey-very-insecure'\n    )\n\n# Ensure apps are loaded (needed for standalone scripts)\nimport django\ndjango.setup()\n\nfrom encrypted_model_fields.fields import EncryptedCharField, EncryptedTextField\n\nclass Customer(models.Model):\n    name = models.CharField(max_length=100)\n    email = EncryptedCharField(max_length=255)\n    notes = EncryptedTextField(blank=True, null=True, key_name='billing') # Use a specific key\n\n    def __str__(self):\n        return self.name\n\n# Example usage (requires database setup, e.g., via manage.py migrate)\nif __name__ == '__main__':\n    # This part would typically be in a Django shell or view\n    print(\"--- Quickstart Example ---\")\n\n    # In a real Django project, you'd run 'python manage.py makemigrations' and 'python manage.py migrate'\n    # For this standalone script, we'll simulate migrations for Customer model\n    from django.apps import apps\n    from django.core.management.commands import makemigrations, migrate\n    from io import StringIO\n\n    # Simulate makemigrations\n    # Note: This is a hack for a standalone script, not for production use.\n    # In a real project, 'encrypted_model_fields' would be in INSTALLED_APPS and 'python manage.py makemigrations/migrate' would be run.\n\n    # Create a simple fake app for the model\n    class FakeAppConfig(apps.AppConfig):\n        name = 'my_app'\n        verbose_name = 'My App'\n\n    apps.apps_ready = False # Reset app loading for standalone execution\n    apps.clear_cache()\n    apps.set_available_apps(['my_app'])\n    apps.populate(settings.INSTALLED_APPS + ['my_app'])\n    settings.INSTALLED_APPS += ['my_app']\n    apps.get_app_config('my_app').models = {'customer': Customer}\n\n    # For real migration, save models.py in an app and run commands.\n    # For this quickstart, we'll just interact with the model directly in memory after setup.\n\n    # Create a record\n    customer = Customer.objects.create(name='Alice', email='alice@example.com', notes='Confidential billing info.')\n    print(f\"Created customer: {customer.name}\")\n    print(f\"Stored email (encrypted): {customer.email}\") # Will show encrypted value if accessed directly before decrypt\n\n    # Retrieve and decrypt\n    retrieved_customer = Customer.objects.get(name='Alice')\n    print(f\"Retrieved email (decrypted): {retrieved_customer.email}\")\n    print(f\"Retrieved notes (decrypted): {retrieved_customer.notes}\")\n\n    # Update\n    retrieved_customer.email = 'alice.smith@example.com'\n    retrieved_customer.save()\n    print(f\"Updated email: {Customer.objects.get(name='Alice').email}\")","lang":"python","description":"This quickstart demonstrates defining a Django model with `EncryptedCharField` and `EncryptedTextField`. It highlights the critical `ENCRYPTED_FIELD_KEYS` setting in `settings.py`, which maps key names to their actual encryption keys. Data saved using these fields will be automatically encrypted and decrypted on access. Note the use of `os.environ.get` for secure key management. The standalone script includes a simplified Django setup for immediate execution (requires `Django` to be installed), but in a real project, you'd integrate the models into your existing Django application and manage migrations via `manage.py`."},"warnings":[{"fix":"Before upgrading, decrypt all data using the old version, upgrade the library, then re-encrypt all data using the new version. This is a manual process and requires careful planning and backup.","message":"Data encrypted with versions prior to 0.6.0 (which used `pycrypto`) is NOT readable by versions 0.6.0 and later (which use `cryptography`). An explicit data migration is required if upgrading from versions < 0.6.0 to >= 0.6.0.","severity":"breaking","affected_versions":"<0.6.0 to >=0.6.0"},{"fix":"Migrate your `settings.py` to use `ENCRYPTED_FIELD_KEYS` as a dictionary mapping key names to their string values. For example, `ENCRYPTED_FIELD_KEYS = {'default': 'your_default_key', 'some_other_key': 'another_key'}`.","message":"The `KEY_GENERATOR` and `KEY_STORE` settings were removed in version 0.6.0. They have been replaced by the `ENCRYPTED_FIELD_KEYS` dictionary setting.","severity":"breaking","affected_versions":"<0.6.0 to >=0.6.0"},{"fix":"Ensure `ENCRYPTED_FIELD_KEYS` is defined in `settings.py` as a dictionary, with at least a 'default' key if not using explicit `key_name` arguments on fields. For production, keys should be loaded from environment variables or a secure secret management system, NOT hardcoded.","message":"Failing to define `ENCRYPTED_FIELD_KEYS` in your Django `settings.py` or providing an invalid structure (e.g., not a dictionary or string) will lead to an `ImproperlyConfigured` exception or `MissingKeyError` when attempting to save or retrieve data.","severity":"gotcha","affected_versions":">=0.6.0"}],"env_vars":null,"last_verified":"2026-04-16T00:00:00.000Z","next_check":"2026-07-15T00:00:00.000Z","problems":[{"fix":"Add `ENCRYPTED_FIELD_KEYS = {'default': 'your_secret_key'}` to your `settings.py`. Ensure it's a dictionary for multiple keys or a single string for a global default.","cause":"`ENCRYPTED_FIELD_KEYS` is missing from `settings.py` or is defined with an incorrect type (e.g., `None` or an empty tuple).","error":"django.core.exceptions.ImproperlyConfigured: ENCRYPTED_FIELD_KEYS setting must be a dictionary or a string."},{"fix":"Ensure that every `key_name` used in your `EncryptedField` definitions has a corresponding entry in `ENCRYPTED_FIELD_KEYS` in `settings.py`. For example, add `'my_custom_key': 'value_for_custom_key'` to the dictionary.","cause":"An `EncryptedField` was defined with `key_name='my_custom_key'`, but 'my_custom_key' is not present as a key in the `ENCRYPTED_FIELD_KEYS` dictionary in `settings.py`.","error":"encrypted_model_fields.exceptions.MissingKeyError: Key 'my_custom_key' not found in ENCRYPTED_FIELD_KEYS"},{"fix":"Verify that the `ENCRYPTED_FIELD_KEYS` used to decrypt the data is identical to the key used to encrypt it. Check for accidental key rotation or discrepancies between environments (e.g., development vs. production keys).","cause":"This error typically originates from the `cryptography` library. It indicates that the data being decrypted has been tampered with, or the wrong encryption key is being used for decryption, or the data itself is corrupted. This can happen if `ENCRYPTED_FIELD_KEYS` changes after data has been written.","error":"ValueError: Ciphertext failed verification"}]}