[Python-modules-commits] [django-modeltranslation] 02/05: Imported Upstream version 0.11

Raphaël Hertzog hertzog at moszumanska.debian.org
Mon Jun 27 21:05:59 UTC 2016


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

hertzog pushed a commit to branch master
in repository django-modeltranslation.

commit 407d5ec65e7f9d785bbb7c506958253388dd1797
Author: Sophie Brun <sophie at freexian.com>
Date:   Mon Jun 27 11:17:31 2016 +0200

    Imported Upstream version 0.11
---
 .gitignore                                         |   31 +
 .travis.yml                                        |  163 ++
 AUTHORS.rst                                        |    2 +
 CHANGELOG.txt                                      |   39 +
 MANIFEST.in                                        |    3 +
 PKG-INFO                                           |   39 +-
 docs/modeltranslation/_static/.gitignore           |    2 +
 modeltranslation/__init__.py                       |    4 +-
 modeltranslation/fields.py                         |   26 +-
 modeltranslation/forms.py                          |    9 +-
 .../management/commands/sync_translation_fields.py |    8 +-
 .../commands/update_translation_fields.py          |    6 +-
 modeltranslation/manager.py                        |  208 +-
 modeltranslation/tests/__init__.py                 |    2 +
 modeltranslation/tests/fixtures/fixture.json       |   10 +
 modeltranslation/tests/models.py                   |  315 ++
 modeltranslation/tests/project_translation.py      |    8 +
 modeltranslation/tests/settings.py                 |   24 +
 modeltranslation/tests/test_app/__init__.py        |    0
 modeltranslation/tests/test_app/models.py          |   14 +
 modeltranslation/tests/test_app/translation.py     |    8 +
 modeltranslation/tests/test_settings.py            |    6 +
 modeltranslation/tests/tests.py                    | 3021 ++++++++++++++++++++
 modeltranslation/tests/translation.py              |  216 ++
 modeltranslation/tests/urls.py                     |   14 +
 modeltranslation/translator.py                     |   37 +-
 runtests.py                                        |   59 +
 tox.ini                                            |  161 ++
 travis.py                                          |    9 +
 29 files changed, 4343 insertions(+), 101 deletions(-)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ce17a1e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,31 @@
+*.py[cod]
+
+# C extensions
+*.so
+
+# Packages
+*.egg
+*.egg-info
+dist
+build
+eggs
+parts
+bin
+var
+sdist
+develop-eggs
+.installed.cfg
+lib
+lib64
+MANIFEST
+
+# Installer logs
+pip-log.txt
+
+# Unit test / coverage reports
+.coverage
+.tox
+nosetests.xml
+
+# Sphinx
+_build
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..af5d6ef
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,163 @@
+language: python
+python:
+  - "2.6"
+  - "2.7"
+  - "3.2"
+  - "3.3"
+  - "3.4"
+  - "3.5"
+env:
+  - DJANGO=1.4 DB=sqlite
+  - DJANGO=1.4 DB=postgres
+  - DJANGO=1.4 DB=mysql
+  - DJANGO=1.5 DB=sqlite
+  - DJANGO=1.5 DB=postgres
+  - DJANGO=1.5 DB=mysql
+  - DJANGO=1.6 DB=sqlite
+  - DJANGO=1.6 DB=postgres
+  - DJANGO=1.6 DB=mysql
+  - DJANGO=1.7 DB=sqlite
+  - DJANGO=1.7 DB=postgres
+  - DJANGO=1.7 DB=mysql
+  - DJANGO=1.8 DB=sqlite
+  - DJANGO=1.8 DB=postgres
+  - DJANGO=1.8 DB=mysql
+  - DJANGO=1.9 DB=sqlite
+  - DJANGO=1.9 DB=postgres
+  - DJANGO=1.9 DB=mysql
+matrix:
+  exclude:
+    - python: "3.2"
+      env: DJANGO=1.4 DB=sqlite
+    - python: "3.2"
+      env: DJANGO=1.4 DB=postgres
+    - python: "3.2"
+      env: DJANGO=1.4 DB=mysql
+    - python: "3.3"
+      env: DJANGO=1.4 DB=sqlite
+    - python: "3.3"
+      env: DJANGO=1.4 DB=postgres
+    - python: "3.3"
+      env: DJANGO=1.4 DB=mysql
+    - python: "3.4"
+      env: DJANGO=1.4 DB=sqlite
+    - python: "3.4"
+      env: DJANGO=1.4 DB=postgres
+    - python: "3.4"
+      env: DJANGO=1.4 DB=mysql
+    - python: "3.5"
+      env: DJANGO=1.4 DB=sqlite
+    - python: "3.5"
+      env: DJANGO=1.4 DB=postgres
+    - python: "3.5"
+      env: DJANGO=1.4 DB=mysql
+
+    - python: "2.6"
+      env: DJANGO=1.7 DB=sqlite
+    - python: "2.6"
+      env: DJANGO=1.7 DB=postgres
+    - python: "2.6"
+      env: DJANGO=1.7 DB=mysql
+    - python: "2.6"
+      env: DJANGO=1.8 DB=sqlite
+    - python: "2.6"
+      env: DJANGO=1.8 DB=postgres
+    - python: "2.6"
+      env: DJANGO=1.8 DB=mysql
+    - python: "2.6"
+      env: DJANGO=1.9 DB=sqlite
+    - python: "2.6"
+      env: DJANGO=1.9 DB=postgres
+    - python: "2.6"
+      env: DJANGO=1.9 DB=mysql
+    - python: "3.2"
+      env: DJANGO=1.9 DB=sqlite
+    - python: "3.2"
+      env: DJANGO=1.9 DB=postgres
+    - python: "3.2"
+      env: DJANGO=1.9 DB=mysql
+    - python: "3.3"
+      env: DJANGO=1.9 DB=sqlite
+    - python: "3.3"
+      env: DJANGO=1.9 DB=postgres
+    - python: "3.3"
+      env: DJANGO=1.9 DB=mysql
+    - python: "3.5"
+      env: DJANGO=1.5 DB=sqlite
+    - python: "3.5"
+      env: DJANGO=1.5 DB=postgres
+    - python: "3.5"
+      env: DJANGO=1.5 DB=mysql
+    - python: "3.5"
+      env: DJANGO=1.6 DB=sqlite
+    - python: "3.5"
+      env: DJANGO=1.6 DB=postgres
+    - python: "3.5"
+      env: DJANGO=1.6 DB=mysql
+    - python: "3.5"
+      env: DJANGO=1.7 DB=sqlite
+    - python: "3.5"
+      env: DJANGO=1.7 DB=postgres
+    - python: "3.5"
+      env: DJANGO=1.7 DB=mysql
+
+    - python: "3.2"
+      env: DJANGO=1.5 DB=mysql
+    - python: "3.3"
+      env: DJANGO=1.5 DB=mysql
+    - python: "3.4"
+      env: DJANGO=1.5 DB=mysql
+    - python: "3.5"
+      env: DJANGO=1.5 DB=mysql
+    - python: "3.2"
+      env: DJANGO=1.6 DB=mysql
+    - python: "3.3"
+      env: DJANGO=1.6 DB=mysql
+    - python: "3.4"
+      env: DJANGO=1.6 DB=mysql
+    - python: "3.5"
+      env: DJANGO=1.6 DB=mysql
+    - python: "3.2"
+      env: DJANGO=1.7 DB=mysql
+    - python: "3.3"
+      env: DJANGO=1.7 DB=mysql
+    - python: "3.4"
+      env: DJANGO=1.7 DB=mysql
+    - python: "3.5"
+      env: DJANGO=1.7 DB=mysql
+    - python: "3.2"
+      env: DJANGO=1.8 DB=mysql
+    - python: "3.3"
+      env: DJANGO=1.8 DB=mysql
+    - python: "3.4"
+      env: DJANGO=1.8 DB=mysql
+    - python: "3.5"
+      env: DJANGO=1.8 DB=mysql
+    - python: "3.2"
+      env: DJANGO=1.9 DB=mysql
+    - python: "3.3"
+      env: DJANGO=1.9 DB=mysql
+    - python: "3.4"
+      env: DJANGO=1.9 DB=mysql
+    - python: "3.5"
+      env: DJANGO=1.9 DB=mysql
+before_install:
+  - pip install -q flake8 --use-mirrors
+  - PYFLAKES_NODOCTEST=1 flake8 modeltranslation
+before_script:
+  - mysql -e 'create database modeltranslation;'
+  - psql -c 'create database modeltranslation;' -U postgres
+install:
+  - if [[ $DB == mysql ]]; then pip install -q mysql-python --use-mirrors; fi
+  - if [[ $DB == postgres ]]; then pip install -q psycopg2 --use-mirrors; fi
+  - pip install -q Pillow --use-mirrors
+  - IDJANGO=$(./travis.py $DJANGO)
+  - pip install -q $IDJANGO
+  - pip install -e . --use-mirrors
+  - if [[ $TRAVIS_PYTHON_VERSION == '3.2' ]]; then pip install 'coverage<4.0.0'; fi
+  - pip install -q coveralls --use-mirrors
+script:
+  - django-admin.py --version
+  - coverage run --source=modeltranslation ./runtests.py
+after_success:
+  coveralls
diff --git a/AUTHORS.rst b/AUTHORS.rst
index f232ee9..f12e191 100644
--- a/AUTHORS.rst
+++ b/AUTHORS.rst
@@ -42,6 +42,8 @@ Contributors
 * Lukas Lundgren
 * zenoamaro
 * oliphunt
+* Venelin Stoykov
+* Stratos Moros
 * And many more ... (if you miss your name here, please let us know!)
 
 .. _django-linguo: https://github.com/zmathew/django-linguo
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index a148df2..b71736e 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -1,3 +1,42 @@
+v0.11
+=====
+Date: 2016-01-31
+
+Released without changes.
+
+
+v0.11rc2
+========
+Date: 2015-12-15
+
+  FIXED: Custom manager in migrations.
+         (resolves issues #330, #339 and #350, thanks Jacek Tomaszewski)
+
+
+v0.11rc1
+========
+Date: 2015-12-07
+
+  ADDED: Support for Django 1.9
+         (resolves issue #349, thanks Jacek Tomaszewski)
+
+
+v0.10.2
+=======
+Date: 2015-10-27
+
+  FIXED: Proxy model inheritance for Django >=1.8
+         (resolves issues #304, thanks Stratos Moros)
+
+
+v0.10.1
+=======
+Date: 2015-09-04
+
+  FIXED: FallbackValuesListQuerySet.iterator which broke ORM datetimes
+         (resolves issue #324, thanks Venelin Stoykov)
+
+
 v0.10.0
 =======
 Date: 2015-07-03
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..40af27f
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,3 @@
+include *.txt *.rst
+recursive-include docs *.rst conf.py Makefile make.bat
+recursive-include modeltranslation/static *
diff --git a/PKG-INFO b/PKG-INFO
index 5ea8f46..1847281 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,24 +1,25 @@
-Metadata-Version: 1.1
+Metadata-Version: 1.0
 Name: django-modeltranslation
-Version: 0.10
+Version: 0.11
 Summary: Translates Django models using a registration approach.
 Home-page: https://github.com/deschler/django-modeltranslation
-Author: Dirk Eschler
-Author-email: eschler at gmail.com
+Author: Peter Eschler,
+        Dirk Eschler,
+        Jacek Tomaszewski
+Author-email: "Peter Eschler" <peschler at gmail.com>,
+              "Dirk Eschler" <eschler at gmail.com>,
+              "Jacek Tomaszewski" <jacek.tomek at gmail.com>
 License: New BSD
-Download-URL: https://github.com/deschler/django-modeltranslation/archive/0.10.tar.gz
-Description: The modeltranslation application can be used to translate dynamic content of existing models to an arbitrary number of languages without having to change the original model classes. It uses a registration approach (comparable to Django's admin app) to be able to add translations to existing or new projects and is fully integrated into the Django admin backend.
+Description: The modeltranslation application can be used to translate dynamic
+             content of existing Django models to an arbitrary number of
+             languages without having to change the original model classes. It
+             uses a registration approach (comparable to Django's admin app) to
+             be able to add translations to existing or new projects and is
+             fully integrated into the Django admin backend.
+
+             The advantage of a registration approach is the ability to add
+             translations to models on a per-project basis. You can use the
+             same app in different projects, may they use translations or not,
+             and you never have to touch the original model class.
 Platform: UNKNOWN
-Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 2.6
-Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.2
-Classifier: Programming Language :: Python :: 3.3
-Classifier: Programming Language :: Python :: 3.4
-Classifier: Operating System :: OS Independent
-Classifier: Environment :: Web Environment
-Classifier: Intended Audience :: Developers
-Classifier: Framework :: Django
-Classifier: License :: OSI Approved :: BSD License
-Requires: django(>=1.4)
+Keywords: django translation i18n
diff --git a/docs/modeltranslation/_static/.gitignore b/docs/modeltranslation/_static/.gitignore
new file mode 100644
index 0000000..4397c3a
--- /dev/null
+++ b/docs/modeltranslation/_static/.gitignore
@@ -0,0 +1,2 @@
+!.gitignore
+
diff --git a/modeltranslation/__init__.py b/modeltranslation/__init__.py
index a4b04b2..98f4e18 100644
--- a/modeltranslation/__init__.py
+++ b/modeltranslation/__init__.py
@@ -3,7 +3,7 @@
 Version code adopted from Django development version.
 https://github.com/django/django
 """
-VERSION = (0, 10, 0, 'final', 0)
+VERSION = (0, 11, 0, 'final', 0)
 default_app_config = 'modeltranslation.apps.ModeltranslationConfig'
 
 
@@ -32,7 +32,7 @@ def get_version(version=None):
             sub = '.dev%s' % git_changeset
 
     elif version[3] != 'final':
-        mapping = {'alpha': 'a', 'beta': 'b', 'rc': 'c'}
+        mapping = {'alpha': 'a', 'beta': 'b', 'rc': 'rc'}
         sub = mapping[version[3]] + str(version[4])
 
     return str(main + sub)
diff --git a/modeltranslation/fields.py b/modeltranslation/fields.py
index 9885c0d..26a98e7 100644
--- a/modeltranslation/fields.py
+++ b/modeltranslation/fields.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+from django import VERSION
 from django import forms
 from django.core.exceptions import ImproperlyConfigured
 from django.db.models import fields
@@ -33,6 +34,8 @@ SUPPORTED_FIELDS = (
     # Above implies also OneToOneField
 )
 
+NEW_RELATED_API = VERSION >= (1, 9)
+
 
 class NONE:
     """
@@ -150,7 +153,7 @@ class TranslationField(object):
         self.verbose_name = build_localized_verbose_name(translated_field.verbose_name, language)
 
         # ForeignKey support - rewrite related_name
-        if self.rel and self.related and not self.rel.is_hidden():
+        if not NEW_RELATED_API and self.rel and self.related and not self.rel.is_hidden():
             import copy
             current = self.related.get_accessor_name()
             self.rel = copy.copy(self.rel)  # Since fields cannot share the same rel object.
@@ -166,6 +169,21 @@ class TranslationField(object):
             self.rel.field = self  # Django 1.6
             if hasattr(self.rel.to._meta, '_related_objects_cache'):
                 del self.rel.to._meta._related_objects_cache
+        elif NEW_RELATED_API and self.remote_field and not self.remote_field.is_hidden():
+            import copy
+            current = self.remote_field.get_accessor_name()
+            # Since fields cannot share the same rel object:
+            self.remote_field = copy.copy(self.remote_field)
+
+            if self.remote_field.related_name is None:
+                # For implicit related_name use different query field name
+                loc_related_query_name = build_localized_fieldname(
+                    self.related_query_name(), self.language)
+                self.related_query_name = lambda: loc_related_query_name
+            self.remote_field.related_name = build_localized_fieldname(current, self.language)
+            self.remote_field.field = self  # Django 1.6
+            if hasattr(self.remote_field.to._meta, '_related_objects_cache'):
+                del self.remote_field.to._meta._related_objects_cache
 
     # Django 1.5 changed definition of __hash__ for fields to be fine with hash requirements.
     # It spoiled our machinery, since TranslationField has the same creation_counter as its
@@ -250,6 +268,12 @@ class TranslationField(object):
             kwargs['db_column'] = self.db_column
         return six.text_type(self.name), path, args, kwargs
 
+    def clone(self):
+        from django.utils.module_loading import import_string
+        name, path, args, kwargs = self.deconstruct()
+        cls = import_string(path)
+        return cls(*args, **kwargs)
+
     def south_field_triple(self):
         """
         Returns a suitable description of this field for South.
diff --git a/modeltranslation/forms.py b/modeltranslation/forms.py
index f74c2c9..9e529be 100644
--- a/modeltranslation/forms.py
+++ b/modeltranslation/forms.py
@@ -35,6 +35,13 @@ class NullableField(forms.Field):
 
     # Django 1.6
     def _has_changed(self, initial, data):
+        return self.has_changed(initial, data)
+
+    def has_changed(self, initial, data):
         if (initial is None and data is not None) or (initial is not None and data is None):
             return True
-        return super(NullableField, self)._has_changed(initial, data)
+        obj = super(NullableField, self)
+        if hasattr(obj, 'has_changed'):
+            return obj.has_changed(initial, data)
+        else:  # Django < 1.9 compat
+            return obj._has_changed(initial, data)
diff --git a/modeltranslation/management/commands/sync_translation_fields.py b/modeltranslation/management/commands/sync_translation_fields.py
index ac89948..9bd6147 100644
--- a/modeltranslation/management/commands/sync_translation_fields.py
+++ b/modeltranslation/management/commands/sync_translation_fields.py
@@ -11,7 +11,7 @@ Credits: Heavily inspired by django-transmeta's sync_transmeta_db command.
 """
 from optparse import make_option
 import django
-from django.core.management.base import NoArgsCommand
+from django.core.management.base import BaseCommand
 from django.core.management.color import no_style
 from django.db import connection, transaction
 from django.utils.six import moves
@@ -46,17 +46,17 @@ def print_missing_langs(missing_langs, field_name, model_name):
         field_name, model_name, ", ".join(missing_langs)))
 
 
-class Command(NoArgsCommand):
+class Command(BaseCommand):
     help = ('Detect new translatable fields or new available languages and'
             ' sync database structure. Does not remove columns of removed'
             ' languages or undeclared fields.')
 
-    option_list = NoArgsCommand.option_list + (
+    option_list = BaseCommand.option_list + (
         make_option('--noinput', action='store_false', dest='interactive', default=True,
                     help='Do NOT prompt the user for input of any kind.'),
     )
 
-    def handle_noargs(self, **options):
+    def handle(self, *args, **options):
         """
         Command execution.
         """
diff --git a/modeltranslation/management/commands/update_translation_fields.py b/modeltranslation/management/commands/update_translation_fields.py
index 76bfc68..98e1bfb 100644
--- a/modeltranslation/management/commands/update_translation_fields.py
+++ b/modeltranslation/management/commands/update_translation_fields.py
@@ -1,17 +1,17 @@
 # -*- coding: utf-8 -*-
 from django.db.models import F, Q
-from django.core.management.base import NoArgsCommand
+from django.core.management.base import BaseCommand
 
 from modeltranslation.settings import DEFAULT_LANGUAGE
 from modeltranslation.translator import translator
 from modeltranslation.utils import build_localized_fieldname
 
 
-class Command(NoArgsCommand):
+class Command(BaseCommand):
     help = ('Updates empty values of default translation fields using'
             ' values from original fields (in all translated models).')
 
-    def handle_noargs(self, **options):
+    def handle(self, *args, **options):
         verbosity = int(options['verbosity'])
         if verbosity > 0:
             self.stdout.write("Using default language: %s\n" % DEFAULT_LANGUAGE)
diff --git a/modeltranslation/manager.py b/modeltranslation/manager.py
index e36df4a..7cf2b18 100644
--- a/modeltranslation/manager.py
+++ b/modeltranslation/manager.py
@@ -16,13 +16,20 @@ try:
 except ImportError:
     NEW_META_API = True
 
-from django.db.models.sql.where import Constraint
+try:
+    from django.db.models.query import ValuesQuerySet
+    from django.db.models.sql.where import Constraint
+    NEW_RELATED_API = False
+except ImportError:
+    from django.db.models.query import ValuesIterable
+    NEW_RELATED_API = True  # Django 1.9
+
 from django.utils.six import moves
 from django.utils.tree import Node
 try:
     from django.db.models.lookups import Lookup
     from django.db.models.sql.datastructures import Col
-    NEW_LOOKUPS = True
+    NEW_LOOKUPS = True  # Django 1.7, 1.8
 except ImportError:
     NEW_LOOKUPS = False
 
@@ -193,15 +200,27 @@ class MultilingualQuerySet(models.query.QuerySet):
         return multilingual_queryset_factory, (self.__class__.__bases__[0],), self.__getstate__()
 
     # This method was not present in django-linguo
-    def _clone(self, klass=None, *args, **kwargs):
-        if klass is not None and not issubclass(klass, MultilingualQuerySet):
-            class NewClass(klass, MultilingualQuerySet):
-                pass
-            NewClass.__name__ = 'Multilingual%s' % klass.__name__
-            klass = NewClass
-        kwargs.setdefault('_rewrite', self._rewrite)
-        kwargs.setdefault('_populate', self._populate)
-        return super(MultilingualQuerySet, self)._clone(klass, *args, **kwargs)
+    if NEW_RELATED_API:
+        def _clone(self, klass=None, **kwargs):
+            kwargs.setdefault('_rewrite', self._rewrite)
+            kwargs.setdefault('_populate', self._populate)
+            if hasattr(self, 'translation_fields'):
+                kwargs.setdefault('translation_fields', self.translation_fields)
+            if hasattr(self, 'fields_to_del'):
+                kwargs.setdefault('fields_to_del', self.fields_to_del)
+            if hasattr(self, 'original_fields'):
+                kwargs.setdefault('original_fields', self.original_fields)
+            return super(MultilingualQuerySet, self)._clone(**kwargs)
+    else:
+        def _clone(self, klass=None, *args, **kwargs):
+            if klass is not None and not issubclass(klass, MultilingualQuerySet):
+                class NewClass(klass, MultilingualQuerySet):
+                    pass
+                NewClass.__name__ = 'Multilingual%s' % klass.__name__
+                klass = NewClass
+            kwargs.setdefault('_rewrite', self._rewrite)
+            kwargs.setdefault('_populate', self._populate)
+            return super(MultilingualQuerySet, self)._clone(klass, *args, **kwargs)
 
     # This method was not present in django-linguo
     def rewrite(self, mode=True):
@@ -220,7 +239,8 @@ class MultilingualQuerySet(models.query.QuerySet):
         Useful when converting any QuerySet into MultilingualQuerySet.
         """
         self._rewrite_where(self.query.where)
-        self._rewrite_where(self.query.having)
+        if not NEW_RELATED_API:
+            self._rewrite_where(self.query.having)
         self._rewrite_order()
         self._rewrite_select_related()
 
@@ -318,7 +338,9 @@ class MultilingualQuerySet(models.query.QuerySet):
         return super(MultilingualQuerySet, self)._filter_or_exclude(negate, *args, **kwargs)
 
     def _get_original_fields(self):
-        return [f.attname for f in self.model._meta.fields if not isinstance(f, TranslationField)]
+        source = (self.model._meta.concrete_fields if hasattr(self.model._meta, 'concrete_fields')
+                  else self.model._meta.fields)
+        return [f.attname for f in source if not isinstance(f, TranslationField)]
 
     def order_by(self, *field_names):
         """
@@ -380,6 +402,16 @@ class MultilingualQuerySet(models.query.QuerySet):
     def raw_values(self, *fields):
         return super(MultilingualQuerySet, self).values(*fields)
 
+    def _values(self, *original, **kwargs):
+        if not kwargs.get('prepare', False):
+            return super(MultilingualQuerySet, self)._values(*original)
+        new_fields, translation_fields = append_fallback(self.model, original)
+        clone = super(MultilingualQuerySet, self)._values(*list(new_fields))
+        clone.original_fields = tuple(original)
+        clone.translation_fields = translation_fields
+        clone.fields_to_del = new_fields - set(original)
+        return clone
+
     # This method was not present in django-linguo
     def values(self, *fields):
         if not self._rewrite:
@@ -387,7 +419,12 @@ class MultilingualQuerySet(models.query.QuerySet):
         if not fields:
             # Emulate original queryset behaviour: get all fields that are not translation fields
             fields = self._get_original_fields()
-        return self._clone(klass=FallbackValuesQuerySet, setup=True, _fields=fields)
+        if NEW_RELATED_API:
+            clone = self._values(*fields, prepare=True)
+            clone._iterable_class = FallbackValuesIterable
+            return clone
+        else:
+            return self._clone(klass=FallbackValuesQuerySet, setup=True, _fields=fields)
 
     # This method was not present in django-linguo
     def values_list(self, *fields, **kwargs):
@@ -402,7 +439,14 @@ class MultilingualQuerySet(models.query.QuerySet):
         if not fields:
             # Emulate original queryset behaviour: get all fields that are not translation fields
             fields = self._get_original_fields()
-        return self._clone(klass=FallbackValuesListQuerySet, setup=True, flat=flat, _fields=fields)
+        if NEW_RELATED_API:
+            clone = self._values(*fields, prepare=True)
+            clone._iterable_class = (FallbackFlatValuesListIterable if flat
+                                     else FallbackValuesListIterable)
+            return clone
+        else:
+            return self._clone(klass=FallbackValuesListQuerySet, setup=True, flat=flat,
+                               _fields=fields)
 
     # This method was not present in django-linguo
     def dates(self, field_name, *args, **kwargs):
@@ -412,63 +456,91 @@ class MultilingualQuerySet(models.query.QuerySet):
         return super(MultilingualQuerySet, self).dates(new_key, *args, **kwargs)
 
 
-class FallbackValuesQuerySet(models.query.ValuesQuerySet, MultilingualQuerySet):
-    def _setup_query(self):
-        original = self._fields
-        new_fields, self.translation_fields = append_fallback(self.model, original)
-        self._fields = list(new_fields)
-        self.fields_to_del = new_fields - set(original)
-        super(FallbackValuesQuerySet, self)._setup_query()
-
-    class X(object):
-        # This stupid class is needed as object use __slots__ and has no __dict__.
-        pass
+if NEW_RELATED_API:
+    class FallbackValuesIterable(ValuesIterable):
+        class X(object):
+            # This stupid class is needed as object use __slots__ and has no __dict__.
+            pass
 
-    def iterator(self):
-        instance = self.X()
-        for row in super(FallbackValuesQuerySet, self).iterator():
-            instance.__dict__.update(row)
-            for key in self.translation_fields:
-                row[key] = getattr(self.model, key).__get__(instance, None)
-            for key in self.fields_to_del:
-                del row[key]
-            yield row
-
-    def _clone(self, klass=None, setup=False, **kwargs):
-        c = super(FallbackValuesQuerySet, self)._clone(klass, **kwargs)
-        c.fields_to_del = self.fields_to_del
-        c.translation_fields = self.translation_fields
-        if setup and hasattr(c, '_setup_query'):
-            c._setup_query()
-        return c
-
-
-class FallbackValuesListQuerySet(FallbackValuesQuerySet):
-    def iterator(self):
-        fields = self.original_fields
-        if hasattr(self, 'aggregate_names'):
-            # Django <1.8
-            fields += tuple(self.aggregate_names)
-        if hasattr(self, 'annotation_names'):
-            # Django >=1.8
-            fields += tuple(self.annotation_names)
-        for row in super(FallbackValuesListQuerySet, self).iterator():
-            if self.flat and len(fields) == 1:
-                yield row[fields[0]]
-            else:
+        def __iter__(self):
+            instance = self.X()
+            for row in super(FallbackValuesIterable, self).__iter__():
+                instance.__dict__.update(row)
+                for key in self.queryset.translation_fields:
+                    row[key] = getattr(self.queryset.model, key).__get__(instance, None)
+                for key in self.queryset.fields_to_del:
+                    del row[key]
+                yield row
+
+    class FallbackValuesListIterable(FallbackValuesIterable):
+        def __iter__(self):
+            fields = self.queryset.original_fields
+            fields += tuple(f for f in self.queryset.query.annotation_select if f not in fields)
+            for row in super(FallbackValuesListIterable, self).__iter__():
                 yield tuple(row[f] for f in fields)
 
-    def _setup_query(self):
-        self.original_fields = tuple(self._fields)
-        super(FallbackValuesListQuerySet, self)._setup_query()
+    class FallbackFlatValuesListIterable(FallbackValuesListIterable):
+        def __iter__(self):
+            for row in super(FallbackFlatValuesListIterable, self).__iter__():
+                yield row[0]
+
+else:
+    class FallbackValuesQuerySet(ValuesQuerySet, MultilingualQuerySet):
+        def _setup_query(self):
+            original = self._fields
+            new_fields, self.translation_fields = append_fallback(self.model, original)
+            self._fields = list(new_fields)
+            self.fields_to_del = new_fields - set(original)
+            super(FallbackValuesQuerySet, self)._setup_query()
+
+        class X(object):
+            # This stupid class is needed as object use __slots__ and has no __dict__.
+            pass
 
-    def _clone(self, *args, **kwargs):
-        clone = super(FallbackValuesListQuerySet, self)._clone(*args, **kwargs)
-        clone.original_fields = self.original_fields
-        if not hasattr(clone, "flat"):
-            # Only assign flat if the clone didn't already get it from kwargs
-            clone.flat = self.flat
-        return clone
+        def iterator(self):
+            instance = self.X()
+            for row in super(FallbackValuesQuerySet, self).iterator():
+                instance.__dict__.update(row)
+                for key in self.translation_fields:
+                    row[key] = getattr(self.model, key).__get__(instance, None)
+                for key in self.fields_to_del:
+                    del row[key]
+                yield row
+
+        def _clone(self, klass=None, setup=False, **kwargs):
+            c = super(FallbackValuesQuerySet, self)._clone(klass, **kwargs)
+            c.fields_to_del = self.fields_to_del
+            c.translation_fields = self.translation_fields
+            if setup and hasattr(c, '_setup_query'):
+                c._setup_query()
+            return c
+
+    class FallbackValuesListQuerySet(FallbackValuesQuerySet):
+        def iterator(self):
+            fields = self.original_fields
+            if hasattr(self, 'aggregate_names'):
+                # Django <1.8
+                fields += tuple(f for f in self.aggregate_names if f not in fields)
+            if hasattr(self, 'annotation_names'):
+                # Django >=1.8
+                fields += tuple(f for f in self.annotation_names if f not in fields)
+            for row in super(FallbackValuesListQuerySet, self).iterator():
+                if self.flat and len(fields) == 1:
+                    yield row[fields[0]]
+                else:
+                    yield tuple(row[f] for f in fields)
+
+        def _setup_query(self):
+            self.original_fields = tuple(self._fields)
+            super(FallbackValuesListQuerySet, self)._setup_query()
+
+        def _clone(self, *args, **kwargs):
+            clone = super(FallbackValuesListQuerySet, self)._clone(*args, **kwargs)
+            clone.original_fields = self.original_fields
+            if not hasattr(clone, "flat"):
+                # Only assign flat if the clone didn't already get it from kwargs
+                clone.flat = self.flat
+            return clone
 
 
 def get_queryset(obj):
diff --git a/modeltranslation/tests/__init__.py b/modeltranslation/tests/__init__.py
new file mode 100644
index 0000000..47945af
--- /dev/null
+++ b/modeltranslation/tests/__init__.py
@@ -0,0 +1,2 @@
+# For Django < 1.6 testrunner
+from .tests import *  # NOQA
diff --git a/modeltranslation/tests/fixtures/fixture.json b/modeltranslation/tests/fixtures/fixture.json
new file mode 100644
index 0000000..83462f3
--- /dev/null
+++ b/modeltranslation/tests/fixtures/fixture.json
@@ -0,0 +1,10 @@
+[
+    {
+        "pk": 1,
+        "model": "tests.TestModel",
+        "fields": {
+            "title": "foo",
+            "text": "bar"
+        }
+    }
+]
diff --git a/modeltranslation/tests/models.py b/modeltranslation/tests/models.py
new file mode 100644
index 0000000..dcbd4b2
--- /dev/null
+++ b/modeltranslation/tests/models.py
@@ -0,0 +1,315 @@
+# -*- coding: utf-8 -*-
+from django.core import validators
+from django.db import models
+from django.utils import six
+from django.utils.translation import ugettext_lazy
+
+
+class TestModel(models.Model):
+    title = models.CharField(ugettext_lazy('title'), max_length=255)
+    text = models.TextField(blank=True, null=True)
+    url = models.URLField(blank=True, null=True)
+    email = models.EmailField(blank=True, null=True)
+
+
+class UniqueNullableModel(models.Model):
+    title = models.CharField(null=True, unique=True, max_length=255)
+
+
+# ######### Proxy model testing
+
+class ProxyTestModel(TestModel):
+    class Meta:
+        proxy = True
+
+    def get_title(self):
+        return self.title
+
+
+# ######### Fallback values testing
+
+class FallbackModel(models.Model):
+    title = models.CharField(ugettext_lazy('title'), max_length=255)
+    text = models.TextField(blank=True, null=True)
+    url = models.URLField(blank=True, null=True)
+    email = models.EmailField(blank=True, null=True)
+    description = models.CharField(max_length=255, null=True)
+
+
+class FallbackModel2(models.Model):
+    title = models.CharField(ugettext_lazy('title'), max_length=255)
+    text = models.TextField(blank=True, null=True)
+    url = models.URLField(blank=True, null=True)
+    email = models.EmailField(blank=True, null=True)
+
+
+# ######### File fields testing
+
+class FileFieldsModel(models.Model):
+    title = models.CharField(ugettext_lazy('title'), max_length=255)
+    file = models.FileField(upload_to='modeltranslation_tests', null=True, blank=True)
+    file2 = models.FileField(upload_to='modeltranslation_tests')
+    image = models.ImageField(upload_to='modeltranslation_tests', null=True, blank=True)
+
+
+# ######### Foreign Key / OneToOneField testing
+
+class NonTranslated(models.Model):
+    title = models.CharField(ugettext_lazy('title'), max_length=255)
+
+
+class ForeignKeyModel(models.Model):
+    title = models.CharField(ugettext_lazy('title'), max_length=255)
+    test = models.ForeignKey(TestModel, null=True, related_name="test_fks")
+    optional = models.ForeignKey(TestModel, blank=True, null=True)
+    hidden = models.ForeignKey(TestModel, blank=True, null=True, related_name="+")
+    non = models.ForeignKey(NonTranslated, blank=True, null=True, related_name="test_fks")
+    untrans = models.ForeignKey(TestModel, blank=True, null=True, related_name="test_fks_un")
+
+
+class OneToOneFieldModel(models.Model):
+    title = models.CharField(ugettext_lazy('title'), max_length=255)
+    test = models.OneToOneField(TestModel, null=True, related_name="test_o2o")
+    optional = models.OneToOneField(TestModel, blank=True, null=True)
+    # No hidden option for OneToOne
+    non = models.OneToOneField(NonTranslated, blank=True, null=True, related_name="test_o2o")
+
+
+# ######### Custom fields testing
+
+class OtherFieldsModel(models.Model):
+    """
+    This class is supposed to include other newly added fields types, so that
+    adding new supported field doesn't end in adding new test model.
+    """
+    # That's rich! PositiveIntegerField is only validated in forms, not in models.
+    int = models.PositiveIntegerField(default=42, validators=[validators.MinValueValidator(0)])
+    boolean = models.BooleanField(default=False)
+    nullboolean = models.NullBooleanField()
+    csi = models.CommaSeparatedIntegerField(max_length=255)
+    float = models.FloatField(blank=True, null=True)
+    decimal = models.DecimalField(max_digits=5, decimal_places=2, blank=True, null=True)
+    ip = models.IPAddressField(blank=True, null=True)
+    date = models.DateField(blank=True, null=True)
+    datetime = models.DateTimeField(blank=True, null=True)
+    time = models.TimeField(blank=True, null=True)
+    genericip = models.GenericIPAddressField(blank=True, null=True)
+
+
+class FancyDescriptor(object):
+    """
+    Stupid demo descriptor, that store int in database and return string of that length on get.
+    """
+    def __init__(self, field):
+        self.field = field
+
+    def __get__(self, instance, owner):
+        length = instance.__dict__[self.field.name]
+        if length is None:
+            return ''
+        return 'a' * length
+
+    def __set__(self, obj, value):
+        if isinstance(value, six.integer_types):
+            obj.__dict__[self.field.name] = value
+        elif isinstance(value, six.string_types):
+            obj.__dict__[self.field.name] = len(value)
+        else:
+            obj.__dict__[self.field.name] = 0
+
+
+class FancyField(models.PositiveIntegerField):
+    def __init__(self, *args, **kwargs):
+        kwargs.setdefault('default', '')
+        super(FancyField, self).__init__(*args, **kwargs)
+
+    def contribute_to_class(self, cls, name):
+        super(FancyField, self).contribute_to_class(cls, name)
+        setattr(cls, self.name, FancyDescriptor(self))
+
+    def pre_save(self, model_instance, add):
+        value = super(FancyField, self).pre_save(model_instance, add)
+        # In this part value should be retrieved using descriptor and be a string
+        assert isinstance(value, six.string_types)
+        # We put an int to database
+        return len(value)
+
+
+class DescriptorModel(models.Model):
+    normal = FancyField()
+    trans = FancyField()
+
+
+# ######### Multitable inheritance testing
+
+class MultitableModelA(models.Model):
+    titlea = models.CharField(ugettext_lazy('title a'), max_length=255)
+
+
+class MultitableModelB(MultitableModelA):
+    titleb = models.CharField(ugettext_lazy('title b'), max_length=255)
+
+
+class MultitableModelC(MultitableModelB):
+    titlec = models.CharField(ugettext_lazy('title c'), max_length=255)
+
+
+class MultitableModelD(MultitableModelB):
+    titled = models.CharField(ugettext_lazy('title d'), max_length=255)
+
+
+# ######### Abstract inheritance testing
... 3851 lines suppressed ...

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



More information about the Python-modules-commits mailing list