Hello community, here is the log from the commit of package python-swapper for openSUSE:Factory checked in at 2019-08-05 10:37:08 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-swapper (Old) and /work/SRC/openSUSE:Factory/.python-swapper.new.4126 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-swapper" Mon Aug 5 10:37:08 2019 rev:2 rq:720178 version:1.1.1 Changes: -------- --- /work/SRC/openSUSE:Factory/python-swapper/python-swapper.changes 2019-07-04 15:42:38.302049857 +0200 +++ /work/SRC/openSUSE:Factory/.python-swapper.new.4126/python-swapper.changes 2019-08-05 10:37:12.707328619 +0200 @@ -1,0 +2,7 @@ +Thu Aug 1 11:05:09 UTC 2019 - Tomáš Chvátal <tchva...@suse.com> + +- Update to 1.1.1: + * Verify Django 2 support in tests (#17 via @gasman). + * Test on Python 3.7 and drop Python 2.6 and 3.3. + +------------------------------------------------------------------- Old: ---- swapper-1.1.0.tar.gz New: ---- swapper-1.1.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-swapper.spec ++++++ --- /var/tmp/diff_new_pack.BayB5q/_old 2019-08-05 10:37:14.443328408 +0200 +++ /var/tmp/diff_new_pack.BayB5q/_new 2019-08-05 10:37:14.475328404 +0200 @@ -18,14 +18,15 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-swapper -Version: 1.1.0 +Version: 1.1.1 Release: 0 Summary: The unofficial Django swappable models API License: MIT Group: Development/Languages/Python URL: https://github.com/wq/django-swappable-models -Source: https://github.com/wq/django-swappable-models/archive/v%{version}.tar.gz#/swapper-%{version}.tar.gz +Source: https://files.pythonhosted.org/packages/source/s/swapper/swapper-%{version}.tar.gz BuildRequires: %{python_module Django1 >= 1.6} +BuildRequires: %{python_module setuptools_scm} BuildRequires: %{python_module setuptools} BuildRequires: fdupes BuildRequires: python-rpm-macros @@ -39,7 +40,7 @@ implementing arbitrary swappable models in your own reusable apps. %prep -%setup -q -n django-swappable-models-%{version} +%setup -q -n swapper-%{version} %build %python_build ++++++ swapper-1.1.0.tar.gz -> swapper-1.1.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-swappable-models-1.1.0/.travis.yml new/swapper-1.1.1/.travis.yml --- old/django-swappable-models-1.1.0/.travis.yml 2017-05-11 04:00:30.000000000 +0200 +++ new/swapper-1.1.1/.travis.yml 2019-07-23 04:57:18.000000000 +0200 @@ -1,11 +1,12 @@ -sudo: false language: python +dist: xenial python: - - "2.6" - "2.7" - - "3.3" - "3.4" - "3.5" - "3.6" -install: pip install tox-travis flake8 + - "3.7" +install: + - pip install tox-travis + - pip install flake8 script: tox diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-swappable-models-1.1.0/PKG-INFO new/swapper-1.1.1/PKG-INFO --- old/django-swappable-models-1.1.0/PKG-INFO 1970-01-01 01:00:00.000000000 +0100 +++ new/swapper-1.1.1/PKG-INFO 2019-07-23 04:57:51.000000000 +0200 @@ -0,0 +1,239 @@ +Metadata-Version: 2.1 +Name: swapper +Version: 1.1.1 +Summary: The unofficial Django swappable models API. +Home-page: https://github.com/wq/django-swappable-models +Author: S. Andrew Sheppard +Author-email: and...@wq.io +License: MIT +Description: Swapper + ======= + + #### Django Swappable Models - No longer only for auth.User! + + 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. + + [![Latest PyPI Release](https://img.shields.io/pypi/v/swapper.svg)](https://pypi.org/project/swapper) + [![Release Notes](https://img.shields.io/github/release/wq/django-swappable-models.svg + )](https://github.com/wq/django-swappable-models/releases) + [![License](https://img.shields.io/pypi/l/swapper.svg)](https://github.com/wq/django-swappable-models/blob/master/LICENSE) + [![GitHub Stars](https://img.shields.io/github/stars/wq/django-swappable-models.svg)](https://github.com/wq/django-swappable-models/stargazers) + [![GitHub Forks](https://img.shields.io/github/forks/wq/django-swappable-models.svg)](https://github.com/wq/django-swappable-models/network) + [![GitHub Issues](https://img.shields.io/github/issues/wq/django-swappable-models.svg)](https://github.com/wq/django-swappable-models/issues) + + [![Travis Build Status](https://img.shields.io/travis/wq/django-swappable-models.svg)](https://travis-ci.org/wq/django-swappable-models) + [![Python Support](https://img.shields.io/pypi/pyversions/swapper.svg)](https://pypi.org/project/swapper) + [![Django Support](https://img.shields.io/pypi/djversions/swapper.svg)](https://pypi.org/project/swapper) + + ## Motivation + + Suppose your reusable app has two related tables: + + ```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]. 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] 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] 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.) + + ## 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. + + ```bash + pip3 install swapper + ``` + Extending the above example, you might create two abstract base classes and corresponding default implementations: + + ```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. + + ```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()] 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]). + + ```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: + + ```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. + + ```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] 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] and [Django 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 + ---------|-------- + `swappable_setting(app_label, model)` | Generates a swappable setting name for the provided model (e.g. `"REUSABLEAPP_PARENT_MODEL"`) + `is_swapped(app_label, model)` | Determines whether or not a given model has been swapped. (Returns the model name if swapped, otherwise `False`) + `get_model_name(app_label, model)` | Gets the name of the model the swappable model has been swapped for (or the name of the original model if not swapped.) + `get_model_names(app_label, models)` | Match a list of model names to their swapped versions. All of the models should be from the same app (though their swapped versions need not be). + `load_model(app_label, model, required=True)` | Load the swapped model class for a swappable model (or the original model if it hasn't been swapped). If your code can function without the specified model, set `required = False`. + `dependency(app_label, model)` | Generate a dependency tuple for use in Django 1.7+ migrations. + `set_app_prefix(app_label, prefix)` | Set a custom prefix for swappable settings (the default is the upper case `app_label`). Used in [wq.db] to make all of the swappable settings start with `"WQ"` (e.g. `WQ_FILE_MODEL` instead of `FILES_FILE_MODEL`). This should be set at the top of your models.py. + `join(app_label, model)`, `split(model)` | Utilities for splitting and joining `"app.Model"` strings and `("app", "Model")` tuples. + + [undocumented]: https://code.djangoproject.com/ticket/19103 + [swapping the auth.User model]: https://docs.djangoproject.com/en/1.10/topics/auth/customizing/#auth-custom-user + [wq.db]: http://wq.io/wq.db + [vera]: http://wq.io/vera + [wq.db.patterns]: http://wq.io/docs/about-patterns + [7 inter-related models]: https://github.com/wq/vera#models + [get_user_model()]: https://docs.djangoproject.com/en/1.10/topics/auth/customizing/#referencing-the-user-model + [#10]: https://github.com/wq/django-swappable-models/issues/10 + [Django ticket #25313]: https://code.djangoproject.com/ticket/25313 + +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.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +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 +Classifier: Framework :: Django :: 2.0 +Classifier: Framework :: Django :: 2.1 +Classifier: Framework :: Django :: 2.2 +Description-Content-Type: text/markdown diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-swappable-models-1.1.0/README.md new/swapper-1.1.1/README.md --- old/django-swappable-models-1.1.0/README.md 2017-05-11 04:00:30.000000000 +0200 +++ new/swapper-1.1.1/README.md 2019-07-23 04:57:18.000000000 +0200 @@ -7,7 +7,7 @@ feature: swappable models. Swapper facilitates implementing arbitrary swappable models in your own reusable apps. -[![Latest PyPI Release](https://img.shields.io/pypi/v/swapper.svg)](https://pypi.python.org/pypi/swapper) +[![Latest PyPI Release](https://img.shields.io/pypi/v/swapper.svg)](https://pypi.org/project/swapper) [![Release Notes](https://img.shields.io/github/release/wq/django-swappable-models.svg )](https://github.com/wq/django-swappable-models/releases) [![License](https://img.shields.io/pypi/l/swapper.svg)](https://github.com/wq/django-swappable-models/blob/master/LICENSE) @@ -16,8 +16,8 @@ [![GitHub Issues](https://img.shields.io/github/issues/wq/django-swappable-models.svg)](https://github.com/wq/django-swappable-models/issues) [![Travis Build Status](https://img.shields.io/travis/wq/django-swappable-models.svg)](https://travis-ci.org/wq/django-swappable-models) -[![Python Support](https://img.shields.io/pypi/pyversions/swapper.svg)](https://pypi.python.org/pypi/swapper) -[![Django Support](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)](https://pypi.python.org/pypi/swapper) +[![Python Support](https://img.shields.io/pypi/pyversions/swapper.svg)](https://pypi.org/project/swapper) +[![Django Support](https://img.shields.io/pypi/djversions/swapper.svg)](https://pypi.org/project/swapper) ## Motivation diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-swappable-models-1.1.0/setup.cfg new/swapper-1.1.1/setup.cfg --- old/django-swappable-models-1.1.0/setup.cfg 1970-01-01 01:00:00.000000000 +0100 +++ new/swapper-1.1.1/setup.cfg 2019-07-23 04:57:51.000000000 +0200 @@ -0,0 +1,4 @@ +[egg_info] +tag_build = +tag_date = 0 + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-swappable-models-1.1.0/setup.py new/swapper-1.1.1/setup.py --- old/django-swappable-models-1.1.0/setup.py 2017-05-11 04:00:30.000000000 +0200 +++ new/swapper-1.1.1/setup.py 2019-07-23 04:57:18.000000000 +0200 @@ -7,40 +7,26 @@ """ -def parse_markdown_readme(): - """ - Convert README.md to RST via pandoc, and load into memory - (fallback to LONG_DESCRIPTION on failure) - """ - # Attempt to run pandoc on markdown file - import subprocess +def readme(): try: - subprocess.call( - ['pandoc', '-t', 'rst', '-o', 'README.rst', 'README.md'] - ) - except OSError: - return LONG_DESCRIPTION - - # Attempt to load output - try: - readme = open(os.path.join( - os.path.dirname(__file__), - 'README.rst' - )) + readme = open('README.md') except IOError: return LONG_DESCRIPTION - return readme.read() + else: + return readme.read() + setup( name='swapper', - version='1.1.0', + use_scm_version=True, author='S. Andrew Sheppard', author_email='and...@wq.io', url='https://github.com/wq/django-swappable-models', license='MIT', packages=['swapper'], description=LONG_DESCRIPTION.strip(), - long_description=parse_markdown_readme(), + long_description=readme(), + long_description_content_type="text/markdown", classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Web Environment', @@ -48,13 +34,12 @@ 'Natural Language :: English', 'Operating System :: OS Independent', 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', 'Framework :: Django', 'Framework :: Django :: 1.6', 'Framework :: Django :: 1.7', @@ -62,7 +47,13 @@ 'Framework :: Django :: 1.9', 'Framework :: Django :: 1.10', 'Framework :: Django :: 1.11', + 'Framework :: Django :: 2.0', + 'Framework :: Django :: 2.1', + 'Framework :: Django :: 2.2', ], tests_require=['django>=1.6'], test_suite='tests', + setup_requires=[ + 'setuptools_scm', + ], ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-swappable-models-1.1.0/swapper.egg-info/PKG-INFO new/swapper-1.1.1/swapper.egg-info/PKG-INFO --- old/django-swappable-models-1.1.0/swapper.egg-info/PKG-INFO 1970-01-01 01:00:00.000000000 +0100 +++ new/swapper-1.1.1/swapper.egg-info/PKG-INFO 2019-07-23 04:57:51.000000000 +0200 @@ -0,0 +1,239 @@ +Metadata-Version: 2.1 +Name: swapper +Version: 1.1.1 +Summary: The unofficial Django swappable models API. +Home-page: https://github.com/wq/django-swappable-models +Author: S. Andrew Sheppard +Author-email: and...@wq.io +License: MIT +Description: Swapper + ======= + + #### Django Swappable Models - No longer only for auth.User! + + 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. + + [![Latest PyPI Release](https://img.shields.io/pypi/v/swapper.svg)](https://pypi.org/project/swapper) + [![Release Notes](https://img.shields.io/github/release/wq/django-swappable-models.svg + )](https://github.com/wq/django-swappable-models/releases) + [![License](https://img.shields.io/pypi/l/swapper.svg)](https://github.com/wq/django-swappable-models/blob/master/LICENSE) + [![GitHub Stars](https://img.shields.io/github/stars/wq/django-swappable-models.svg)](https://github.com/wq/django-swappable-models/stargazers) + [![GitHub Forks](https://img.shields.io/github/forks/wq/django-swappable-models.svg)](https://github.com/wq/django-swappable-models/network) + [![GitHub Issues](https://img.shields.io/github/issues/wq/django-swappable-models.svg)](https://github.com/wq/django-swappable-models/issues) + + [![Travis Build Status](https://img.shields.io/travis/wq/django-swappable-models.svg)](https://travis-ci.org/wq/django-swappable-models) + [![Python Support](https://img.shields.io/pypi/pyversions/swapper.svg)](https://pypi.org/project/swapper) + [![Django Support](https://img.shields.io/pypi/djversions/swapper.svg)](https://pypi.org/project/swapper) + + ## Motivation + + Suppose your reusable app has two related tables: + + ```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]. 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] 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] 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.) + + ## 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. + + ```bash + pip3 install swapper + ``` + Extending the above example, you might create two abstract base classes and corresponding default implementations: + + ```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. + + ```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()] 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]). + + ```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: + + ```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. + + ```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] 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] and [Django 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 + ---------|-------- + `swappable_setting(app_label, model)` | Generates a swappable setting name for the provided model (e.g. `"REUSABLEAPP_PARENT_MODEL"`) + `is_swapped(app_label, model)` | Determines whether or not a given model has been swapped. (Returns the model name if swapped, otherwise `False`) + `get_model_name(app_label, model)` | Gets the name of the model the swappable model has been swapped for (or the name of the original model if not swapped.) + `get_model_names(app_label, models)` | Match a list of model names to their swapped versions. All of the models should be from the same app (though their swapped versions need not be). + `load_model(app_label, model, required=True)` | Load the swapped model class for a swappable model (or the original model if it hasn't been swapped). If your code can function without the specified model, set `required = False`. + `dependency(app_label, model)` | Generate a dependency tuple for use in Django 1.7+ migrations. + `set_app_prefix(app_label, prefix)` | Set a custom prefix for swappable settings (the default is the upper case `app_label`). Used in [wq.db] to make all of the swappable settings start with `"WQ"` (e.g. `WQ_FILE_MODEL` instead of `FILES_FILE_MODEL`). This should be set at the top of your models.py. + `join(app_label, model)`, `split(model)` | Utilities for splitting and joining `"app.Model"` strings and `("app", "Model")` tuples. + + [undocumented]: https://code.djangoproject.com/ticket/19103 + [swapping the auth.User model]: https://docs.djangoproject.com/en/1.10/topics/auth/customizing/#auth-custom-user + [wq.db]: http://wq.io/wq.db + [vera]: http://wq.io/vera + [wq.db.patterns]: http://wq.io/docs/about-patterns + [7 inter-related models]: https://github.com/wq/vera#models + [get_user_model()]: https://docs.djangoproject.com/en/1.10/topics/auth/customizing/#referencing-the-user-model + [#10]: https://github.com/wq/django-swappable-models/issues/10 + [Django ticket #25313]: https://code.djangoproject.com/ticket/25313 + +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.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +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 +Classifier: Framework :: Django :: 2.0 +Classifier: Framework :: Django :: 2.1 +Classifier: Framework :: Django :: 2.2 +Description-Content-Type: text/markdown diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-swappable-models-1.1.0/swapper.egg-info/SOURCES.txt new/swapper-1.1.1/swapper.egg-info/SOURCES.txt --- old/django-swappable-models-1.1.0/swapper.egg-info/SOURCES.txt 1970-01-01 01:00:00.000000000 +0100 +++ new/swapper-1.1.1/swapper.egg-info/SOURCES.txt 2019-07-23 04:57:51.000000000 +0200 @@ -0,0 +1,19 @@ +.gitignore +.travis.yml +LICENSE +README.md +setup.py +tox.ini +swapper/__init__.py +swapper.egg-info/PKG-INFO +swapper.egg-info/SOURCES.txt +swapper.egg-info/dependency_links.txt +swapper.egg-info/top_level.txt +tests/__init__.py +tests/settings.py +tests/swap_settings.py +tests/test_swapper.py +tests/alt_app/__init__.py +tests/alt_app/models.py +tests/default_app/__init__.py +tests/default_app/models.py \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-swappable-models-1.1.0/swapper.egg-info/dependency_links.txt new/swapper-1.1.1/swapper.egg-info/dependency_links.txt --- old/django-swappable-models-1.1.0/swapper.egg-info/dependency_links.txt 1970-01-01 01:00:00.000000000 +0100 +++ new/swapper-1.1.1/swapper.egg-info/dependency_links.txt 2019-07-23 04:57:51.000000000 +0200 @@ -0,0 +1 @@ + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-swappable-models-1.1.0/swapper.egg-info/top_level.txt new/swapper-1.1.1/swapper.egg-info/top_level.txt --- old/django-swappable-models-1.1.0/swapper.egg-info/top_level.txt 1970-01-01 01:00:00.000000000 +0100 +++ new/swapper-1.1.1/swapper.egg-info/top_level.txt 2019-07-23 04:57:51.000000000 +0200 @@ -0,0 +1 @@ +swapper diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-swappable-models-1.1.0/tests/default_app/models.py new/swapper-1.1.1/tests/default_app/models.py --- old/django-swappable-models-1.1.0/tests/default_app/models.py 2017-05-11 04:00:30.000000000 +0200 +++ new/swapper-1.1.1/tests/default_app/models.py 2019-07-23 04:57:18.000000000 +0200 @@ -15,6 +15,8 @@ class Item(models.Model): - type = models.ForeignKey(swapper.get_model_name('default_app', "Type")) + type = models.ForeignKey( + swapper.get_model_name('default_app', "Type"), on_delete=models.CASCADE + ) name = models.CharField(max_length=255) description = models.TextField() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-swappable-models-1.1.0/tests/test_swapper.py new/swapper-1.1.1/tests/test_swapper.py --- old/django-swappable-models-1.1.0/tests/test_swapper.py 2017-05-11 04:00:30.000000000 +0200 +++ new/swapper-1.1.1/tests/test_swapper.py 2019-07-23 04:57:18.000000000 +0200 @@ -1,14 +1,10 @@ from django.test import TestCase -import sys import swapper from django.conf import settings from django.core.exceptions import ImproperlyConfigured -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest +import unittest try: from django.db import migrations # noqa diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-swappable-models-1.1.0/tox.ini new/swapper-1.1.1/tox.ini --- old/django-swappable-models-1.1.0/tox.ini 2017-05-11 04:00:30.000000000 +0200 +++ new/swapper-1.1.1/tox.ini 2019-07-23 04:57:18.000000000 +0200 @@ -1,21 +1,22 @@ [tox] envlist = - py26-django16-unittest2-{noswap,swap} - py{27,33}-django16-{noswap,swap} - py{27,33,34}-django17-{noswap,swap} - py{27,33,34,35}-django18-{noswap,swap} + py{27}-django16-{noswap,swap} + py{27,34}-django17-{noswap,swap} + py{27,34,35}-django18-{noswap,swap} py{27,34,35}-django19-{noswap,swap} py{27,34,35}-django110-{noswap,swap} - py{27,34,35,36}-django111-{noswap,swap} + py{27,34,35,36,37}-django111-{noswap,swap} + py{34,35,36,37}-django20-{noswap,swap} + py{35,36,37}-django21-{noswap,swap} + py{35,36,37}-django22-{noswap,swap} lint [tox:travis] -2.6 = py26 2.7 = py27 -3.3 = py33 3.4 = py34 3.5 = py35 -3.6 = py36, lint +3.6 = py36 +3.7 = py37, lint [testenv] commands = @@ -28,7 +29,9 @@ django19: django~=1.9.0 django110: django~=1.10.0 django111: django~=1.11.0 - unittest2: unittest2 + django20: django~=2.0.0 + django21: django~=2.1.0 + django22: django~=2.2.0 setenv = noswap: DJANGO_SETTINGS_MODULE=tests.settings swap: DJANGO_SETTINGS_MODULE=tests.swap_settings