{"id":6466,"library":"swapper","title":"Swapper","description":"Swapper is an unofficial API for Django's powerful but undocumented swappable models feature. It facilitates implementing arbitrary swappable models in reusable Django applications, akin to how `auth.User` is swappable. The library is actively maintained by the OpenWISP project with a moderate release cadence, with the latest major version being 1.4.0.","status":"active","version":"1.4.0","language":"en","source_language":"en","source_url":"https://github.com/openwisp/django-swappable-models","tags":["django","swappable-models","models","reusable-apps","openwisp"],"install":[{"cmd":"pip install swapper","lang":"bash","label":"Install latest version"}],"dependencies":[{"reason":"Core functionality for swappable models is built on Django's internal features.","package":"Django","optional":false}],"imports":[{"symbol":"swapper","correct":"import swapper"},{"note":"Used to dynamically load the active model class for a swappable model, similar to `django.contrib.auth.get_user_model()`.","symbol":"load_model","correct":"from swapper import load_model"},{"note":"Used in model Meta options to declare a model as swappable.","symbol":"swappable_setting","correct":"from swapper import swappable_setting"},{"note":"Used in Django migrations to declare a swappable dependency.","symbol":"dependency","correct":"from swapper import dependency"},{"note":"Retrieves the 'app_label.ModelName' string for the currently active swappable model.","symbol":"get_model_name","correct":"from swapper import get_model_name"}],"quickstart":{"code":"import swapper\nfrom django.db import models\n\n# reusableapp/models.py\nclass BaseParent(models.Model):\n    name = models.CharField(max_length=255)\n\n    class Meta:\n        abstract = True\n\nclass Parent(BaseParent):\n    class Meta:\n        swappable = swapper.swappable_setting('reusableapp', 'Parent')\n\nclass BaseChild(models.Model):\n    parent = models.ForeignKey(\n        swapper.get_model_name('reusableapp', 'Parent'),\n        on_delete=models.CASCADE\n    )\n\n    class Meta:\n        abstract = True\n\nclass Child(BaseChild):\n    class Meta:\n        swappable = swapper.swappable_setting('reusableapp', 'Child')\n\n# reusableapp/views.py (or any other module needing the model)\n# Always use swapper.load_model() instead of direct imports\nParentModel = swapper.load_model('reusableapp', 'Parent')\nChildModel = swapper.load_model('reusableapp', 'Child')\n\ndef get_all_parents():\n    return ParentModel.objects.all()\n","lang":"python","description":"To create a swappable model in a reusable app, define abstract base classes and default implementations. Use `swapper.swappable_setting()` in the `Meta` class of the concrete model. When referencing swappable models (e.g., in ForeignKeys or in code), always use `swapper.get_model_name()` for string references or `swapper.load_model()` for the model class. This ensures that the user's swapped-in model is correctly referenced. Users then specify their custom model in Django settings (e.g., `REUSABLEAPP_PARENT_MODEL = 'myapp.MyCustomParent'`)."},"warnings":[{"fix":"Call `swapper.load_model()` within functions, methods, or after Django's app registry is ready (e.g., in `AppConfig.ready()`).","message":"Do not use `swapper.load_model()` before the Django model system has fully initialized (e.g., at module level in `models.py`). It behaves similarly to `django.contrib.auth.get_user_model()` and should be called after models are ready.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Plan for swappable models from the very beginning of your app's development. If retrofitting swappable models, be prepared for manual migration hacking or consider starting with a fresh database for the swapped implementation.","message":"Migrating from a non-swapped model implementation to a swapped one after initial migrations have been created for the non-swapped model is difficult and generally discouraged. Django's migration system makes assumptions that make this transition problematic.","severity":"breaking","affected_versions":"All versions"},{"fix":"Upgrade Python to 3.8 or newer. If not possible, pin `swapper<1.4.0` in your dependencies.","message":"Swapper v1.4.0 dropped support for Python 3.7. Applications running on Python 3.7 will need to use an older version of swapper (v1.3.0 or earlier) or upgrade their Python environment.","severity":"breaking","affected_versions":">=1.4.0"},{"fix":"Upgrade Django to a stable supported version (e.g., 4.2 or 5.0). If not possible, pin `swapper<1.4.0`.","message":"Swapper v1.4.0 dropped support for Django 4.0a1. While this was an alpha release, any applications still using this specific Django version will experience issues.","severity":"breaking","affected_versions":">=1.4.0"},{"fix":"Specify an explicit migration number for `version` if a specific migration is required, or rely on the default behavior which typically depends on the initial migration of the target app.","message":"When using `swapper.dependency()` in migrations, avoid `version='__latest__'` as it can lead to issues if new migrations are added to the depended module, potentially causing unexpected behavior or broken dependencies.","severity":"gotcha","affected_versions":"All versions"},{"fix":"If you need to override a parent link field, give it a different name than Django's default `_ptr` suffix, or ensure your custom field definition correctly aligns with the swappable model logic (e.g., referencing `swapper.get_model_name()` for the foreign key).","message":"When using multi-table inheritance with swappable models, ensure that any explicitly declared fields (especially those mimicking Django's auto-generated `_ptr` fields for parent links) do not clash with Django's automatically generated field names. This can lead to `FieldError` exceptions.","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-04-15T00:00:00.000Z","next_check":"2026-07-14T00:00:00.000Z"}