[Python-modules-commits] [django-model-utils] 01/05: New upstream version 3.0.0

Brian May bam at moszumanska.debian.org
Sun Jul 2 22:27:47 UTC 2017


This is an automated email from the git hooks/post-receive script.

bam pushed a commit to branch debian/master
in repository django-model-utils.

commit 9c39ae8052402c292fcf33bb659e3ec50d18457a
Author: Brian May <bam at debian.org>
Date:   Mon Jul 3 08:15:51 2017 +1000

    New upstream version 3.0.0
---
 .github/ISSUE_TEMPLATE.md                          |   14 +
 .github/PULL_REQUEST_TEMPLATE.md                   |   15 +
 .travis.yml                                        |   32 +-
 AUTHORS.rst                                        |    3 +
 CHANGES.rst                                        |   42 +
 README.rst                                         |    5 +-
 docs/managers.rst                                  |   61 +-
 docs/models.rst                                    |    8 +
 docs/setup.rst                                     |    4 +-
 model_utils/__init__.py                            |    2 +-
 model_utils/fields.py                              |   32 +-
 model_utils/managers.py                            |  116 +-
 model_utils/models.py                              |   39 +-
 model_utils/tests/tests.py                         | 1873 --------------------
 model_utils/tracker.py                             |   87 +-
 requirements.txt                                   |    2 +
 runtests.py                                        |   26 +-
 setup.py                                           |    9 +-
 {model_utils/tests => tests}/__init__.py           |    0
 {model_utils/tests => tests}/fields.py             |    0
 tests/managers.py                                  |   15 +
 {model_utils/tests => tests}/models.py             |  107 +-
 tests/test_choices.py                              |  261 +++
 .../tests => tests/test_fields}/__init__.py        |    0
 tests/test_fields/test_field_tracker.py            |  728 ++++++++
 tests/test_fields/test_monitor_field.py            |  120 ++
 tests/test_fields/test_split_field.py              |   78 +
 tests/test_fields/test_status_field.py             |   32 +
 .../tests => tests/test_managers}/__init__.py      |    0
 tests/test_managers/test_inheritance_manager.py    |  456 +++++
 tests/test_managers/test_query_manager.py          |   29 +
 tests/test_managers/test_softdelete_manager.py     |   28 +
 tests/test_managers/test_status_manager.py         |   23 +
 tests/test_miscellaneous.py                        |   29 +
 .../tests => tests/test_models}/__init__.py        |    0
 tests/test_models/test_softdeletable_model.py      |   52 +
 tests/test_models/test_status_model.py             |   70 +
 tests/test_models/test_timeframed_model.py         |   47 +
 tests/test_models/test_timestamped_model.py        |   25 +
 tox.ini                                            |   21 +-
 translations.py                                    |   12 +-
 41 files changed, 2394 insertions(+), 2109 deletions(-)

diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 0000000..f384dde
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,14 @@
+## Problem
+
+Explain the problem you encountered.
+
+## Environment
+
+- Django Model Utils version:
+- Django version: 
+- Python version: 
+- Other libraries used, if any:
+
+## Code examples
+
+Give code example that demonstrates the issue, or even better, write new tests that fails because of that issue.
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..63db703
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,15 @@
+## Problem
+
+Explain the problem you are fixing (add the link to the related issue(s), if any).
+
+## Solution
+
+Explain the solution that has been implemented, and what has been changed.
+
+## Commandments
+
+- [ ] Write PEP8 compliant code.
+- [ ] Cover it with tests.
+- [ ] Update `CHANGES.rst` file to describe the changes, and quote according issue with `GH-<issue_number>`.
+- [ ] Pay attention to backward compatibility, or if it breaks it, explain why.
+- [ ] Update documentation (if relevant).
diff --git a/.travis.yml b/.travis.yml
index 6ea0c92..5e01979 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,31 +3,22 @@ language: python
 python: 2.7
 
 env:
-  - TOXENV=py26-django14
-  - TOXENV=py26-django15
-  - TOXENV=py26-django16
-  - TOXENV=py27-django110
-  - TOXENV=py27-django14
-  - TOXENV=py27-django15
-  - TOXENV=py27-django15_nosouth
-  - TOXENV=py27-django16
-  - TOXENV=py27-django17
   - TOXENV=py27-django18
   - TOXENV=py27-django19
-  - TOXENV=py27-django_trunk
-  - TOXENV=py33-django15
-  - TOXENV=py33-django16
-  - TOXENV=py33-django17
+  - TOXENV=py27-django110
+  - TOXENV=py27-django111
   - TOXENV=py33-django18
-  - TOXENV=py34-django110
-  - TOXENV=py34-django17
   - TOXENV=py34-django18
   - TOXENV=py34-django19
-  - TOXENV=py34-django_trunk
-  - TOXENV=py35-django110
+  - TOXENV=py34-django110
+  - TOXENV=py34-django111
   - TOXENV=py35-django18
   - TOXENV=py35-django19
+  - TOXENV=py35-django110
+  - TOXENV=py35-django111
   - TOXENV=py35-django_trunk
+  - TOXENV=py36-django111
+  - TOXENV=py36-django_trunk
 
 install:
   - pip install --upgrade pip setuptools tox virtualenv coveralls
@@ -37,11 +28,8 @@ script:
 
 matrix:
   allow_failures:
-    - env: TOXENV=py27-django110
-    - env: TOXENV=py34-django110
-    - env: TOXENV=py35-django110
-    - env: TOXENV=py27-django_trunk
-    - env: TOXENV=py34-django_trunk
     - env: TOXENV=py35-django_trunk
+    - env: TOXENV=py36-django111
+    - env: TOXENV=py36-django_trunk
 
 after_success: coveralls
diff --git a/AUTHORS.rst b/AUTHORS.rst
index 668453e..4dd3044 100644
--- a/AUTHORS.rst
+++ b/AUTHORS.rst
@@ -1,6 +1,7 @@
 ad-m <github.com/ad-m>
 Alejandro Varas <alej0varas at gmail.com>
 Alex Orange <crazycasta at gmail.com>
+Alexey Evseev <myhappydo at gmail.com>
 Andy Freeland <andy at andyfreeland.net>
 Artis Avotins <artis.avotins at gmail.com>
 Bram Boogaard <b.boogaard at auto-interactive.nl>
@@ -30,6 +31,7 @@ Paul McLanahan <paul at mclanahan.net>
 Philipp Steinhardt <steinhardt at myvision.de>
 Rinat Shigapov <rinatshigapov at gmail.com>
 Rodney Folz <rodney at rodneyfolz.com>
+Romain Garrigues <github.com/romgar>
 rsenkbeil <github.com/rsenkbeil>
 Ryan Kaskel <dev at ryankaskel.com>
 Simon Meers <simon at simonmeers.com>
@@ -39,3 +41,4 @@ Travis Swicegood <travis at domain51.com>
 Trey Hunner <trey at treyhunner.com>
 Karl Wan Nan Wo <karl.wnw at gmail.com>
 zyegfryed
+Radosław Jan Ganczarek <radoslaw at ganczarek.in>
diff --git a/CHANGES.rst b/CHANGES.rst
index fa0bd0e..efa85a9 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,6 +1,48 @@
 CHANGES
 =======
 
+3.0.0 (2017.04.13)
+------------------
+
+* Drop support for Python 2.6.
+* Drop support for Django 1.4, 1.5, 1.6, 1.7.
+* Exclude tests from the distribution, fixes GH-258.
+* Add support for Django 1.11 GH-269
+
+
+2.6.1 (2017.01.11)
+------------------
+
+* Fix infinite recursion with multiple `MonitorField` and `defer()` or `only()`
+  on Django 1.10+. Thanks Romain Garrigues. Merge of GH-242, fixes GH-241.
+
+* Fix `InheritanceManager` and `SoftDeletableManager` to respect
+  `self._queryset_class` instead of hardcoding the queryset class. Merge of
+  GH-250, fixes GH-249.
+
+* Add mixins for `SoftDeletableQuerySet` and `SoftDeletableManager`, as stated
+  in the the documentation.
+
+* Fix `SoftDeletableModel.delete()` to use the correct database connection.
+  Merge of GH-239.
+
+* Added boolean keyword argument `soft` to `SoftDeletableModel.delete()` that
+  revert to default behavior when set to `False`. Merge of GH-240.
+
+* Enforced default manager in `StatusModel` to avoid manager order issues when
+  using abstract models that redefine `objects` manager. Merge of GH-253, fixes
+  GH-251.
+
+
+2.6 (2016.09.19)
+----------------
+
+* Added `SoftDeletableModel` abstract class, its manageer
+  `SoftDeletableManager` and queryset `SoftDeletableQuerySet`.
+
+* Fix issue with field tracker and deferred FileField for Django 1.10.
+
+
 2.5.2 (2016.08.09)
 ------------------
 
diff --git a/README.rst b/README.rst
index 001712f..9ce6788 100644
--- a/README.rst
+++ b/README.rst
@@ -11,9 +11,8 @@ django-model-utils
 
 Django model mixins and utilities.
 
-``django-model-utils`` supports `Django`_ 1.4 through 1.9 (latest bugfix
-release in each series only) on Python 2.6 (through Django 1.6 only), 2.7, 3.3
-(through Django 1.8 only), 3.4 and 3.5.
+``django-model-utils`` supports `Django`_ 1.8 through 1.10 (latest bugfix
+release in each series only) on Python 2.7, 3.3 (Django 1.8 only), 3.4 and 3.5.
 
 .. _Django: http://www.djangoproject.com/
 
diff --git a/docs/managers.rst b/docs/managers.rst
index 2669a1c..43aa030 100644
--- a/docs/managers.rst
+++ b/docs/managers.rst
@@ -84,14 +84,7 @@ If you don't explicitly call ``select_subclasses()`` or ``get_subclass()``,
 an ``InheritanceManager`` behaves identically to a normal ``Manager``; so
 it's safe to use as your default manager for the model.
 
-.. note::
-
-    Due to `Django bug #16572`_, on Django versions prior to 1.6
-    ``InheritanceManager`` only supports a single level of model inheritance;
-    it won't work for grandchild models.
-
 .. _contributed by Jeff Elmore: http://jeffelmore.org/2010/11/11/automatic-downcasting-of-inherited-models-in-django/
-.. _Django bug #16572: https://code.djangoproject.com/ticket/16572
 
 
 .. _QueryManager:
@@ -124,57 +117,19 @@ set the ordering of the ``QuerySet`` returned by the ``QueryManager``
 by chaining a call to ``.order_by()`` on the ``QueryManager`` (this is
 not required).
 
+SoftDeletableManager
+--------------------
 
-PassThroughManager
-------------------
-
-`PassThroughManager` was removed in django-model-utils 2.4. Use Django's
-built-in `QuerySet.as_manager()` and/or `Manager.from_queryset()` utilities
-instead.
+Returns only model instances that have the ``is_removed`` field set
+to False. Uses ``SoftDeletableQuerySet``, which ensures model instances
+won't be removed in bulk, but they will be marked as removed instead.
 
 Mixins
 ------
 
 Each of the above manager classes has a corresponding mixin that can be used to
-add functionality to any manager. For example, to create a GeoDjango
-``GeoManager`` that includes "pass through" functionality, you can write the
-following code:
-
-.. code-block:: python
-
-    from django.contrib.gis.db import models
-    from django.contrib.gis.db.models.query import GeoQuerySet
-
-    from model_utils.managers import PassThroughManagerMixin
-
-    class PassThroughGeoManager(PassThroughManagerMixin, models.GeoManager):
-        pass
-
-    class LocationQuerySet(GeoQuerySet):
-        def within_boundary(self, geom):
-            return self.filter(point__within=geom)
-
-        def public(self):
-            return self.filter(public=True)
-
-    class Location(models.Model):
-        point  = models.PointField()
-        public = models.BooleanField(default=True)
-        objects = PassThroughGeoManager.for_queryset_class(LocationQuerySet)()
-
-    Location.objects.public()
-    Location.objects.within_boundary(geom=geom)
-    Location.objects.within_boundary(geom=geom).public()
-
-
-Now you have a "pass through manager" that can also take advantage of
-GeoDjango's spatial lookups. You can similarly add additional functionality to
-any manager by composing that manager with ``InheritanceManagerMixin`` or
-``QueryManagerMixin``.
+add functionality to any manager.
 
-(Note that any manager class using ``InheritanceManagerMixin`` must return a
+Note that any manager class using ``InheritanceManagerMixin`` must return a
 ``QuerySet`` class using ``InheritanceQuerySetMixin`` from its ``get_queryset``
-method. This means that if composing ``InheritanceManagerMixin`` and
-``PassThroughManagerMixin``, the ``QuerySet`` class passed to
-``PassThroughManager.for_queryset_class`` must inherit
-``InheritanceQuerySetMixin``.)
+method.
diff --git a/docs/models.rst b/docs/models.rst
index 7a05c79..51bde8f 100644
--- a/docs/models.rst
+++ b/docs/models.rst
@@ -47,3 +47,11 @@ returns objects with that status only:
 
     # this query will only return published articles:
     Article.published.all()
+
+
+SoftDeletableModel
+------------------
+
+This abstract base class just provides field ``is_removed`` which is
+set to True instead of removing the instance. Entities returned in
+default manager are limited to not-deleted instances.
diff --git a/docs/setup.rst b/docs/setup.rst
index 5621649..db2a34e 100644
--- a/docs/setup.rst
+++ b/docs/setup.rst
@@ -17,7 +17,7 @@ modify your ``INSTALLED_APPS`` setting.
 Dependencies
 ============
 
-``django-model-utils`` supports `Django`_ 1.4.2 and later on Python 2.6, 2.7,
-3.2, and 3.3.
+``django-model-utils`` supports `Django`_ 1.8 through 1.10 (latest bugfix
+release in each series only) on Python 2.7, 3.3 (Django 1.8 only), 3.4 and 3.5.
 
 .. _Django: http://www.djangoproject.com/
diff --git a/model_utils/__init__.py b/model_utils/__init__.py
index e42aa57..54c239c 100644
--- a/model_utils/__init__.py
+++ b/model_utils/__init__.py
@@ -1,4 +1,4 @@
 from .choices import Choices
 from .tracker import FieldTracker, ModelTracker
 
-__version__ = '2.5.2'
+__version__ = '3.0.0'
diff --git a/model_utils/fields.py b/model_utils/fields.py
index 805c707..f308706 100644
--- a/model_utils/fields.py
+++ b/model_utils/fields.py
@@ -110,6 +110,9 @@ class MonitorField(models.DateTimeField):
         return getattr(instance, self.monitor)
 
     def _save_initial(self, sender, instance, **kwargs):
+        if django.VERSION >= (1, 10) and self.monitor in instance.get_deferred_fields():
+            # Fix related to issue #241 to avoid recursive error on double monitor fields
+            return
         setattr(instance, self.monitor_attname,
                 self.get_monitored_value(instance))
 
@@ -225,7 +228,7 @@ class SplitField(models.TextField):
         return value.content
 
     def value_to_string(self, obj):
-        value = self._get_val_from_obj(obj)
+        value = self.value_from_object(obj)
         return value.content
 
     def get_prep_value(self, value):
@@ -238,30 +241,3 @@ class SplitField(models.TextField):
         name, path, args, kwargs = super(SplitField, self).deconstruct()
         kwargs['no_excerpt_field'] = True
         return name, path, args, kwargs
-
-# allow South to handle these fields smoothly
-try:
-    from south.modelsinspector import add_introspection_rules
-    # For a normal MarkupField, the add_excerpt_field attribute is
-    # always True, which means no_excerpt_field arg will always be
-    # True in a frozen MarkupField, which is what we want.
-    add_introspection_rules(rules=[
-        (
-            (SplitField,),
-            [],
-            {'no_excerpt_field': ('add_excerpt_field', {})}
-        ),
-        (
-            (MonitorField,),
-            [],
-            {'monitor': ('monitor', {})}
-        ),
-        (
-            (StatusField,),
-            [],
-            {'no_check_for_status': ('check_for_status', {})}
-        ),
-    ], patterns=['model_utils\.fields\.'])
-except ImportError:
-    pass
-
diff --git a/model_utils/managers.py b/model_utils/managers.py
index 4f06d2e..9dc68a2 100644
--- a/model_utils/managers.py
+++ b/model_utils/managers.py
@@ -3,17 +3,54 @@ import django
 from django.db import models
 from django.db.models.fields.related import OneToOneField, OneToOneRel
 from django.db.models.query import QuerySet
+try:
+    from django.db.models.query import BaseIterable, ModelIterable
+except ImportError:
+    # Django 1.8 does not have iterable classes
+    BaseIterable = object
 from django.core.exceptions import ObjectDoesNotExist
 
-try:
-    from django.db.models.constants import LOOKUP_SEP
-    from django.utils.six import string_types
-except ImportError:  # Django < 1.5
-    from django.db.models.sql.constants import LOOKUP_SEP
-    string_types = (basestring,)
+from django.db.models.constants import LOOKUP_SEP
+from django.utils.six import string_types
+
+
+class InheritanceIterable(BaseIterable):
+    def __iter__(self):
+        queryset = self.queryset
+        iter = ModelIterable(queryset)
+        if getattr(queryset, 'subclasses', False):
+            extras = tuple(queryset.query.extra.keys())
+            # sort the subclass names longest first,
+            # so with 'a' and 'a__b' it goes as deep as possible
+            subclasses = sorted(queryset.subclasses, key=len, reverse=True)
+            for obj in iter:
+                sub_obj = None
+                for s in subclasses:
+                    sub_obj = queryset._get_sub_obj_recurse(obj, s)
+                    if sub_obj:
+                        break
+                if not sub_obj:
+                    sub_obj = obj
+
+                if getattr(queryset, '_annotated', False):
+                    for k in queryset._annotated:
+                        setattr(sub_obj, k, getattr(obj, k))
+
+                for k in extras:
+                    setattr(sub_obj, k, getattr(obj, k))
+
+                yield sub_obj
+        else:
+            for obj in iter:
+                yield obj
 
 
 class InheritanceQuerySetMixin(object):
+    def __init__(self, *args, **kwargs):
+        super(InheritanceQuerySetMixin, self).__init__(*args, **kwargs)
+        if django.VERSION > (1, 8):
+            self._iterable_class = InheritanceIterable
+
     def select_subclasses(self, *subclasses):
         levels = self._get_maximum_depth()
         calculated_subclasses = self._get_subclasses_recurse(
@@ -68,6 +105,7 @@ class InheritanceQuerySetMixin(object):
         return qset
 
     def iterator(self):
+        # Maintained for Django 1.8 compatability
         iter = super(InheritanceQuerySetMixin, self).iterator()
         if getattr(self, 'subclasses', False):
             extras = tuple(self.query.extra.keys())
@@ -143,10 +181,10 @@ class InheritanceQuerySetMixin(object):
         if levels:
             levels -= 1
         while parent_link is not None:
-            if django.VERSION < (1, 8):
-                related = parent_link.related
-            else:
+            if django.VERSION < (1, 9):
                 related = parent_link.rel
+            else:
+                related = parent_link.remote_field
             ancestry.insert(0, related.get_accessor_name())
             if levels or levels is None:
                 if django.VERSION < (1, 8):
@@ -192,13 +230,16 @@ class InheritanceQuerySetMixin(object):
         return levels
 
 
+class InheritanceQuerySet(InheritanceQuerySetMixin, QuerySet):
+    pass
+
+
 class InheritanceManagerMixin(object):
     use_for_related_fields = True
+    _queryset_class = InheritanceQuerySet
 
     def get_queryset(self):
-        return InheritanceQuerySet(self.model)
-
-    get_query_set = get_queryset
+        return self._queryset_class(self.model)
 
     def select_subclasses(self, *subclasses):
         return self.get_queryset().select_subclasses(*subclasses)
@@ -207,10 +248,6 @@ class InheritanceManagerMixin(object):
         return self.get_queryset().get_subclass(*args, **kwargs)
 
 
-class InheritanceQuerySet(InheritanceQuerySetMixin, QuerySet):
-    pass
-
-
 class InheritanceManager(InheritanceManagerMixin, models.Manager):
     pass
 
@@ -231,16 +268,51 @@ class QueryManagerMixin(object):
         return self
 
     def get_queryset(self):
-        try:
-            qs = super(QueryManagerMixin, self).get_queryset().filter(self._q)
-        except AttributeError:
-            qs = super(QueryManagerMixin, self).get_query_set().filter(self._q)
+        qs = super(QueryManagerMixin, self).get_queryset().filter(self._q)
         if self._order_by is not None:
             return qs.order_by(*self._order_by)
         return qs
 
-    get_query_set = get_queryset
-
 
 class QueryManager(QueryManagerMixin, models.Manager):
     pass
+
+
+class SoftDeletableQuerySetMixin(object):
+    """
+    QuerySet for SoftDeletableModel. Instead of removing instance sets
+    its ``is_removed`` field to True.
+    """
+
+    def delete(self):
+        """
+        Soft delete objects from queryset (set their ``is_removed``
+        field to True)
+        """
+        self.update(is_removed=True)
+
+
+class SoftDeletableQuerySet(SoftDeletableQuerySetMixin, QuerySet):
+    pass
+
+
+class SoftDeletableManagerMixin(object):
+    """
+    Manager that limits the queryset by default to show only not removed
+    instances of model.
+    """
+    _queryset_class = SoftDeletableQuerySet
+
+    def get_queryset(self):
+        """
+        Return queryset limited to not removed entries.
+        """
+        kwargs = {'model': self.model, 'using': self._db}
+        if hasattr(self, '_hints'):
+            kwargs['hints'] = self._hints
+
+        return self._queryset_class(**kwargs).filter(is_removed=False)
+
+
+class SoftDeletableManager(SoftDeletableManagerMixin, models.Manager):
+    pass
diff --git a/model_utils/models.py b/model_utils/models.py
index 3db4073..c679fc6 100644
--- a/model_utils/models.py
+++ b/model_utils/models.py
@@ -1,16 +1,16 @@
 from __future__ import unicode_literals
 
 import django
+from django.core.exceptions import ImproperlyConfigured
 from django.db import models
 from django.utils.translation import ugettext_lazy as _
-from django.core.exceptions import ImproperlyConfigured
 if django.VERSION >= (1, 9, 0):
     from django.db.models.functions import Now
     now = Now()
 else:
     from django.utils.timezone import now
 
-from model_utils.managers import QueryManager
+from model_utils.managers import QueryManager, SoftDeletableManager
 from model_utils.fields import AutoCreatedField, AutoLastModifiedField, \
     StatusField, MonitorField
 
@@ -64,6 +64,11 @@ def add_status_query_managers(sender, **kwargs):
     """
     if not issubclass(sender, StatusModel):
         return
+
+    if django.VERSION >= (1, 10):
+        # First, get current manager name...
+        default_manager = sender._meta.default_manager
+
     for value, display in getattr(sender, 'STATUS', ()):
         if _field_exists(sender, value):
             raise ImproperlyConfigured(
@@ -73,6 +78,10 @@ def add_status_query_managers(sender, **kwargs):
             )
         sender.add_to_class(value, QueryManager(status=value))
 
+    if django.VERSION >= (1, 10):
+        # ...then, put it back, as add_to_class is modifying the default manager!
+        sender._meta.default_manager_name = default_manager.name
+
 
 def add_timeframed_query_manager(sender, **kwargs):
     """
@@ -99,3 +108,29 @@ models.signals.class_prepared.connect(add_timeframed_query_manager)
 
 def _field_exists(model_class, field_name):
     return field_name in [f.attname for f in model_class._meta.local_fields]
+
+
+class SoftDeletableModel(models.Model):
+    """
+    An abstract base class model with a ``is_removed`` field that
+    marks entries that are not going to be used anymore, but are
+    kept in db for any reason.
+    Default manager returns only not-removed entries.
+    """
+    is_removed = models.BooleanField(default=False)
+
+    class Meta:
+        abstract = True
+
+    objects = SoftDeletableManager()
+
+    def delete(self, using=None, soft=True, *args, **kwargs):
+        """
+        Soft delete object (set its ``is_removed`` field to True).
+        Actually delete object if setting ``soft`` to False.
+        """
+        if soft:
+            self.is_removed = True
+            self.save(using=using)
+        else:
+            return super(SoftDeletableModel, self).delete(using=using, *args, **kwargs)
diff --git a/model_utils/tests/tests.py b/model_utils/tests/tests.py
deleted file mode 100644
index 14b6329..0000000
--- a/model_utils/tests/tests.py
+++ /dev/null
@@ -1,1873 +0,0 @@
-from __future__ import unicode_literals
-
-from datetime import datetime, timedelta
-try:
-    from unittest import skipUnless
-except ImportError: # Python 2.6
-    from django.utils.unittest import skipUnless
-
-import django
-from django.db import models
-from django.db.models.fields import FieldDoesNotExist
-from django.utils.six import text_type
-from django.core.exceptions import ImproperlyConfigured, FieldError
-from django.core.management import call_command
-from django.test import TestCase
-
-from model_utils import Choices, FieldTracker
-from model_utils.fields import get_excerpt, MonitorField, StatusField
-from model_utils.managers import QueryManager
-from model_utils.models import StatusModel, TimeFramedModel
-from model_utils.tests.models import (
-    InheritanceManagerTestRelated, InheritanceManagerTestGrandChild1,
-    InheritanceManagerTestGrandChild1_2,
-    InheritanceManagerTestParent, InheritanceManagerTestChild1,
-    InheritanceManagerTestChild2, TimeStamp, Post, Article, Status,
-    StatusPlainTuple, TimeFrame, Monitored, MonitorWhen, MonitorWhenEmpty, StatusManagerAdded,
-    TimeFrameManagerAdded, SplitFieldAbstractParent,
-    ModelTracked, ModelTrackedFK, ModelTrackedNotDefault, ModelTrackedMultiple, InheritedModelTracked,
-    Tracked, TrackedFK, InheritedTrackedFK, TrackedNotDefault, TrackedNonFieldAttr, TrackedMultiple,
-    InheritedTracked, StatusFieldDefaultFilled, StatusFieldDefaultNotFilled,
-    InheritanceManagerTestChild3, StatusFieldChoicesName)
-
-
-class MigrationsTests(TestCase):
-    @skipUnless(django.VERSION >= (1, 7, 0), "test only applies to Django 1.7+")
-    def test_makemigrations(self):
-        call_command('makemigrations', dry_run=True)
-
-
-class GetExcerptTests(TestCase):
-    def test_split(self):
-        e = get_excerpt("some content\n\n<!-- split -->\n\nsome more")
-        self.assertEqual(e, 'some content\n')
-
-
-    def test_auto_split(self):
-        e = get_excerpt("para one\n\npara two\n\npara three")
-        self.assertEqual(e, 'para one\n\npara two')
-
-
-    def test_middle_of_para(self):
-        e = get_excerpt("some text\n<!-- split -->\nmore text")
-        self.assertEqual(e, 'some text')
-
-
-    def test_middle_of_line(self):
-        e = get_excerpt("some text <!-- split --> more text")
-        self.assertEqual(e, "some text <!-- split --> more text")
-
-
-
-class SplitFieldTests(TestCase):
-    full_text = 'summary\n\n<!-- split -->\n\nmore'
-    excerpt = 'summary\n'
-
-
-    def setUp(self):
-        self.post = Article.objects.create(
-            title='example post', body=self.full_text)
-
-
-    def test_unicode_content(self):
-        self.assertEqual(text_type(self.post.body), self.full_text)
-
-
-    def test_excerpt(self):
-        self.assertEqual(self.post.body.excerpt, self.excerpt)
-
-
-    def test_content(self):
-        self.assertEqual(self.post.body.content, self.full_text)
-
-
-    def test_has_more(self):
-        self.assertTrue(self.post.body.has_more)
-
-
-    def test_not_has_more(self):
-        post = Article.objects.create(title='example 2',
-                                      body='some text\n\nsome more\n')
-        self.assertFalse(post.body.has_more)
-
-
-    def test_load_back(self):
-        post = Article.objects.get(pk=self.post.pk)
-        self.assertEqual(post.body.content, self.post.body.content)
-        self.assertEqual(post.body.excerpt, self.post.body.excerpt)
-
-
-    def test_assign_to_body(self):
-        new_text = 'different\n\n<!-- split -->\n\nother'
-        self.post.body = new_text
-        self.post.save()
-        self.assertEqual(text_type(self.post.body), new_text)
-
-
-    def test_assign_to_content(self):
-        new_text = 'different\n\n<!-- split -->\n\nother'
-        self.post.body.content = new_text
-        self.post.save()
-        self.assertEqual(text_type(self.post.body), new_text)
-
-
-    def test_assign_to_excerpt(self):
-        with self.assertRaises(AttributeError):
-            self.post.body.excerpt = 'this should fail'
-
-
-    def test_access_via_class(self):
-        with self.assertRaises(AttributeError):
-            Article.body
-
-
-    def test_none(self):
-        a = Article(title='Some Title', body=None)
-        self.assertEqual(a.body, None)
-
-
-    def test_assign_splittext(self):
-        a = Article(title='Some Title')
-        a.body = self.post.body
-        self.assertEqual(a.body.excerpt, 'summary\n')
-
-
-    def test_value_to_string(self):
-        f = self.post._meta.get_field('body')
-        self.assertEqual(f.value_to_string(self.post), self.full_text)
-
-
-    def test_abstract_inheritance(self):
-        class Child(SplitFieldAbstractParent):
-            pass
-
-        self.assertEqual(
-            [f.name for f in Child._meta.fields],
-            ["id", "content", "_content_excerpt"])
-
-
-
-class MonitorFieldTests(TestCase):
-    def setUp(self):
-        self.instance = Monitored(name='Charlie')
-        self.created = self.instance.name_changed
-
-
-    def test_save_no_change(self):
-        self.instance.save()
-        self.assertEqual(self.instance.name_changed, self.created)
-
-
-    def test_save_changed(self):
-        self.instance.name = 'Maria'
-        self.instance.save()
-        self.assertTrue(self.instance.name_changed > self.created)
-
-
-    def test_double_save(self):
-        self.instance.name = 'Jose'
-        self.instance.save()
-        changed = self.instance.name_changed
-        self.instance.save()
-        self.assertEqual(self.instance.name_changed, changed)
-
-
-    def test_no_monitor_arg(self):
-        with self.assertRaises(TypeError):
-            MonitorField()
-
-
-
-class MonitorWhenFieldTests(TestCase):
-    """
-    Will record changes only when name is 'Jose' or 'Maria'
-    """
-    def setUp(self):
-        self.instance = MonitorWhen(name='Charlie')
-        self.created = self.instance.name_changed
-
-
-    def test_save_no_change(self):
-        self.instance.save()
-        self.assertEqual(self.instance.name_changed, self.created)
-
-
-    def test_save_changed_to_Jose(self):
-        self.instance.name = 'Jose'
-        self.instance.save()
-        self.assertTrue(self.instance.name_changed > self.created)
-
-
-    def test_save_changed_to_Maria(self):
-        self.instance.name = 'Maria'
-        self.instance.save()
-        self.assertTrue(self.instance.name_changed > self.created)
-
-
-    def test_save_changed_to_Pedro(self):
-        self.instance.name = 'Pedro'
-        self.instance.save()
-        self.assertEqual(self.instance.name_changed, self.created)
-
-
-    def test_double_save(self):
-        self.instance.name = 'Jose'
-        self.instance.save()
-        changed = self.instance.name_changed
-        self.instance.save()
-        self.assertEqual(self.instance.name_changed, changed)
-
-
-
-class MonitorWhenEmptyFieldTests(TestCase):
-    """
-    Monitor should never be updated id when is an empty list.
-    """
-    def setUp(self):
-        self.instance = MonitorWhenEmpty(name='Charlie')
-        self.created = self.instance.name_changed
-
-
-    def test_save_no_change(self):
-        self.instance.save()
-        self.assertEqual(self.instance.name_changed, self.created)
-
-
-    def test_save_changed_to_Jose(self):
-        self.instance.name = 'Jose'
-        self.instance.save()
-        self.assertEqual(self.instance.name_changed, self.created)
-
-
-    def test_save_changed_to_Maria(self):
-        self.instance.name = 'Maria'
-        self.instance.save()
-        self.assertEqual(self.instance.name_changed, self.created)
-
-
-
-class StatusFieldTests(TestCase):
-
-    def test_status_with_default_filled(self):
-        instance = StatusFieldDefaultFilled()
-        self.assertEqual(instance.status, instance.STATUS.yes)
-
-    def test_status_with_default_not_filled(self):
-        instance = StatusFieldDefaultNotFilled()
-        self.assertEqual(instance.status, instance.STATUS.no)
-
-    def test_no_check_for_status(self):
-        field = StatusField(no_check_for_status=True)
-        # this model has no STATUS attribute, so checking for it would error
-        field.prepare_class(Article)
-
-    def test_get_status_display(self):
-        instance = StatusFieldDefaultFilled()
-        self.assertEqual(instance.get_status_display(), "Yes")
-
-    def test_choices_name(self):
-        StatusFieldChoicesName()
-
-
-class ChoicesTests(TestCase):
-    def setUp(self):
-        self.STATUS = Choices('DRAFT', 'PUBLISHED')
-
-
-    def test_getattr(self):
-        self.assertEqual(self.STATUS.DRAFT, 'DRAFT')
-
-
-    def test_indexing(self):
-        self.assertEqual(self.STATUS['PUBLISHED'], 'PUBLISHED')
-
-
-    def test_iteration(self):
-        self.assertEqual(tuple(self.STATUS), (('DRAFT', 'DRAFT'), ('PUBLISHED', 'PUBLISHED')))
-
-
-    def test_len(self):
-        self.assertEqual(len(self.STATUS), 2)
-
-
-    def test_repr(self):
-        self.assertEqual(repr(self.STATUS), "Choices" + repr((
-            ('DRAFT', 'DRAFT', 'DRAFT'),
-            ('PUBLISHED', 'PUBLISHED', 'PUBLISHED'),
-        )))
-
-
-    def test_wrong_length_tuple(self):
-        with self.assertRaises(ValueError):
-            Choices(('a',))
-
-
-    def test_contains_value(self):
-        self.assertTrue('PUBLISHED' in self.STATUS)
-        self.assertTrue('DRAFT' in self.STATUS)
-
-
-    def test_doesnt_contain_value(self):
-        self.assertFalse('UNPUBLISHED' in self.STATUS)
-
-    def test_deepcopy(self):
-        import copy
-        self.assertEqual(list(self.STATUS),
-                         list(copy.deepcopy(self.STATUS)))
-
-
-    def test_equality(self):
-        self.assertEqual(self.STATUS, Choices('DRAFT', 'PUBLISHED'))
-
... 4246 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/django-model-utils.git



More information about the Python-modules-commits mailing list