Merge pull request #298 from jleclanche/cleanup/django18

Drop Django < 1.8 compatibility
fix_request_path_info
Diederik van der Boor 2017-07-10 10:55:16 +02:00 committed by GitHub
commit e5d21d7b4e
30 changed files with 218 additions and 654 deletions

View File

@ -2,20 +2,12 @@ sudo: false
language: python
cache: pip
python:
- "2.6"
- "2.7"
- "3.2"
- "3.3"
- "3.4"
- "3.5"
- "3.6"
env:
- TOXENV="django14"
- TOXENV="django15"
- TOXENV="django16"
- TOXENV="django17"
- TOXENV="django18"
- TOXENV="django19"
- TOXENV="django110"
- TOXENV="django111"
- TOXENV="djangodev"
@ -23,77 +15,19 @@ env:
matrix:
fast_finish: true
exclude:
- python: "3.6"
env: TOXENV="django14"
- python: "3.6"
env: TOXENV="django15"
- python: "3.6"
env: TOXENV="django16"
- python: "3.6"
env: TOXENV="django17"
- python: "3.6"
env: TOXENV="django18"
- python: "3.6"
env: TOXENV="django19"
- python: "3.6"
env: TOXENV="django110"
- python: "3.5"
env: TOXENV="django14"
- python: "3.5"
env: TOXENV="django15"
- python: "3.5"
env: TOXENV="django16"
- python: "3.5"
env: TOXENV="django17"
- python: "3.4"
env: TOXENV="django14"
- python: "3.4"
env: TOXENV="django19"
- python: "3.4"
env: TOXENV="django110"
- python: "3.4"
env: TOXENV="django111"
- python: "3.3"
env: TOXENV="django14"
- python: "3.3"
env: TOXENV="django19"
- python: "3.3"
env: TOXENV="django110"
- python: "3.3"
env: TOXENV="django111"
- python: "3.3"
env: TOXENV="djangodev"
- python: "3.2"
env: TOXENV="django14"
- python: "3.2"
env: TOXENV="django19"
- python: "3.2"
env: TOXENV="django110"
- python: "3.2"
env: TOXENV="django111"
- python: "3.2"
env: TOXENV="djangodev"
- python: "2.7"
env: TOXENV="djangodev"
- python: "2.6"
env: TOXENV="django17"
- python: "2.6"
env: TOXENV="django18"
- python: "2.6"
env: TOXENV="django19"
- python: "2.6"
env: TOXENV="django110"
- python: "2.6"
env: TOXENV="django111"
- python: "2.6"
env: TOXENV="djangodev"
allow_failures:
- env: TOXENV="djangodev"

View File

@ -22,6 +22,7 @@ Contributors
* Hugo Osvaldo Barrera
* Jacob Rief
* Jedediah Smith (proxy models support)
* Jerome Leclanche
* John Furr
* Jonas Obrist
* Julian Wachholz

View File

@ -1,4 +0,0 @@
# for readthedocs
# Remaining requirements are picked up from setup.py
Django==1.11
django-extra-views==0.9.0

View File

@ -2,22 +2,16 @@
Automatically mention all model fields as parameters in the model construction.
Based on http://djangosnippets.org/snippets/2533/
"""
import django
from django.utils.html import strip_tags
from django.utils.encoding import force_text
import inspect
from django.utils.encoding import force_text
from django.utils.html import strip_tags
def improve_model_docstring(app, what, name, obj, options, lines):
from django.db import models # must be inside the function, to allow settings initialization first.
if inspect.isclass(obj) and issubclass(obj, models.Model):
if django.VERSION >= (1,8):
model_fields = obj._meta.get_fields()
elif django.VERSION >= (1,6):
model_fields = obj._meta.fields
else:
model_fields = obj._meta._fields()
model_fields = obj._meta.get_fields()
for field in model_fields:
help_text = strip_tags(force_text(field.help_text))
@ -39,9 +33,9 @@ def improve_model_docstring(app, what, name, obj, options, lines):
# Return the extended docstring
return lines
# Allow this module to be used as sphinx extension:
def setup(app):
# Generate docstrings for Django model fields
# Register the docstring processor with sphinx
app.connect('autodoc-process-docstring', improve_model_docstring)

View File

@ -23,8 +23,7 @@ sys.path.insert(0, os.path.abspath('_ext'))
sys.path.insert(0, os.path.abspath('..'))
os.environ['DJANGO_SETTINGS_MODULE'] = 'djangodummy.settings'
if django.VERSION >= (1, 8):
django.setup()
django.setup()
# -- General configuration -----------------------------------------------------

View File

@ -32,8 +32,6 @@ Django as automatic manager for several purposes, including accessing
related objects. It must not filter objects and it's safest to use
the plain ``PolymorphicManager`` here.
Note that get_query_set is deprecated in Django 1.8 and creates warnings in Django 1.7.
Manager Inheritance
-------------------
@ -69,8 +67,6 @@ regarding their start time and ``ArtProject.objects_ordered.most_recent()``
will return the ten most recent art projects.
.
Note that get_query_set is deprecated in Django 1.8 and creates warnings in Django 1.7.
Using a Custom Queryset Class
-----------------------------

View File

@ -61,6 +61,7 @@ It's recommended to let ``makemigrations`` create the migration file,
and include the ``RunPython`` manually before running the migration.
.. versionadded:: 1.1
When the model is created elsewhere, you can also use
the :func:`polymorphic.utils.reset_polymorphic_ctype` function:

View File

@ -1,4 +1,3 @@
import django
import os
DEBUG = True
@ -67,7 +66,6 @@ INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.contenttypes',
'django.contrib.sessions',
#'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
@ -76,8 +74,7 @@ INSTALLED_APPS = (
'orders',
)
if django.VERSION >= (1, 7):
TEST_RUNNER = 'django.test.runner.DiscoverRunner' # silence system checks
TEST_RUNNER = 'django.test.runner.DiscoverRunner' # silence system checks
# Logging configuration
LOGGING = {

View File

@ -3,34 +3,32 @@
This module is a scratchpad for general development, testing & debugging
Well, even more so than pcmd.py. You best ignore p2cmd.py.
"""
from django.core.management.base import NoArgsCommand
from pprint import pprint
import time
import sys
import time
from pprint import pprint
from random import Random
from django.core.management.base import NoArgsCommand
from django.db import connection
from pexp.models import *
def reset_queries():
if django.VERSION < (1, 8):
connection.queries = []
else:
connection.queries_log.clear()
rnd = Random()
def show_queries():
print
print 'QUERIES:', len(connection.queries)
print()
print("QUERIES:", len(connection.queries))
pprint(connection.queries)
print
print()
connection.queries = []
def print_timing(func, message='', iterations=1):
def wrapper(*arg):
results = []
reset_queries()
for i in xrange(iterations):
connection.queries_log.clear()
for i in range(iterations):
t1 = time.time()
x = func(*arg)
t2 = time.time()
@ -38,13 +36,12 @@ def print_timing(func, message='', iterations=1):
res_sum = 0
for r in results:
res_sum += r
median = res_sum / len(results)
print '%s%-19s: %.4f ms, %i queries (%i times)' % (
print("%s%-19s: %.4f ms, %i queries (%i times)" % (
message, func.func_name,
res_sum,
len(connection.queries),
iterations
)
))
sys.stdout.flush()
return wrapper
@ -58,18 +55,18 @@ class Command(NoArgsCommand):
a = TestModelA.objects.create(field1='A1')
b = TestModelB.objects.create(field1='B1', field2='B2')
c = TestModelC.objects.create(field1='C1', field2='C2', field3='C3')
reset_queries()
print TestModelC.base_objects.all()
connection.queries_log.clear()
print(TestModelC.base_objects.all())
show_queries()
if False:
TestModelA.objects.all().delete()
for i in xrange(1000):
for i in range(1000):
a = TestModelA.objects.create(field1=str(i % 100))
b = TestModelB.objects.create(field1=str(i % 100), field2=str(i % 200))
c = TestModelC.objects.create(field1=str(i % 100), field2=str(i % 200), field3=str(i % 300))
if i % 100 == 0:
print i
print(i)
f = print_timing(poly_sql_query, iterations=1000)
f()
@ -85,11 +82,7 @@ class Command(NoArgsCommand):
c = NormalModelC.objects.create(field1='C1', field2='C2', field3='C3')
qs = TestModelA.objects.raw("SELECT * from pexp_testmodela")
for o in list(qs):
print o
from django.db import connection, transaction
from random import Random
rnd = Random()
print(o)
def poly_sql_query():
@ -103,7 +96,7 @@ def poly_sql_query():
ON pexp_testmodelb.testmodela_ptr_id = pexp_testmodelc.testmodelb_ptr_id
WHERE pexp_testmodela.field1=%i
ORDER BY pexp_testmodela.id
""" % rnd.randint(0, 100) )
""" % rnd.randint(0, 100))
# row=cursor.fetchone()
return
@ -115,6 +108,6 @@ def poly_sql_query2():
FROM pexp_testmodela
WHERE pexp_testmodela.field1=%i
ORDER BY pexp_testmodela.id
""" % rnd.randint(0, 100) )
""" % rnd.randint(0, 100))
# row=cursor.fetchone()
return

View File

@ -3,30 +3,25 @@
This module is a scratchpad for general development, testing & debugging
"""
import time
import sys
from django.core.management.base import NoArgsCommand
from django.db import connection
from pprint import pprint
import sys
from pexp.models import *
num_objects = 1000
def reset_queries():
if django.VERSION < (1, 8):
connection.queries = []
else:
connection.queries_log.clear()
def show_queries():
print
print 'QUERIES:', len(connection.queries)
print()
print("QUERIES:", len(connection.queries))
pprint(connection.queries)
print
reset_queries()
print()
connection.queries_log.clear()
import time
###################################################################################
# benchmark wrappers
@ -35,8 +30,8 @@ import time
def print_timing(func, message='', iterations=1):
def wrapper(*arg):
results = []
reset_queries()
for i in xrange(iterations):
connection.queries_log.clear()
for i in range(iterations):
t1 = time.time()
x = func(*arg)
t2 = time.time()
@ -45,11 +40,11 @@ def print_timing(func, message='', iterations=1):
for r in results:
res_sum += r
median = res_sum / len(results)
print '%s%-19s: %.0f ms, %i queries' % (
print("%s%-19s: %.0f ms, %i queries" % (
message, func.func_name,
median,
len(connection.queries) / len(results)
)
))
sys.stdout.flush()
return wrapper

View File

@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-
import django
from django.db import models
from polymorphic.models import PolymorphicModel
@ -20,15 +19,9 @@ class ResearchProject(Project):
supervisor = models.CharField(max_length=30)
if django.VERSION < (1, 8):
from polymorphic.tools_for_tests import UUIDField
else:
from django.db.models import UUIDField
class UUIDModelA(ShowFieldTypeAndContent, PolymorphicModel):
"""UUID as primary key example"""
uuid_primary_key = UUIDField(primary_key=True)
uuid_primary_key = models.UUIDField(primary_key=True)
field1 = models.CharField(max_length=10)

View File

@ -6,36 +6,6 @@ Copyright:
This code and affiliated files are (C) by Bert Constantin and individual contributors.
Please see LICENSE and AUTHORS for more information.
"""
import django
# See PEP 440 (https://www.python.org/dev/peps/pep-0440/)
__version__ = "1.2"
# Monkey-patch Django < 1.5 to allow ContentTypes for proxy models.
if django.VERSION[:2] < (1, 5):
from django.contrib.contenttypes.models import ContentTypeManager
from django.utils.encoding import smart_text
def get_for_model(self, model, for_concrete_model=True):
if for_concrete_model:
model = model._meta.concrete_model
elif model._deferred:
model = model._meta.proxy_for_model
opts = model._meta
try:
ct = self._get_from_cache(opts)
except KeyError:
ct, created = self.get_or_create(
app_label=opts.app_label,
model=opts.object_name.lower(),
defaults={'name': smart_text(opts.verbose_name_raw)},
)
self._add_to_cache(self.db, ct)
return ct
ContentTypeManager.get_for_model__original = ContentTypeManager.get_for_model
ContentTypeManager.get_for_model = get_for_model

View File

@ -1,14 +1,10 @@
from django.contrib.contenttypes.admin import GenericInlineModelAdmin
from django.contrib.contenttypes.models import ContentType
from django.utils.functional import cached_property
from polymorphic.formsets import polymorphic_child_forms_factory, BaseGenericPolymorphicInlineFormSet, GenericPolymorphicFormSetChild
from .inlines import PolymorphicInlineModelAdmin
try:
from django.contrib.contenttypes.admin import GenericInlineModelAdmin # Django 1.7+
except ImportError:
from django.contrib.contenttypes.generic import GenericInlineModelAdmin
class GenericPolymorphicInlineModelAdmin(PolymorphicInlineModelAdmin, GenericInlineModelAdmin):
"""

View File

@ -6,6 +6,7 @@ Each row in the inline can correspond with a different subclass.
from functools import partial
from django.contrib.admin.options import InlineModelAdmin
from django.contrib.admin.utils import flatten_fieldsets
from django.core.exceptions import ImproperlyConfigured
from django.forms import Media
@ -13,11 +14,6 @@ from polymorphic.formsets import polymorphic_child_forms_factory, BasePolymorphi
from polymorphic.formsets.utils import add_media
from .helpers import PolymorphicInlineSupportMixin
try:
from django.contrib.admin.utils import flatten_fieldsets # Django 1.7+
except ImportError:
from django.contrib.admin.util import flatten_fieldsets
class PolymorphicInlineModelAdmin(InlineModelAdmin):
"""

View File

@ -1,19 +1,18 @@
"""
The parent admin displays the list view of the base model.
"""
import sys
import warnings
import django
from django.conf.urls import url
from django.contrib import admin
from django.contrib.admin.helpers import AdminErrorList, AdminForm
from django.contrib.admin.templatetags.admin_urls import add_preserved_filters
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import RegexURLResolver
from django.http import Http404, HttpResponseRedirect
from django.shortcuts import render_to_response
from django.template.context import RequestContext
from django.template.response import TemplateResponse
from django.utils.encoding import force_text
from django.utils.http import urlencode
from django.utils.safestring import mark_safe
@ -21,16 +20,6 @@ from django.utils.translation import ugettext_lazy as _
from .forms import PolymorphicModelChoiceForm
try:
# Django 1.6 implements this
from django.contrib.admin.templatetags.admin_urls import add_preserved_filters
except ImportError:
def add_preserved_filters(context, form_url):
return form_url
if sys.version_info[0] >= 3:
long = int
class RegistrationClosed(RuntimeError):
"The admin model can't be registered anymore at this point."
@ -72,7 +61,7 @@ class PolymorphicParentModelAdmin(admin.ModelAdmin):
#: The regular expression to filter the primary key in the URL.
#: This accepts only numbers as defensive measure against catch-all URLs.
#: If your primary key consists of string values, update this regular expression.
pk_regex = '(\d+|__fk__)'
pk_regex = r"(\d+|__fk__)"
def __init__(self, model, admin_site, *args, **kwargs):
super(PolymorphicParentModelAdmin, self).__init__(model, admin_site, *args, **kwargs)
@ -210,13 +199,6 @@ class PolymorphicParentModelAdmin(admin.ModelAdmin):
qs = qs.non_polymorphic()
return qs
# For Django 1.5:
def queryset(self, request):
qs = super(PolymorphicParentModelAdmin, self).queryset(request)
if not self.polymorphic_list:
qs = qs.non_polymorphic()
return qs
def add_view(self, request, form_url='', extra_context=None):
"""Redirect the add view to the real admin."""
ct_id = int(request.GET.get('ct_id', 0))
@ -238,17 +220,16 @@ class PolymorphicParentModelAdmin(admin.ModelAdmin):
real_admin = self._get_real_admin(object_id)
return real_admin.change_view(request, object_id, *args, **kwargs)
if django.VERSION >= (1, 7):
def changeform_view(self, request, object_id=None, *args, **kwargs):
# The `changeform_view` is available as of Django 1.7, combining the add_view and change_view.
# As it's directly called by django-reversion, this method is also overwritten to make sure it
# also redirects to the child admin.
if object_id:
real_admin = self._get_real_admin(object_id)
return real_admin.changeform_view(request, object_id, *args, **kwargs)
else:
# Add view. As it should already be handled via `add_view`, this means something custom is done here!
return super(PolymorphicParentModelAdmin, self).changeform_view(request, object_id, *args, **kwargs)
def changeform_view(self, request, object_id=None, *args, **kwargs):
# The `changeform_view` is available as of Django 1.7, combining the add_view and change_view.
# As it's directly called by django-reversion, this method is also overwritten to make sure it
# also redirects to the child admin.
if object_id:
real_admin = self._get_real_admin(object_id)
return real_admin.changeform_view(request, object_id, *args, **kwargs)
else:
# Add view. As it should already be handled via `add_view`, this means something custom is done here!
return super(PolymorphicParentModelAdmin, self).changeform_view(request, object_id, *args, **kwargs)
def history_view(self, request, object_id, extra_context=None):
"""Redirect the history view to the real admin."""
@ -334,9 +315,9 @@ class PolymorphicParentModelAdmin(admin.ModelAdmin):
try:
pos = path.find('/')
if pos == -1:
object_id = long(path)
object_id = int(path)
else:
object_id = long(path[0:pos])
object_id = int(path[0:pos])
except ValueError:
raise Http404("No ct_id parameter, unable to find admin subclass for path '{0}'.".format(path))
@ -407,8 +388,6 @@ class PolymorphicParentModelAdmin(admin.ModelAdmin):
'add': True,
'save_on_top': self.save_on_top,
})
if hasattr(self.admin_site, 'root_path'):
context['root_path'] = self.admin_site.root_path # Django < 1.4
templates = self.add_type_template or [
"admin/%s/%s/add_type_form.html" % (app_label, opts.object_name.lower()),
@ -417,13 +396,8 @@ class PolymorphicParentModelAdmin(admin.ModelAdmin):
"admin/add_type_form.html"
]
if django.VERSION >= (1, 8):
from django.template.response import TemplateResponse
request.current_app = self.admin_site.name
return TemplateResponse(request, templates, context)
else:
context_instance = RequestContext(request, current_app=self.admin_site.name)
return render_to_response(templates, context, context_instance=context_instance)
request.current_app = self.admin_site.name
return TemplateResponse(request, templates, context)
@property
def change_list_template(self):
@ -445,7 +419,4 @@ class PolymorphicParentModelAdmin(admin.ModelAdmin):
def _get_opt(model):
try:
return model._meta.app_label, model._meta.model_name # Django 1.7 format
except AttributeError:
return model._meta.app_label, model._meta.module_name
return model._meta.app_label, model._meta.model_name

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
""" PolymorphicModel Meta Class
Please see README.rst or DOCS.rst or http://chrisglass.github.com/django_polymorphic/
"""
PolymorphicModel Meta Class
"""
from __future__ import absolute_import
@ -132,6 +132,17 @@ class PolymorphicModelBase(ModelBase):
if type(manager) == models.manager.ManagerDescriptor:
manager = manager.manager
# As of Django 1.5, the abstract models don't get any managers, only a
# AbstractManagerDescriptor as substitute.
if type(manager) == AbstractManagerDescriptor and base.__name__ == 'PolymorphicModel':
model = manager.model
if key == 'objects':
manager = PolymorphicManager()
manager.model = model
elif key == 'base_objects':
manager = models.Manager()
manager.model = model
if AbstractManagerDescriptor is not None:
# Django 1.4 unconditionally assigned managers to a model. As of Django 1.5 however,
# the abstract models don't get any managers, only a AbstractManagerDescriptor as substitute.

View File

@ -1,15 +1,11 @@
import django
from django.contrib.contenttypes.forms import BaseGenericInlineFormSet, generic_inlineformset_factory
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.forms.models import ModelForm
from .models import BasePolymorphicModelFormSet, polymorphic_child_forms_factory, PolymorphicFormSetChild
try:
from django.contrib.contenttypes.forms import BaseGenericInlineFormSet, generic_inlineformset_factory # Django 1.7+
except ImportError:
from django.contrib.contenttypes.generic import BaseGenericInlineFormSet, generic_inlineformset_factory
class GenericPolymorphicFormSetChild(PolymorphicFormSetChild):
"""

View File

@ -1,3 +1,4 @@
from collections import OrderedDict
import django
from django import forms
@ -7,11 +8,6 @@ from django.forms.models import ModelForm, BaseModelFormSet, BaseInlineFormSet,
from django.utils.functional import cached_property
from .utils import add_media
try:
from collections import OrderedDict
except ImportError:
from django.utils.datastructures import SortedDict as OrderedDict # Python 2.6
class PolymorphicFormSetChild(object):
"""

View File

@ -4,15 +4,10 @@ The manager class for use in the models.
"""
from __future__ import unicode_literals
import warnings
import django
from django.db import models
from django.utils.six import python_2_unicode_compatible
from polymorphic.query import PolymorphicQuerySet
try:
from django.utils.six import python_2_unicode_compatible
except ImportError:
from django.utils.encoding import python_2_unicode_compatible # Django 1.5
__all__ = (
'PolymorphicManager',
@ -32,6 +27,12 @@ class PolymorphicManager(models.Manager):
use_for_related_fields = True
queryset_class = PolymorphicQuerySet
@classmethod
def from_queryset(cls, queryset_class, class_name=None):
manager = super(PolymorphicManager, cls).from_queryset(queryset_class, class_name=class_name)
manager.queryset_class = queryset_class # also set our version, Django uses _queryset_class
return manager
def __init__(self, queryset_class=None, *args, **kwrags):
# Up till polymorphic 0.4, the queryset class could be specified as parameter to __init__.
# However, this doesn't work for related managers which instantiate a new version of this class.
@ -44,18 +45,11 @@ class PolymorphicManager(models.Manager):
super(PolymorphicManager, self).__init__(*args, **kwrags)
def get_queryset(self):
if django.VERSION >= (1, 7):
qs = self.queryset_class(self.model, using=self._db, hints=self._hints)
else:
qs = self.queryset_class(self.model, using=self._db)
qs = self.queryset_class(self.model, using=self._db, hints=self._hints)
if self.model._meta.proxy:
qs = qs.instance_of(self.model)
return qs
# For Django 1.5
if django.VERSION < (1, 7):
get_query_set = get_queryset
def __str__(self):
return '%s (PolymorphicManager) using %s' % (self.__class__.__name__, self.queryset_class.__name__)
@ -71,10 +65,3 @@ class PolymorphicManager(models.Manager):
def get_real_instances(self, base_result_objects=None):
return self.all().get_real_instances(base_result_objects=base_result_objects)
if django.VERSION >= (1, 7):
@classmethod
def from_queryset(cls, queryset_class, class_name=None):
manager = super(PolymorphicManager, cls).from_queryset(queryset_class, class_name=class_name)
manager.queryset_class = queryset_class # also set our version, Django uses _queryset_class
return manager

View File

@ -4,9 +4,9 @@ Seamless Polymorphic Inheritance for Django Models
"""
from __future__ import absolute_import
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.db.utils import DEFAULT_DB_ALIAS
from django.contrib.contenttypes.models import ContentType
from django.utils import six
from .base import PolymorphicModelBase
@ -37,8 +37,10 @@ class PolymorphicModel(six.with_metaclass(PolymorphicModelBase, models.Model)):
# avoid ContentType related field accessor clash (an error emitted by model validation)
#: The model field that stores the :class:`~django.contrib.contenttypes.models.ContentType` reference to the actual class.
polymorphic_ctype = models.ForeignKey(ContentType, null=True, editable=False,
related_name='polymorphic_%(app_label)s.%(class)s_set+')
polymorphic_ctype = models.ForeignKey(
ContentType, null=True, editable=False, on_delete=models.CASCADE,
related_name='polymorphic_%(app_label)s.%(class)s_set+'
)
# some applications want to know the name of the fields that are added to its models
polymorphic_internal_model_fields = ['polymorphic_ctype']
@ -85,11 +87,7 @@ class PolymorphicModel(six.with_metaclass(PolymorphicModelBase, models.Model)):
# so we use the following version, which uses the ContentType manager cache.
# Note that model_class() can return None for stale content types;
# when the content type record still exists but no longer refers to an existing model.
try:
model = ContentType.objects.db_manager(self._state.db).get_for_id(self.polymorphic_ctype_id).model_class()
except AttributeError:
# Django <1.6 workaround
return None
model = ContentType.objects.db_manager(self._state.db).get_for_id(self.polymorphic_ctype_id).model_class()
# Protect against bad imports (dumpdata without --natural) or other
# issues missing with the ContentType models.

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
""" QuerySet for PolymorphicModel
Please see README.rst or DOCS.rst or http://chrisglass.github.com/django_polymorphic/
"""
QuerySet for PolymorphicModel
"""
from __future__ import absolute_import
@ -8,21 +8,16 @@ import copy
from collections import defaultdict
import django
from django.db.models.query import QuerySet, Q
from django.contrib.contenttypes.models import ContentType
from django.db.models.query import Q, QuerySet
from django.utils import six
from .query_translate import translate_polymorphic_filter_definitions_in_kwargs, translate_polymorphic_filter_definitions_in_args
from .query_translate import translate_polymorphic_field_path, translate_polymorphic_Q_object
# chunk-size: maximum number of objects requested per db-request
# by the PolymorphicModelIterable; we use the same chunk size as Django
try:
from django.db.models.query import CHUNK_SIZE # this is 100 for Django 1.1/1.2
except ImportError:
# CHUNK_SIZE was removed in Django 1.6
CHUNK_SIZE = 100
Polymorphic_QuerySet_objects_per_request = CHUNK_SIZE
# by the polymorphic queryset.iterator() implementation
Polymorphic_QuerySet_objects_per_request = 100
def _polymorphic_iterator(queryset, base_iter):
@ -97,14 +92,6 @@ def transmogrify(cls, obj):
###################################################################################
# PolymorphicQuerySet
def _query_annotations(query):
try:
return query.annotations
except AttributeError:
# Django < 1.8
return query.aggregates
class PolymorphicQuerySet(QuerySet):
"""
QuerySet for PolymorphicModel
@ -138,15 +125,13 @@ class PolymorphicQuerySet(QuerySet):
self.polymorphic_deferred_loading[1])
return new
if django.VERSION >= (1, 7):
def as_manager(cls):
# Make sure the Django 1.7 way of creating managers works.
from .managers import PolymorphicManager
manager = PolymorphicManager.from_queryset(cls)()
manager._built_with_as_manager = True
return manager
as_manager.queryset_only = True
as_manager = classmethod(as_manager)
def as_manager(cls):
from .managers import PolymorphicManager
manager = PolymorphicManager.from_queryset(cls)()
manager._built_with_as_manager = True
return manager
as_manager.queryset_only = True
as_manager = classmethod(as_manager)
def non_polymorphic(self):
"""switch off polymorphic behaviour for this query.
@ -244,47 +229,40 @@ class PolymorphicQuerySet(QuerySet):
Modifies kwargs if needed (these are Aggregate objects, we translate the lookup member variable)"""
___lookup_assert_msg = 'PolymorphicModel: annotate()/aggregate(): ___ model lookup supported for keyword arguments only'
if django.VERSION < (1, 8):
def patch_lookup(a):
a.lookup = translate_polymorphic_field_path(self.model, a.lookup)
def patch_lookup(a):
# The field on which the aggregate operates is
# stored inside a complex query expression.
if isinstance(a, Q):
translate_polymorphic_Q_object(self.model, a)
elif hasattr(a, 'get_source_expressions'):
for source_expression in a.get_source_expressions():
if source_expression is not None:
patch_lookup(source_expression)
else:
a.name = translate_polymorphic_field_path(self.model, a.name)
def test___lookup(a):
assert '___' not in a.lookup, ___lookup_assert_msg
else:
def patch_lookup(a):
# With Django > 1.8, the field on which the aggregate operates is
# stored inside a complex query expression.
if isinstance(a, Q):
translate_polymorphic_Q_object(self.model, a)
elif hasattr(a, 'get_source_expressions'):
for source_expression in a.get_source_expressions():
if source_expression is not None:
patch_lookup(source_expression)
else:
a.name = translate_polymorphic_field_path(self.model, a.name)
def test___lookup(a):
""" *args might be complex expressions too in django 1.8 so
the testing for a '___' is rather complex on this one """
if isinstance(a, Q):
def tree_node_test___lookup(my_model, node):
" process all children of this Q node "
for i in range(len(node.children)):
child = node.children[i]
def test___lookup(a):
""" *args might be complex expressions too in django 1.8 so
the testing for a '___' is rather complex on this one """
if isinstance(a, Q):
def tree_node_test___lookup(my_model, node):
" process all children of this Q node "
for i in range(len(node.children)):
child = node.children[i]
if type(child) == tuple:
# this Q object child is a tuple => a kwarg like Q( instance_of=ModelB )
assert '___' not in child[0], ___lookup_assert_msg
else:
# this Q object child is another Q object, recursively process this as well
tree_node_test___lookup(my_model, child)
if type(child) == tuple:
# this Q object child is a tuple => a kwarg like Q( instance_of=ModelB )
assert '___' not in child[0], ___lookup_assert_msg
else:
# this Q object child is another Q object, recursively process this as well
tree_node_test___lookup(my_model, child)
tree_node_test___lookup(self.model, a)
elif hasattr(a, 'get_source_expressions'):
for source_expression in a.get_source_expressions():
test___lookup(source_expression)
else:
assert '___' not in a.name, ___lookup_assert_msg
tree_node_test___lookup(self.model, a)
elif hasattr(a, 'get_source_expressions'):
for source_expression in a.get_source_expressions():
test___lookup(source_expression)
else:
assert '___' not in a.name, ___lookup_assert_msg
for a in args:
test___lookup(a)
@ -438,8 +416,8 @@ class PolymorphicQuerySet(QuerySet):
if real_class != real_concrete_class:
real_object = transmogrify(real_class, real_object)
if _query_annotations(self.query):
for anno_field_name in six.iterkeys(_query_annotations(self.query)):
if self.query.annotations:
for anno_field_name in six.iterkeys(self.query.annotations):
attr = getattr(base_result_objects_by_id[o_pk], anno_field_name)
setattr(real_object, anno_field_name, attr)
@ -454,8 +432,8 @@ class PolymorphicQuerySet(QuerySet):
resultlist = [results[ordered_id] for ordered_id in ordered_id_list if ordered_id in results]
# set polymorphic_annotate_names in all objects (currently just used for debugging/printing)
if _query_annotations(self.query):
annotate_names = list(six.iterkeys(_query_annotations(self.query))) # get annotate field list
if self.query.annotations:
annotate_names = list(six.iterkeys(self.query.annotations)) # get annotate field list
for real_object in resultlist:
real_object.polymorphic_annotate_names = annotate_names

View File

@ -1,32 +1,18 @@
# -*- coding: utf-8 -*-
""" PolymorphicQuerySet support functions
Please see README.rst or DOCS.rst or http://chrisglass.github.com/django_polymorphic/
"""
PolymorphicQuerySet support functions
"""
from __future__ import absolute_import
import copy
import django
from functools import reduce
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.db.models import Q, FieldDoesNotExist
from django.db import models
from django.db.models.fields.related import ForeignObjectRel, RelatedField
from django.db.utils import DEFAULT_DB_ALIAS
from django.utils import six
from django.db.models.fields.related import RelatedField
if django.VERSION < (1, 6):
# There was no common base class in Django 1.5, mention all variants here.
from django.db.models.fields.related import RelatedObject, ManyToOneRel, ManyToManyRel
REL_FIELD_CLASSES = (RelatedField, RelatedObject, ManyToOneRel, ManyToManyRel) # Leaving GenericRel out.
elif django.VERSION < (1, 8):
# As of Django 1.6 there is a ForeignObjectRel.
from django.db.models.fields.related import ForeignObjectRel, RelatedObject
REL_FIELD_CLASSES = (RelatedField, ForeignObjectRel, RelatedObject)
else:
# As of Django 1.8 the base class serves everything. RelatedObject is gone.
from django.db.models.fields.related import ForeignObjectRel
REL_FIELD_CLASSES = (RelatedField, ForeignObjectRel)
###################################################################################
# PolymorphicQuerySet support functions
@ -102,7 +88,7 @@ def translate_polymorphic_filter_definitions_in_args(queryset_model, args, using
"""
if django.VERSION >= (1, 10):
q_objects = [copy.deepcopy(q) for q in args]
elif django.VERSION >= (1, 6):
elif django.VERSION >= (1, 8):
q_objects = [q.clone() for q in args]
else:
q_objects = args # NOTE: edits existing objects in place.
@ -175,17 +161,14 @@ def translate_polymorphic_field_path(queryset_model, field_path):
# Test whether it's actually a regular relation__ _fieldname (the field starting with an _)
# so no tripple ClassName___field was intended.
try:
if django.VERSION >= (1, 8):
# This also retreives M2M relations now (including reverse foreign key relations)
field = queryset_model._meta.get_field(classname)
else:
field = queryset_model._meta.get_field_by_name(classname)[0]
# This also retreives M2M relations now (including reverse foreign key relations)
field = queryset_model._meta.get_field(classname)
if isinstance(field, REL_FIELD_CLASSES):
if isinstance(field, (RelatedField, ForeignObjectRel)):
# Can also test whether the field exists in the related object to avoid ambiguity between
# class names and field names, but that never happens when your class names are in CamelCase.
return field_path # No exception raised, field does exist.
except FieldDoesNotExist:
except models.FieldDoesNotExist:
pass
# function to collect all sub-models, this should be optimized (cached)
@ -261,7 +244,7 @@ def _create_model_filter_Q(modellist, not_instance_of=False, using=DEFAULT_DB_AL
assert False, 'PolymorphicModel: instance_of expects a list of (polymorphic) models or a single (polymorphic) model'
def q_class_with_subclasses(model):
q = Q(polymorphic_ctype=ContentType.objects.db_manager(using).get_for_model(model, for_concrete_model=False))
q = models.Q(polymorphic_ctype=ContentType.objects.db_manager(using).get_for_model(model, for_concrete_model=False))
for subclass in model.__subclasses__():
q = q | q_class_with_subclasses(subclass)
return q

View File

@ -1,13 +1,10 @@
# -*- coding: utf-8 -*-
import django
import re
from django.db import models
from django.utils import six
from django.utils.six import python_2_unicode_compatible
try:
from django.utils.six import python_2_unicode_compatible
except ImportError:
from django.utils.encoding import python_2_unicode_compatible # Django 1.5
RE_DEFERRED = re.compile('_Deferred_.*')
@ -157,11 +154,6 @@ class ShowFieldBase(object):
return '<' + out + '>'
if django.VERSION < (1, 8):
def get_deferred_fields(self):
from django.db.models.query_utils import DeferredAttribute
return set(attr for attr, value in self.__class__.__dict__.items() if isinstance(value, DeferredAttribute))
class ShowFieldType(ShowFieldBase):
""" model mixin that shows the object's class and it's field types """

View File

@ -1,22 +1,16 @@
# -*- coding: utf-8 -*-
import django
import uuid
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.db.models.query import QuerySet
from django.contrib.contenttypes.models import ContentType
from polymorphic.managers import PolymorphicManager
from polymorphic.models import PolymorphicModel
from polymorphic.query import PolymorphicQuerySet
from polymorphic.showfields import ShowFieldContent, ShowFieldType, ShowFieldTypeAndContent
try:
from django.db.models import UUIDField
except ImportError:
# django<1.8
from polymorphic.tools_for_tests import UUIDField
class PlainA(models.Model):
field1 = models.CharField(max_length=10)
@ -113,7 +107,7 @@ class Enhance_Inherit(Enhance_Base, Enhance_Plain):
class RelationBase(ShowFieldTypeAndContent, PolymorphicModel):
field_base = models.CharField(max_length=10)
fk = models.ForeignKey('self', null=True, related_name='relationbase_set')
fk = models.ForeignKey('self', on_delete=models.CASCADE, null=True, related_name='relationbase_set')
m2m = models.ManyToManyField('self')
@ -134,7 +128,7 @@ class RelatingModel(models.Model):
class One2OneRelatingModel(PolymorphicModel):
one2one = models.OneToOneField(Model2A)
one2one = models.OneToOneField(Model2A, on_delete=models.CASCADE)
field1 = models.CharField(max_length=10)
@ -148,7 +142,7 @@ class ModelUnderRelParent(PolymorphicModel):
class ModelUnderRelChild(PolymorphicModel):
parent = models.ForeignKey(ModelUnderRelParent, related_name='children')
parent = models.ForeignKey(ModelUnderRelParent, on_delete=models.CASCADE, related_name='children')
_private2 = models.CharField(max_length=10)
@ -167,9 +161,6 @@ class MyManager(PolymorphicManager):
def my_queryset_foo(self):
return self.all().my_queryset_foo()
# Django <= 1.5 compatibility
get_query_set = get_queryset
class ModelWithMyManager(ShowFieldTypeAndContent, Model2A):
objects = MyManager()
@ -188,10 +179,9 @@ class ModelWithMyManagerDefault(ShowFieldTypeAndContent, Model2A):
field4 = models.CharField(max_length=10)
if django.VERSION >= (1, 7):
class ModelWithMyManager2(ShowFieldTypeAndContent, Model2A):
objects = MyManagerQuerySet.as_manager()
field4 = models.CharField(max_length=10)
class ModelWithMyManager2(ShowFieldTypeAndContent, Model2A):
objects = MyManagerQuerySet.as_manager()
field4 = models.CharField(max_length=10)
class MROBase1(ShowFieldType, PolymorphicModel):
@ -218,7 +208,7 @@ class ParentModelWithManager(PolymorphicModel):
class ChildModelWithManager(PolymorphicModel):
# Also test whether foreign keys receive the manager:
fk = models.ForeignKey(ParentModelWithManager, related_name='childmodel_set')
fk = models.ForeignKey(ParentModelWithManager, on_delete=models.CASCADE, related_name='childmodel_set')
objects = MyManager()
@ -236,16 +226,13 @@ class PlainMyManager(models.Manager):
def get_queryset(self):
return PlainMyManagerQuerySet(self.model, using=self._db)
# Django <= 1.5 compatibility
get_query_set = get_queryset
class PlainParentModelWithManager(models.Model):
pass
class PlainChildModelWithManager(models.Model):
fk = models.ForeignKey(PlainParentModelWithManager, related_name='childmodel_set')
fk = models.ForeignKey(PlainParentModelWithManager, on_delete=models.CASCADE, related_name='childmodel_set')
objects = PlainMyManager()
@ -277,12 +264,12 @@ class BlogB(BlogBase):
class BlogEntry(ShowFieldTypeAndContent, PolymorphicModel):
blog = models.ForeignKey(BlogA)
blog = models.ForeignKey(BlogA, on_delete=models.CASCADE)
text = models.CharField(max_length=10)
class BlogEntry_limit_choices_to(ShowFieldTypeAndContent, PolymorphicModel):
blog = models.ForeignKey(BlogBase)
blog = models.ForeignKey(BlogBase, on_delete=models.CASCADE)
text = models.CharField(max_length=10)
@ -319,7 +306,7 @@ class Bottom(Middle):
class UUIDProject(ShowFieldTypeAndContent, PolymorphicModel):
uuid_primary_key = UUIDField(primary_key=True, default=uuid.uuid1)
uuid_primary_key = models.UUIDField(primary_key=True, default=uuid.uuid1)
topic = models.CharField(max_length=30)
@ -332,7 +319,7 @@ class UUIDResearchProject(UUIDProject):
class UUIDPlainA(models.Model):
uuid_primary_key = UUIDField(primary_key=True, default=uuid.uuid1)
uuid_primary_key = models.UUIDField(primary_key=True, default=uuid.uuid1)
field1 = models.CharField(max_length=10)
@ -388,13 +375,15 @@ class ProxyModelB(ProxyModelBase):
# with related field 'ContentType.relatednameclash_set'." (reported by Andrew Ingram)
# fixed with related_name
class RelatedNameClash(ShowFieldType, PolymorphicModel):
ctype = models.ForeignKey(ContentType, null=True, editable=False)
ctype = models.ForeignKey(ContentType, on_delete=models.CASCADE, null=True, editable=False)
# class with a parent_link to superclass, and a related_name back to subclass
class TestParentLinkAndRelatedName(ModelShow1_plain):
superclass = models.OneToOneField(ModelShow1_plain, parent_link=True, related_name='related_name_subclass')
superclass = models.OneToOneField(
ModelShow1_plain, on_delete=models.CASCADE, parent_link=True, related_name='related_name_subclass'
)
class CustomPkBase(ShowFieldTypeAndContent, PolymorphicModel):

View File

@ -1,25 +1,15 @@
from __future__ import print_function
import django
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from django.db.models import Q
from django.test import TestCase
from polymorphic.tests import * # all models
try:
from unittest import skipIf
except ImportError:
# python<2.7
from django.utils.unittest import skipIf
class MultipleDatabasesTests(TestCase):
multi_db = True
@skipIf(django.VERSION < (1, 5,), "This test needs Django >=1.5")
def test_save_to_non_default_database(self):
Model2A.objects.db_manager('secondary').create(field1='A1')
Model2C(field1='C1', field2='C2', field3='C3').save(using='secondary')

View File

@ -1,22 +1,11 @@
from __future__ import print_function
import re
import django
from django.db.models import Case, Count, Q, When
from django.test import TestCase
from django.db.models import Q, Count
from django.utils import six
from polymorphic.tests import * # all models
from polymorphic.contrib.guardian import get_polymorphic_base_content_type
try:
from unittest import skipIf
except ImportError:
# python<2.7
from django.utils.unittest import skipIf
if django.VERSION >= (1, 8):
from django.db.models import Case, When
from polymorphic.tests import * # all models
class PolymorphicTests(TestCase):
@ -188,11 +177,6 @@ class PolymorphicTests(TestCase):
'<Model2D: id 4, field1 (CharField), field2 (CharField), field3 (CharField), field4 (CharField), '
'deferred[field2,field3,field4,model2a_ptr_id,model2b_ptr_id]>')
# A bug in Django 1.4 prevents using defer across reverse relations
# <https://code.djangoproject.com/ticket/14694>. Since polymorphic
# uses reverse relations to traverse down model inheritance, deferring
# fields in child models will not work in Django 1.4.
@skipIf(django.VERSION < (1, 5), "Django 1.4 does not support defer on related fields")
def test_defer_related_fields(self):
self.create_model2abcd()
@ -424,7 +408,6 @@ class PolymorphicTests(TestCase):
self.assertEqual(repr(objects[0]), '<Model2B: id 2, field1 (CharField), field2 (CharField)>')
self.assertEqual(repr(objects[1]), '<Model2C: id 3, field1 (CharField), field2 (CharField), field3 (CharField)>')
@skipIf(django.VERSION < (1, 6), "Django 1.4 and 1.5 don't support q.clone()")
def test_query_filter_exclude_is_immutable(self):
# given
q_to_reuse = Q(Model2B___field2='something')
@ -564,7 +547,6 @@ class PolymorphicTests(TestCase):
self.assertIs(type(ModelWithMyManagerDefault._default_manager), MyManager)
self.assertIs(type(ModelWithMyManagerDefault.base_objects), models.Manager)
@skipIf(django.VERSION < (1, 7), "This test needs Django 1.7+")
def test_user_defined_queryset_as_manager(self):
self.create_model2abcd()
ModelWithMyManager2.objects.create(field1='D1a', field4='D4a')
@ -751,7 +733,6 @@ class PolymorphicTests(TestCase):
lambda: Model2A.objects.aggregate(Count('Model2B___field2'))
)
@skipIf(django.VERSION < (1, 8,), "This test needs Django >=1.8")
def test_polymorphic__complex_aggregate(self):
""" test (complex expression on) aggregate (should work for annotate either) """
@ -777,7 +758,6 @@ class PolymorphicTests(TestCase):
with self.assertRaisesMessage(AssertionError, 'PolymorphicModel: annotate()/aggregate(): ___ model lookup supported for keyword arguments only'):
Model2A.objects.aggregate(ComplexAgg('Model2B___field2'))
@skipIf(django.VERSION < (1, 8,), "This test needs Django >=1.8")
def test_polymorphic__expressions(self):
from django.db.models.functions import Concat

View File

@ -1,144 +0,0 @@
# Compatibility module for Django < 1.8
import uuid
from django import forms
from django.db import models
from django.utils.encoding import smart_text
from django.utils import six
class UUIDVersionError(Exception):
pass
class UUIDField(six.with_metaclass(models.SubfieldBase, models.CharField)):
"""Encode and stores a Python uuid.UUID in a manner that is appropriate
for the given datatabase that we are using.
For sqlite3 or MySQL we save it as a 36-character string value
For PostgreSQL we save it as a uuid field
This class supports type 1, 2, 4, and 5 UUID's.
"""
_CREATE_COLUMN_TYPES = {
'postgresql_psycopg2': 'uuid',
'postgresql': 'uuid'
}
def __init__(self, verbose_name=None, name=None, auto=True, version=1, node=None, clock_seq=None, namespace=None, **kwargs):
"""Contruct a UUIDField.
@param verbose_name: Optional verbose name to use in place of what
Django would assign.
@param name: Override Django's name assignment
@param auto: If True, create a UUID value if one is not specified.
@param version: By default we create a version 1 UUID.
@param node: Used for version 1 UUID's. If not supplied, then the uuid.getnode() function is called to obtain it. This can be slow.
@param clock_seq: Used for version 1 UUID's. If not supplied a random 14-bit sequence number is chosen
@param namespace: Required for version 3 and version 5 UUID's.
@param name: Required for version4 and version 5 UUID's.
See Also:
- Python Library Reference, section 18.16 for more information.
- RFC 4122, "A Universally Unique IDentifier (UUID) URN Namespace"
If you want to use one of these as a primary key for a Django
model, do this::
id = UUIDField(primary_key=True)
This will currently I{not} work with Jython because PostgreSQL support
in Jython is not working for uuid column types.
"""
self.max_length = 36
kwargs['max_length'] = self.max_length
if auto:
kwargs['blank'] = True
kwargs.setdefault('editable', False)
self.auto = auto
self.version = version
if version == 1:
self.node, self.clock_seq = node, clock_seq
elif version == 3 or version == 5:
self.namespace, self.name = namespace, name
super(UUIDField, self).__init__(verbose_name=verbose_name,
name=name, **kwargs)
def create_uuid(self):
if not self.version or self.version == 4:
return uuid.uuid4()
elif self.version == 1:
return uuid.uuid1(self.node, self.clock_seq)
elif self.version == 2:
raise UUIDVersionError("UUID version 2 is not supported.")
elif self.version == 3:
return uuid.uuid3(self.namespace, self.name)
elif self.version == 5:
return uuid.uuid5(self.namespace, self.name)
else:
raise UUIDVersionError("UUID version %s is not valid." % self.version)
def db_type(self, connection):
from django.conf import settings
full_database_type = settings.DATABASES['default']['ENGINE']
database_type = full_database_type.split('.')[-1]
return UUIDField._CREATE_COLUMN_TYPES.get(database_type, "char(%s)" % self.max_length)
def to_python(self, value):
"""Return a uuid.UUID instance from the value returned by the database."""
#
# This is the proper way... But this doesn't work correctly when
# working with an inherited model
#
if not value:
return None
if isinstance(value, uuid.UUID):
return value
# attempt to parse a UUID
return uuid.UUID(smart_text(value))
#
# If I do the following (returning a String instead of a UUID
# instance), everything works.
#
# if not value:
# return None
# if isinstance(value, uuid.UUID):
# return smart_text(value)
# else:
# return value
def pre_save(self, model_instance, add):
if self.auto and add:
value = self.create_uuid()
setattr(model_instance, self.attname, value)
else:
value = super(UUIDField, self).pre_save(model_instance, add)
if self.auto and not value:
value = self.create_uuid()
setattr(model_instance, self.attname, value)
return value
def get_db_prep_value(self, value, connection, prepared):
"""Casts uuid.UUID values into the format expected by the back end for use in queries"""
if isinstance(value, uuid.UUID):
return smart_text(value)
return value
def value_to_string(self, obj):
val = self._get_val_from_obj(obj)
if val is None:
data = ''
else:
data = smart_text(val)
return data
def formfield(self, **kwargs):
defaults = {
'form_class': forms.CharField,
'max_length': self.max_length
}
defaults.update(kwargs)
return super(UUIDField, self).formfield(**defaults)

View File

@ -1,12 +1,10 @@
#!/usr/bin/env python
import os
import sys
import django
from os.path import abspath, dirname
import django
from django.conf import settings
from django.core.management import execute_from_command_line
from django.conf import settings, global_settings as default_settings
from os.path import dirname, realpath, abspath
# Give feedback on used versions
@ -17,42 +15,6 @@ sys.stderr.write('Using Django version {0} from {1}\n'.format(
)
if not settings.configured:
if django.VERSION >= (1, 8):
template_settings = dict(
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': (),
'OPTIONS': {
'loaders': (
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
),
'context_processors': (
'django.template.context_processors.debug',
'django.template.context_processors.i18n',
'django.template.context_processors.media',
'django.template.context_processors.request',
'django.template.context_processors.static',
'django.contrib.messages.context_processors.messages',
'django.contrib.auth.context_processors.auth',
),
},
},
]
)
else:
template_settings = dict(
TEMPLATE_LOADERS = (
'django.template.loaders.app_directories.Loader',
'django.template.loaders.filesystem.Loader',
),
TEMPLATE_CONTEXT_PROCESSORS = list(default_settings.TEMPLATE_CONTEXT_PROCESSORS) + [
'django.contrib.messages.context_processors.messages',
'django.core.context_processors.request',
],
)
settings.configure(
DEBUG=False,
DATABASES={
@ -65,8 +27,8 @@ if not settings.configured:
'NAME': ':memory:'
}
},
TEST_RUNNER = 'django.test.runner.DiscoverRunner' if django.VERSION >= (1, 7) else 'django.test.simple.DjangoTestSuiteRunner',
INSTALLED_APPS = (
TEST_RUNNER="django.test.runner.DiscoverRunner",
INSTALLED_APPS=(
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.messages',
@ -74,11 +36,31 @@ if not settings.configured:
'django.contrib.admin',
'polymorphic',
),
MIDDLEWARE_CLASSES = (),
SITE_ID = 3,
**template_settings
MIDDLEWARE_CLASSES=(),
SITE_ID=3,
TEMPLATES=[{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": (),
"OPTIONS": {
"loaders": (
"django.template.loaders.filesystem.Loader",
"django.template.loaders.app_directories.Loader",
),
"context_processors": (
"django.template.context_processors.debug",
"django.template.context_processors.i18n",
"django.template.context_processors.media",
"django.template.context_processors.request",
"django.template.context_processors.static",
"django.contrib.messages.context_processors.messages",
"django.contrib.auth.context_processors.auth",
),
},
},
]
)
DEFAULT_TEST_APPS = [
'polymorphic',
]
@ -90,5 +72,6 @@ def runtests():
argv = sys.argv[:1] + ['test', '--traceback'] + other_args + test_apps
execute_from_command_line(argv)
if __name__ == '__main__':
runtests()

View File

@ -1,10 +1,9 @@
#!/usr/bin/env python
from setuptools import setup, find_packages
from os import path
import codecs
import os
import re
import sys
from os import path
from setuptools import find_packages, setup
def read(*parts):
@ -47,18 +46,14 @@ setup(
'License :: OSI Approved :: BSD License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.2',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Framework :: Django',
'Framework :: Django :: 1.4',
'Framework :: Django :: 1.5',
'Framework :: Django :: 1.6',
'Framework :: Django :: 1.7',
'Framework :: Django :: 1.8',
'Framework :: Django :: 1.10',
'Framework :: Django :: 1.11',
'Topic :: Software Development :: Libraries :: Python Modules',
]
)

30
tox.ini
View File

@ -1,30 +1,28 @@
[tox]
envlist=
py26-django{14,15,16},
py27-django{14,15,16,17,18,19,110,111},
py32-django{15,16,17,18},
py33-django{15,16,17,18},
py34-django{15,16,17,18,19,110,111},
py35-django{18,19,110,111,dev},
py36-django{111,dev},
docs,
envlist =
py27-django{18,110,111}
py34-django{18,110,111}
py35-django{18,110,111,dev}
py36-django{111,dev}
docs
[testenv]
setenv =
PYTHONWARNINGS = all
deps =
coverage == 3.6
django14: Django >= 1.4, < 1.5
django15: Django >= 1.5, < 1.6
django16: Django >= 1.6, < 1.7
django17: Django >= 1.7, < 1.8
django18: Django >= 1.8, < 1.9
django19: Django >= 1.9, < 1.10
django110: Django >= 1.10, < 1.11
django111: Django >= 1.11, < 1.12
djangodev: https://github.com/django/django/tarball/master
commands=
commands =
coverage run --source polymorphic runtests.py
[testenv:docs]
deps=Sphinx
deps =
Sphinx
sphinx_rtd_theme
Django
django-extra-views
changedir = docs
commands = sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html