Swapper is an unofficial API for the undocumented but very powerful Django feature: swappable models. Swapper facilitates implementing arbitrary swappable models in your own reusable apps.
Tested on Python 2.7 and 3.4, with Django 1.6 and 1.7.
Suppose your reusable app has two related tables:
from django.db import models
class Parent(models.Model):
name = models.TextField()
class Child(models.Model):
name = models.TextField()
parent = models.ForeignKey(Parent)
Suppose further that you want to allow the user to subclass either or both of
these models and supplement them with their own implementations. You could use
Abstract classes (e.g. BaseParent
and BaseChild
) for this, but then you
would either need to:
- Avoid putting the foreign key on
BaseChild
and tell the user they need to do it. - Put the foreign key on
BaseChild
, but makeParent
a concrete model that can't be swapped - Use swappable models, together with
ForeignKeys
that read the swappable settings.
This third approach is taken by Django to facilitate swapping the auth.User model. Swapper extends this approach to apply to any model.
pip install swapper
Extending the above example, create abstract base classes and default implementations:
# reusableapp/models.py
from django.db import models
from swapper import swappable_setting, get_model_name
class BaseParent(models.Model):
# minimal base implementation ...
class Meta:
abstract = True
class Parent(BaseParent):
# default (swappable) implementation ...
class Meta:
swappable = swappable_setting('reusableapp', 'Parent')
class BaseChild(models.Model):
parent = models.ForeignKey(get_model_name('reusableapp', 'Parent'))
# minimal base implementation ...
class Meta:
abstract = True
class Child(BaseChild):
# default (swappable) implementation ...
class Meta:
swappable = swappable_setting('reusableapp', 'Child')
Then the user can override one or both models in their own app:
# myapp/models.py
from reusableapp.models import BaseParent
class Parent(BaseParent):
# custom implementation ...
The user then specifies the appropriate setting to trigger the swap:
# myproject/settings.py
REUSABLEAPP_PARENT_MODEL = "myapp.Parent"
Note: Instead of importing concrete models directly, always use the swapper:
# reusableapp/views.py
# Might work, might not
# from .models import Parent
from swapper import load_model
Parent = load_model("reusableapp", "Parent")
Child = load_model("reusableapp", "Parent")
def view(request, *args, **kwargs):
qs = Parent.objects.all()
# ...
Swapper is used extensively in the vera extension to wq.db. vera provides 7 inter-related models, each of which can be swapped out for custom implementations. (Swapper actually started out as part of wq.db.patterns, but was extracted for more general-purpose use.)