Metadata-Version: 1.1
Name: swapper
Version: 1.1.0
Summary: The unofficial Django swappable models API.
Home-page: https://github.com/wq/django-swappable-models
Author: S. Andrew Sheppard
Author-email: andrew@wq.io
License: MIT
Description: Swapper
        =======
        
        Django Swappable Models - No longer only for auth.User!
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        
        Swapper is an unofficial API for the
        `undocumented <https://code.djangoproject.com/ticket/19103>`__ but very
        powerful Django feature: swappable models. Swapper facilitates
        implementing arbitrary swappable models in your own reusable apps.
        
        |Latest PyPI Release| |Release Notes| |License| |GitHub Stars| |GitHub
        Forks| |GitHub Issues|
        
        |Travis Build Status| |Python Support| |Django Support|
        
        Motivation
        ----------
        
        Suppose your reusable app has two related tables:
        
        .. code:: python
        
            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 additional
        fields. You could use Abstract classes (e.g. ``BaseParent`` and
        ``BaseChild``) for this, but then you would either need to:
        
        1. Avoid putting the foreign key on ``BaseChild`` and tell the user they
           need to do it.
        2. Put the foreign key on ``BaseChild``, but make ``Parent`` a concrete
           model that can't be swapped
        3. 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 <https://docs.djangoproject.com/en/1.10/topics/auth/customizing/#auth-custom-user>`__.
        The ``auth.User`` swappable code was implemented in a generic way that
        allows it to be used for any model. Although this capability is
        currently `undocumented <https://code.djangoproject.com/ticket/19103>`__
        while any remaining issues are being sorted out, it has proven to be
        very stable and useful in our experience.
        
        Swapper is essentially a simple API wrapper around this existing
        functionality. Note that Swapper is primarily a tool for library
        authors; users of your reusable app generally should not need to know
        about Swapper in order to use it. (See the notes on `End User
        Documentation <#end-user-documentation>`__ below.)
        
        Real-World Example
        ~~~~~~~~~~~~~~~~~~
        
        Swapper is used extensively in the `vera <http://wq.io/vera>`__
        extension to `wq.db <http://wq.io/wq.db>`__. vera provides `7
        inter-related models <https://github.com/wq/vera#models>`__, each of
        which can be swapped out for custom implementations. (Swapper actually
        started out as part of
        `wq.db.patterns <http://wq.io/docs/about-patterns>`__, but was extracted
        for more general-purpose use.)
        
        Creating a Reusable App
        -----------------------
        
        First, make sure you have ``swapper`` installed. If you are publishing
        your reusable app as a Python package, be sure to add ``swapper`` to
        your project's dependencies (e.g. ``setup.py``) to ensure that users of
        your app don't have errors integrating it.
        
        .. code:: bash
        
            pip3 install swapper
        
        Extending the above example, you might create two abstract base classes
        and corresponding default implementations:
        
        .. code:: python
        
            # reusableapp/models.py
            from django.db import models
            import swapper
        
            class BaseParent(models.Model):
                # minimal base implementation ...
                class Meta:
                    abstract = True
        
            class Parent(BaseParent):
                # default (swappable) implementation ...
                class Meta:
                   swappable = swapper.swappable_setting('reusableapp', 'Parent')
        
            class BaseChild(models.Model):
                parent = models.ForeignKey(swapper.get_model_name('reusableapp', 'Parent'))
                # minimal base implementation ...
                class Meta:
                    abstract = True
        
            class Child(BaseChild):
                # default (swappable) implementation ...
                class Meta:
                   swappable = swapper.swappable_setting('reusableapp', 'Child')
        
        Loading Swapped Models
        ~~~~~~~~~~~~~~~~~~~~~~
        
        In your reusable views and other functions, always use the swapper
        instead of importing swappable models directly. This is because you
        might not know whether the user of your app is using your default
        implementation or their own version.
        
        .. code:: python
        
            # reusableapp/views.py
        
            # Might work, might not
            # from .models import Parent
        
            import swapper
            Parent = swapper.load_model("reusableapp", "Parent")
            Child = swapper.load_model("reusableapp", "Child")
        
            def view(request, *args, **kwargs):
                qs = Parent.objects.all()
                # ...
        
            Note: ``swapper.load_model()`` is the general equivalent of
            `get\_user\_model() <https://docs.djangoproject.com/en/1.10/topics/auth/customizing/#referencing-the-user-model>`__
            and subject to the same constraints: e.g. it should not be used
            until after the model system has fully initialized.
        
        Migration Scripts
        ~~~~~~~~~~~~~~~~~
        
        Swapper can also be used in Django 1.7+ migration scripts to facilitate
        dependency ordering and foreign key references. To use this feature in
        your library, generate a migration script with ``makemigrations`` and
        make the following changes. In general, users of your library should not
        need to make any similar changes to their own migration scripts. The one
        exception is if you have multiple levels of swappable models with
        foreign keys pointing to each other (as in
        `vera <http://wq.io/vera>`__).
        
        .. code:: diff
        
              # reusableapp/migrations/0001_initial.py
        
              from django.db import models, migrations
            < from django.conf import settings
            > import swapper
        
              class Migration(migrations.Migration):
        
                  dependencies = [
            <          migrations.swappable_dependency(settings.REUSABLEAPP_PARENT_MODEL),
            >          swapper.dependency('reusableapp', 'Parent')
                  ]
        
                  operations = [
                      migrations.CreateModel(
                          name='Child',
                          fields=[
                              ('id', models.AutoField(auto_created=True, serialize=False, primary_key=True, verbose_name='ID')),
                          ],
                          options={
            <                 'swappable': 'REUSABLEAPP_CHILD_MODEL',
            >                 'swappable': swapper.swappable_setting('reusableapp', 'Child'),
                          },
                          bases=(models.Model,),
                      ),
                      migrations.CreateModel(
                          name='Parent',
                          fields=[
                              ('id', models.AutoField(auto_created=True, serialize=False, primary_key=True, verbose_name='ID')),
                          ],
                          options={
            <                 'swappable': 'REUSABLEAPP_PARENT_MODEL',
            >                 'swappable': swapper.swappable_setting('reusableapp', 'Parent'),
                          },
                          bases=(models.Model,),
                      ),
                      migrations.AddField(
                          model_name='child',
                          name='parent',
            <             field=models.ForeignKey(to=settings.REUSABLEAPP_PARENT_MODEL),
            >             field=models.ForeignKey(to=swapper.get_model_name('reusableapp', 'Parent')),
                          preserve_default=True,
                      ),
                  ]
        
        End User Documentation
        ----------------------
        
        With the above setup, the user of your app can override one or both
        models in their own app. You might provide them with an example like
        this:
        
        .. code:: python
        
            # myapp/models.py
            from reusableapp.models import BaseParent
            class Parent(BaseParent):
                # custom implementation ...
        
        Then, tell your users to update their settings to trigger the swap.
        
        .. code:: python
        
            # myproject/settings.py
            REUSABLEAPP_PARENT_MODEL = "myapp.Parent"
        
        The goal is to make this process just as easy for your end user as
        `swapping the auth.User
        model <https://docs.djangoproject.com/en/1.10/topics/auth/customizing/#auth-custom-user>`__
        is. As with ``auth.User``, there are some important caveats that you may
        want to inform your users about.
        
        The biggest issue is that your users will probably need to define the
        swapped model settings **before creating any migrations** for their
        implementation of ``myapp``. Due to key assumptions made within Django's
        migration infrastructure, it is difficult to start out with a default
        (non-swapped) model and then later to switch to a swapped implementation
        without doing some migration hacking. This is somewhat awkward - as your
        users will most likely want to try out your default implementation
        before deciding to customize it. Unfortunately, there isn't an easy
        workaround due to how the swappable setting is currently implemented in
        Django core. This will likely be addressed in future Django versions
        (see `#10 <https://github.com/wq/django-swappable-models/issues/10>`__
        and `Django ticket
        #25313 <https://code.djangoproject.com/ticket/25313>`__).
        
        API Documentation
        -----------------
        
        Here is the full API for ``swapper``, which you may find useful in
        creating your reusable app code. End users of your library should
        generally not need to reference this API.
        
        +------------+-----------+
        | function   | purpose   |
        +============+===========+
        | ``swappabl | Generates |
        | e_setting( | a         |
        | app_label, | swappable |
        |  model)``  | setting   |
        |            | name for  |
        |            | the       |
        |            | provided  |
        |            | model     |
        |            | (e.g.     |
        |            | ``"REUSAB |
        |            | LEAPP_PAR |
        |            | ENT_MODEL |
        |            | "``)      |
        +------------+-----------+
        | ``is_swapp | Determine |
        | ed(app_lab | s         |
        | el, model) | whether   |
        | ``         | or not a  |
        |            | given     |
        |            | model has |
        |            | been      |
        |            | swapped.  |
        |            | (Returns  |
        |            | the model |
        |            | name if   |
        |            | swapped,  |
        |            | otherwise |
        |            | ``False`` |
        |            | )         |
        +------------+-----------+
        | ``get_mode | Gets the  |
        | l_name(app | name of   |
        | _label, mo | the model |
        | del)``     | the       |
        |            | swappable |
        |            | model has |
        |            | been      |
        |            | swapped   |
        |            | for (or   |
        |            | the name  |
        |            | of the    |
        |            | original  |
        |            | model if  |
        |            | not       |
        |            | swapped.) |
        +------------+-----------+
        | ``get_mode | Match a   |
        | l_names(ap | list of   |
        | p_label, m | model     |
        | odels)``   | names to  |
        |            | their     |
        |            | swapped   |
        |            | versions. |
        |            | All of    |
        |            | the       |
        |            | models    |
        |            | should be |
        |            | from the  |
        |            | same app  |
        |            | (though   |
        |            | their     |
        |            | swapped   |
        |            | versions  |
        |            | need not  |
        |            | be).      |
        +------------+-----------+
        | ``load_mod | Load the  |
        | el(app_lab | swapped   |
        | el, model, | model     |
        |  required= | class for |
        | True)``    | a         |
        |            | swappable |
        |            | model (or |
        |            | the       |
        |            | original  |
        |            | model if  |
        |            | it hasn't |
        |            | been      |
        |            | swapped). |
        |            | If your   |
        |            | code can  |
        |            | function  |
        |            | without   |
        |            | the       |
        |            | specified |
        |            | model,    |
        |            | set       |
        |            | ``require |
        |            | d = False |
        |            | ``.       |
        +------------+-----------+
        | ``dependen | Generate  |
        | cy(app_lab | a         |
        | el, model) | dependenc |
        | ``         | y         |
        |            | tuple for |
        |            | use in    |
        |            | Django    |
        |            | 1.7+      |
        |            | migration |
        |            | s.        |
        +------------+-----------+
        | ``set_app_ | Set a     |
        | prefix(app | custom    |
        | _label, pr | prefix    |
        | efix)``    | for       |
        |            | swappable |
        |            | settings  |
        |            | (the      |
        |            | default   |
        |            | is the    |
        |            | upper     |
        |            | case      |
        |            | ``app_lab |
        |            | el``).    |
        |            | Used in   |
        |            | `wq.db <h |
        |            | ttp://wq. |
        |            | io/wq.db> |
        |            | `__       |
        |            | to make   |
        |            | all of    |
        |            | the       |
        |            | swappable |
        |            | settings  |
        |            | start     |
        |            | with      |
        |            | ``"WQ"``  |
        |            | (e.g.     |
        |            | ``WQ_FILE |
        |            | _MODEL``  |
        |            | instead   |
        |            | of        |
        |            | ``FILES_F |
        |            | ILE_MODEL |
        |            | ``).      |
        |            | This      |
        |            | should be |
        |            | set at    |
        |            | the top   |
        |            | of your   |
        |            | models.py |
        |            | .         |
        +------------+-----------+
        | ``join(app | Utilities |
        | _label, mo | for       |
        | del)``,    | splitting |
        | ``split(mo | and       |
        | del)``     | joining   |
        |            | ``"app.Mo |
        |            | del"``    |
        |            | strings   |
        |            | and       |
        |            | ``("app", |
        |            |  "Model") |
        |            | ``        |
        |            | tuples.   |
        +------------+-----------+
        
        .. |Latest PyPI Release| image:: https://img.shields.io/pypi/v/swapper.svg
           :target: https://pypi.python.org/pypi/swapper
        .. |Release Notes| image:: https://img.shields.io/github/release/wq/django-swappable-models.svg
           :target: https://github.com/wq/django-swappable-models/releases
        .. |License| image:: https://img.shields.io/pypi/l/swapper.svg
           :target: https://github.com/wq/django-swappable-models/blob/master/LICENSE
        .. |GitHub Stars| image:: https://img.shields.io/github/stars/wq/django-swappable-models.svg
           :target: https://github.com/wq/django-swappable-models/stargazers
        .. |GitHub Forks| image:: https://img.shields.io/github/forks/wq/django-swappable-models.svg
           :target: https://github.com/wq/django-swappable-models/network
        .. |GitHub Issues| image:: https://img.shields.io/github/issues/wq/django-swappable-models.svg
           :target: https://github.com/wq/django-swappable-models/issues
        .. |Travis Build Status| image:: https://img.shields.io/travis/wq/django-swappable-models.svg
           :target: https://travis-ci.org/wq/django-swappable-models
        .. |Python Support| image:: https://img.shields.io/pypi/pyversions/swapper.svg
           :target: https://pypi.python.org/pypi/swapper
        .. |Django Support| image:: https://img.shields.io/badge/Django-1.6%2C%201.7%2C%201.8%2C%201.9%2C%201.10%2C%201.11-blue.svg
           :target: https://pypi.python.org/pypi/swapper
        
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: License :: OSI Approved :: MIT License
Classifier: Natural Language :: English
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Framework :: Django
Classifier: Framework :: Django :: 1.6
Classifier: Framework :: Django :: 1.7
Classifier: Framework :: Django :: 1.8
Classifier: Framework :: Django :: 1.9
Classifier: Framework :: Django :: 1.10
Classifier: Framework :: Django :: 1.11
