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


Reply via email to