Merge branch 'master' into lookups_3

This commit is contained in:
Anssi Kääriäinen 2014-01-11 13:21:01 +02:00
commit 90e7004ec1
378 changed files with 6075 additions and 4298 deletions

View File

@ -15,7 +15,8 @@ Extensive contribution guidelines are available in the repository at
https://docs.djangoproject.com/en/dev/internals/contributing/ https://docs.djangoproject.com/en/dev/internals/contributing/
**Warning: pull requests are ignored!** `File a ticket`__ to suggest changes. **Warning: non-trivial pull requests (anything more than fixing a typo) without
Trac tickets will be closed!** `Please file a ticket`__ to suggest changes.
__ https://code.djangoproject.com/newticket __ https://code.djangoproject.com/newticket

View File

@ -2,8 +2,8 @@ Django is a high-level Python Web framework that encourages rapid development
and clean, pragmatic design. Thanks for checking it out. and clean, pragmatic design. Thanks for checking it out.
All documentation is in the "docs" directory and online at All documentation is in the "docs" directory and online at
http://docs.djangoproject.com/en/dev/. If you're just getting started, here's https://docs.djangoproject.com/en/stable/. If you're just getting started,
how we recommend you read the docs: here's how we recommend you read the docs:
* First, read docs/intro/install.txt for instructions on installing Django. * First, read docs/intro/install.txt for instructions on installing Django.
@ -19,11 +19,9 @@ how we recommend you read the docs:
* See docs/README for instructions on building an HTML version of the docs. * See docs/README for instructions on building an HTML version of the docs.
Docs are updated rigorously. If you find any problems in the docs, or think they Docs are updated rigorously. If you find any problems in the docs, or think
should be clarified in any way, please take 30 seconds to fill out a ticket they should be clarified in any way, please take 30 seconds to fill out a
here: ticket here: https://code.djangoproject.com/newticket
http://code.djangoproject.com/newticket
To get more help: To get more help:
@ -31,11 +29,11 @@ To get more help:
there. Read the archives at http://django-irc-logs.com/. there. Read the archives at http://django-irc-logs.com/.
* Join the django-users mailing list, or read the archives, at * Join the django-users mailing list, or read the archives, at
http://groups.google.com/group/django-users. https://groups.google.com/group/django-users.
To contribute to Django: To contribute to Django:
* Check out http://www.djangoproject.com/community/ for information about * Check out https://www.djangoproject.com/community/ for information about
getting involved. getting involved.
To run Django's test suite: To run Django's test suite:

View File

@ -6,3 +6,16 @@ def get_version(*args, **kwargs):
# Only import if it's actually called. # Only import if it's actually called.
from django.utils.version import get_version from django.utils.version import get_version
return get_version(*args, **kwargs) return get_version(*args, **kwargs)
def setup():
"""
Configure the settings (this happens as a side effect of accessing the
first setting), configure logging and populate the app registry.
"""
from django.apps import apps
from django.conf import settings
from django.utils.log import configure_logging
configure_logging(settings.LOGGING_CONFIG, settings.LOGGING)
apps.populate(settings.INSTALLED_APPS)

2
django/apps/__init__.py Normal file
View File

@ -0,0 +1,2 @@
from .base import AppConfig # NOQA
from .registry import apps # NOQA

165
django/apps/base.py Normal file
View File

@ -0,0 +1,165 @@
from importlib import import_module
from django.core.exceptions import ImproperlyConfigured
from django.utils.module_loading import module_has_submodule
from django.utils._os import upath
MODELS_MODULE_NAME = 'models'
class AppConfig(object):
"""
Class representing a Django application and its configuration.
"""
def __init__(self, app_name, app_module):
# Full Python path to the application eg. 'django.contrib.admin'.
self.name = app_name
# Root module for the application eg. <module 'django.contrib.admin'
# from 'django/contrib/admin/__init__.pyc'>.
self.module = app_module
# The following attributes could be defined at the class level in a
# subclass, hence the test-and-set pattern.
# Last component of the Python path to the application eg. 'admin'.
# This value must be unique across a Django project.
if not hasattr(self, 'label'):
self.label = app_name.rpartition(".")[2]
# Human-readable name for the application eg. "Admin".
if not hasattr(self, 'verbose_name'):
self.verbose_name = self.label.title()
# Filesystem path to the application directory eg.
# u'/usr/lib/python2.7/dist-packages/django/contrib/admin'. May be
# None if the application isn't a bona fide package eg. if it's an
# egg. Otherwise it's a unicode on Python 2 and a str on Python 3.
if not hasattr(self, 'path'):
try:
self.path = upath(app_module.__path__[0])
except AttributeError:
self.path = None
# Module containing models eg. <module 'django.contrib.admin.models'
# from 'django/contrib/admin/models.pyc'>. Set by import_models().
# None if the application doesn't have a models module.
self.models_module = None
# Mapping of lower case model names to model classes. Initally set to
# None to prevent accidental access before import_models() runs.
self.models = None
def __repr__(self):
return '<%s: %s>' % (self.__class__.__name__, self.label)
@classmethod
def create(cls, entry):
"""
Factory that creates an app config from an entry in INSTALLED_APPS.
"""
try:
# If import_module succeeds, entry is a path to an app module.
# Otherwise, entry is a path to an app config class or an error.
module = import_module(entry)
except ImportError:
# Avoid django.utils.module_loading.import_by_path because it
# masks errors -- it reraises ImportError as ImproperlyConfigured.
mod_path, _, cls_name = entry.rpartition('.')
# Raise the original exception when entry cannot be a path to an
# app config class.
if not mod_path:
raise
mod = import_module(mod_path)
try:
cls = getattr(mod, cls_name)
except AttributeError:
# Emulate the error that "from <mod_path> import <cls_name>"
# would raise when <mod_path> exists but not <cls_name>, with
# more context (Python just says "cannot import name ...").
raise ImportError(
"cannot import name '%s' from '%s'" % (cls_name, mod_path))
# Check for obvious errors. (This check prevents duck typing, but
# it could be removed if it became a problem in practice.)
if not issubclass(cls, AppConfig):
raise ImproperlyConfigured(
"'%s' isn't a subclass of AppConfig." % entry)
# Obtain app name here rather than in AppClass.__init__ to keep
# all error checking for entries in INSTALLED_APPS in one place.
try:
app_name = cls.name
except AttributeError:
raise ImproperlyConfigured(
"'%s' must supply a name attribute." % entry)
# Ensure app_names points to a valid module.
app_module = import_module(app_name)
# Entry is a path to an app config class.
return cls(app_name, app_module)
else:
# Entry is a path to an app module.
return cls(entry, module)
def get_model(self, model_name):
"""
Returns the model with the given case-insensitive model_name.
Raises LookupError if no model exists with this name.
"""
if self.models is None:
raise LookupError(
"App '%s' doesn't have any models." % self.label)
try:
return self.models[model_name.lower()]
except KeyError:
raise LookupError(
"App '%s' doesn't have a '%s' model." % (self.label, model_name))
def get_models(self, include_auto_created=False,
include_deferred=False, include_swapped=False):
"""
Returns an iterable of models.
By default, the following models aren't included:
- auto-created models for many-to-many relations without
an explicit intermediate table,
- models created to satisfy deferred attribute queries,
- models that have been swapped out.
Set the corresponding keyword argument to True to include such models.
Keyword arguments aren't documented; they're a private API.
"""
for model in self.models.values():
if model._deferred and not include_deferred:
continue
if model._meta.auto_created and not include_auto_created:
continue
if model._meta.swapped and not include_swapped:
continue
yield model
def import_models(self, all_models):
# Dictionary of models for this app, primarily maintained in the
# 'all_models' attribute of the Apps this AppConfig is attached to.
# Injected as a parameter because it gets populated when models are
# imported, which might happen before populate() imports models.
self.models = all_models
if module_has_submodule(self.module, MODELS_MODULE_NAME):
models_module_name = '%s.%s' % (self.name, MODELS_MODULE_NAME)
self.models_module = import_module(models_module_name)
def ready(self):
"""
Override this method in subclasses to run code when Django starts.
"""

413
django/apps/registry.py Normal file
View File

@ -0,0 +1,413 @@
from collections import Counter, defaultdict, OrderedDict
import os
import sys
import warnings
from django.core.exceptions import ImproperlyConfigured
from django.utils import lru_cache
from django.utils.module_loading import import_lock
from django.utils._os import upath
from .base import AppConfig
class Apps(object):
"""
A registry that stores the configuration of installed applications.
It also keeps track of models eg. to provide reverse-relations.
"""
def __init__(self, installed_apps=()):
# installed_apps is set to None when creating the master registry
# because it cannot be populated at that point. Other registries must
# provide a list of installed apps and are populated immediately.
if installed_apps is None and hasattr(sys.modules[__name__], 'apps'):
raise RuntimeError("You must supply an installed_apps argument.")
# Mapping of app labels => model names => model classes. Every time a
# model is imported, ModelBase.__new__ calls apps.register_model which
# creates an entry in all_models. All imported models are registered,
# regardless of whether they're defined in an installed application
# and whether the registry has been populated. Since it isn't possible
# to reimport a module safely (it could reexecute initialization code)
# all_models is never overridden or reset.
self.all_models = defaultdict(OrderedDict)
# Mapping of labels to AppConfig instances for installed apps.
self.app_configs = OrderedDict()
# Stack of app_configs. Used to store the current state in
# set_available_apps and set_installed_apps.
self.stored_app_configs = []
# Whether the registry is populated.
self.ready = False
# Pending lookups for lazy relations.
self._pending_lookups = {}
# Populate apps and models, unless it's the master registry.
if installed_apps is not None:
self.populate(installed_apps)
def populate(self, installed_apps=None):
"""
Loads application configurations and models.
This method imports each application module and then each model module.
It is thread safe and idempotent, but not reentrant.
"""
if self.ready:
return
# Since populate() may be a side effect of imports, and since it will
# itself import modules, an ABBA deadlock between threads would be
# possible if we didn't take the import lock. See #18251.
with import_lock():
if self.ready:
return
# app_config should be pristine, otherwise the code below won't
# guarantee that the order matches the order in INSTALLED_APPS.
if self.app_configs:
raise RuntimeError("populate() isn't reentrant")
# Load app configs and app modules.
for entry in installed_apps:
if isinstance(entry, AppConfig):
app_config = entry
else:
app_config = AppConfig.create(entry)
if app_config.label in self.app_configs:
raise ImproperlyConfigured(
"Application labels aren't unique, "
"duplicates: %s" % app_config.label)
self.app_configs[app_config.label] = app_config
# Check for duplicate app names.
counts = Counter(
app_config.name for app_config in self.app_configs.values())
duplicates = [
name for name, count in counts.most_common() if count > 1]
if duplicates:
raise ImproperlyConfigured(
"Application names aren't unique, "
"duplicates: %s" % ", ".join(duplicates))
# Load models.
for app_config in self.app_configs.values():
all_models = self.all_models[app_config.label]
app_config.import_models(all_models)
self.clear_cache()
self.ready = True
for app_config in self.get_app_configs():
app_config.ready()
def check_ready(self):
"""
Raises an exception if the registry isn't ready.
"""
if not self.ready:
raise RuntimeError("App registry isn't ready yet.")
def get_app_configs(self):
"""
Imports applications and returns an iterable of app configs.
"""
self.check_ready()
return self.app_configs.values()
def get_app_config(self, app_label):
"""
Imports applications and returns an app config for the given label.
Raises LookupError if no application exists with this label.
"""
self.check_ready()
try:
return self.app_configs[app_label]
except KeyError:
raise LookupError("No installed app with label '%s'." % app_label)
# This method is performance-critical at least for Django's test suite.
@lru_cache.lru_cache(maxsize=None)
def get_models(self, app_mod=None, include_auto_created=False,
include_deferred=False, include_swapped=False):
"""
Returns a list of all installed models.
By default, the following models aren't included:
- auto-created models for many-to-many relations without
an explicit intermediate table,
- models created to satisfy deferred attribute queries,
- models that have been swapped out.
Set the corresponding keyword argument to True to include such models.
"""
self.check_ready()
if app_mod:
warnings.warn(
"The app_mod argument of get_models is deprecated.",
PendingDeprecationWarning, stacklevel=2)
app_label = app_mod.__name__.split('.')[-2]
try:
return list(self.get_app_config(app_label).get_models(
include_auto_created, include_deferred, include_swapped))
except LookupError:
return []
result = []
for app_config in self.app_configs.values():
result.extend(list(app_config.get_models(
include_auto_created, include_deferred, include_swapped)))
return result
def get_model(self, app_label, model_name):
"""
Returns the model matching the given app_label and model_name.
model_name is case-insensitive.
Raises LookupError if no application exists with this label, or no
model exists with this name in the application.
"""
self.check_ready()
return self.get_app_config(app_label).get_model(model_name.lower())
def register_model(self, app_label, model):
# Since this method is called when models are imported, it cannot
# perform imports because of the risk of import loops. It mustn't
# call get_app_config().
model_name = model._meta.model_name
app_models = self.all_models[app_label]
if model_name in app_models:
raise RuntimeError(
"Conflicting '%s' models in application '%s': %s and %s." %
(model_name, app_label, app_models[model_name], model))
app_models[model_name] = model
self.clear_cache()
def is_installed(self, app_name):
"""
Checks whether an application with this name exists in the registry.
app_name is the full name of the app eg. 'django.contrib.admin'.
It's safe to call this method at import time, even while the registry
is being populated. It returns False for apps that aren't loaded yet.
"""
app_config = self.app_configs.get(app_name.rpartition(".")[2])
return app_config is not None and app_config.name == app_name
def get_containing_app_config(self, object_name):
"""
Look for an app config containing a given object.
object_name is the dotted Python path to the object.
Returns the app config for the inner application in case of nesting.
Returns None if the object isn't in any registered app config.
It's safe to call this method at import time, even while the registry
is being populated.
"""
candidates = []
for app_config in self.app_configs.values():
if object_name.startswith(app_config.name):
subpath = object_name[len(app_config.name):]
if subpath == '' or subpath[0] == '.':
candidates.append(app_config)
if candidates:
return sorted(candidates, key=lambda ac: -len(ac.name))[0]
def get_registered_model(self, app_label, model_name):
"""
Similar to get_model(), but doesn't require that an app exists with
the given app_label.
It's safe to call this method at import time, even while the registry
is being populated.
"""
model = self.all_models[app_label].get(model_name.lower())
if model is None:
raise LookupError(
"Model '%s.%s' not registered." % (app_label, model_name))
return model
def set_available_apps(self, available):
"""
Restricts the set of installed apps used by get_app_config[s].
available must be an iterable of application names.
set_available_apps() must be balanced with unset_available_apps().
Primarily used for performance optimization in TransactionTestCase.
This method is safe is the sense that it doesn't trigger any imports.
"""
available = set(available)
installed = set(app_config.name for app_config in self.get_app_configs())
if not available.issubset(installed):
raise ValueError("Available apps isn't a subset of installed "
"apps, extra apps: %s" % ", ".join(available - installed))
self.stored_app_configs.append(self.app_configs)
self.app_configs = OrderedDict(
(label, app_config)
for label, app_config in self.app_configs.items()
if app_config.name in available)
self.clear_cache()
def unset_available_apps(self):
"""
Cancels a previous call to set_available_apps().
"""
self.app_configs = self.stored_app_configs.pop()
self.clear_cache()
def set_installed_apps(self, installed):
"""
Enables a different set of installed apps for get_app_config[s].
installed must be an iterable in the same format as INSTALLED_APPS.
set_installed_apps() must be balanced with unset_installed_apps(),
even if it exits with an exception.
Primarily used as a receiver of the setting_changed signal in tests.
This method may trigger new imports, which may add new models to the
registry of all imported models. They will stay in the registry even
after unset_installed_apps(). Since it isn't possible to replay
imports safely (eg. that could lead to registering listeners twice),
models are registered when they're imported and never removed.
"""
self.check_ready()
self.stored_app_configs.append(self.app_configs)
self.app_configs = OrderedDict()
self.ready = False
self.clear_cache()
self.populate(installed)
def unset_installed_apps(self):
"""
Cancels a previous call to set_installed_apps().
"""
self.app_configs = self.stored_app_configs.pop()
self.ready = True
self.clear_cache()
def clear_cache(self):
"""
Clears all internal caches, for methods that alter the app registry.
This is mostly used in tests.
"""
self.get_models.cache_clear()
### DEPRECATED METHODS GO BELOW THIS LINE ###
def load_app(self, app_name):
"""
Loads the app with the provided fully qualified name, and returns the
model module.
"""
warnings.warn(
"load_app(app_name) is deprecated.",
PendingDeprecationWarning, stacklevel=2)
app_config = AppConfig.create(app_name)
app_config.import_models(self.all_models[app_config.label])
self.app_configs[app_config.label] = app_config
self.clear_cache()
return app_config.models_module
def app_cache_ready(self):
warnings.warn(
"app_cache_ready() is deprecated in favor of the ready property.",
PendingDeprecationWarning, stacklevel=2)
return self.ready
def get_app(self, app_label):
"""
Returns the module containing the models for the given app_label.
"""
warnings.warn(
"get_app_config(app_label).models_module supersedes get_app(app_label).",
PendingDeprecationWarning, stacklevel=2)
try:
models_module = self.get_app_config(app_label).models_module
except LookupError as exc:
# Change the exception type for backwards compatibility.
raise ImproperlyConfigured(*exc.args)
if models_module is None:
raise ImproperlyConfigured(
"App '%s' doesn't have a models module." % app_label)
return models_module
def get_apps(self):
"""
Returns a list of all installed modules that contain models.
"""
warnings.warn(
"[a.models_module for a in get_app_configs()] supersedes get_apps().",
PendingDeprecationWarning, stacklevel=2)
app_configs = self.get_app_configs()
return [app_config.models_module for app_config in app_configs
if app_config.models_module is not None]
def _get_app_package(self, app):
return '.'.join(app.__name__.split('.')[:-1])
def get_app_package(self, app_label):
warnings.warn(
"get_app_config(label).name supersedes get_app_package(label).",
PendingDeprecationWarning, stacklevel=2)
return self._get_app_package(self.get_app(app_label))
def _get_app_path(self, app):
if hasattr(app, '__path__'): # models/__init__.py package
app_path = app.__path__[0]
else: # models.py module
app_path = app.__file__
return os.path.dirname(upath(app_path))
def get_app_path(self, app_label):
warnings.warn(
"get_app_config(label).path supersedes get_app_path(label).",
PendingDeprecationWarning, stacklevel=2)
return self._get_app_path(self.get_app(app_label))
def get_app_paths(self):
"""
Returns a list of paths to all installed apps.
Useful for discovering files at conventional locations inside apps
(static files, templates, etc.)
"""
warnings.warn(
"[a.path for a in get_app_configs()] supersedes get_app_paths().",
PendingDeprecationWarning, stacklevel=2)
self.check_ready()
app_paths = []
for app in self.get_apps():
app_paths.append(self._get_app_path(app))
return app_paths
def register_models(self, app_label, *models):
"""
Register a set of models as belonging to an app.
"""
warnings.warn(
"register_models(app_label, *models) is deprecated.",
PendingDeprecationWarning, stacklevel=2)
for model in models:
self.register_model(app_label, model)
apps = Apps(installed_apps=None)

View File

@ -7,16 +7,12 @@ a list of all possible variables.
""" """
import importlib import importlib
import logging
import os import os
import sys
import time # Needed for Windows import time # Needed for Windows
import warnings
from django.conf import global_settings from django.conf import global_settings
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.utils.functional import LazyObject, empty from django.utils.functional import LazyObject, empty
from django.utils.module_loading import import_by_path
from django.utils import six from django.utils import six
ENVIRONMENT_VARIABLE = "DJANGO_SETTINGS_MODULE" ENVIRONMENT_VARIABLE = "DJANGO_SETTINGS_MODULE"
@ -44,34 +40,12 @@ class LazySettings(LazyObject):
% (desc, ENVIRONMENT_VARIABLE)) % (desc, ENVIRONMENT_VARIABLE))
self._wrapped = Settings(settings_module) self._wrapped = Settings(settings_module)
self._configure_logging()
def __getattr__(self, name): def __getattr__(self, name):
if self._wrapped is empty: if self._wrapped is empty:
self._setup(name) self._setup(name)
return getattr(self._wrapped, name) return getattr(self._wrapped, name)
def _configure_logging(self):
"""
Setup logging from LOGGING_CONFIG and LOGGING settings.
"""
if not sys.warnoptions:
# Route warnings through python logging
logging.captureWarnings(True)
# Allow DeprecationWarnings through the warnings filters
warnings.simplefilter("default", DeprecationWarning)
if self.LOGGING_CONFIG:
from django.utils.log import DEFAULT_LOGGING
# First find the logging configuration function ...
logging_config_func = import_by_path(self.LOGGING_CONFIG)
logging_config_func(DEFAULT_LOGGING)
# ... then invoke it with the logging settings
if self.LOGGING:
logging_config_func(self.LOGGING)
def configure(self, default_settings=global_settings, **options): def configure(self, default_settings=global_settings, **options):
""" """
Called to manually configure the settings. The 'default_settings' Called to manually configure the settings. The 'default_settings'
@ -84,7 +58,6 @@ class LazySettings(LazyObject):
for name, value in options.items(): for name, value in options.items():
setattr(holder, name, value) setattr(holder, name, value)
self._wrapped = holder self._wrapped = holder
self._configure_logging()
@property @property
def configured(self): def configured(self):
@ -104,11 +77,6 @@ class BaseSettings(object):
elif name == "ALLOWED_INCLUDE_ROOTS" and isinstance(value, six.string_types): elif name == "ALLOWED_INCLUDE_ROOTS" and isinstance(value, six.string_types):
raise ValueError("The ALLOWED_INCLUDE_ROOTS setting must be set " raise ValueError("The ALLOWED_INCLUDE_ROOTS setting must be set "
"to a tuple, not a string.") "to a tuple, not a string.")
elif name == "INSTALLED_APPS":
value = list(value) # force evaluation of generators on Python 3
if len(value) != len(set(value)):
raise ImproperlyConfigured("The INSTALLED_APPS setting must contain unique values.")
object.__setattr__(self, name, value) object.__setattr__(self, name, value)

View File

@ -290,7 +290,7 @@ MEDIA_URL = ''
# Absolute path to the directory static files should be collected to. # Absolute path to the directory static files should be collected to.
# Example: "/var/www/example.com/static/" # Example: "/var/www/example.com/static/"
STATIC_ROOT = '' STATIC_ROOT = None
# URL that handles the static files served from STATIC_ROOT. # URL that handles the static files served from STATIC_ROOT.
# Example: "http://example.com/static/", "http://static.example.com/" # Example: "http://example.com/static/", "http://static.example.com/"

View File

@ -71,7 +71,6 @@ def delete_selected(modeladmin, request, queryset):
"perms_lacking": perms_needed, "perms_lacking": perms_needed,
"protected": protected, "protected": protected,
"opts": opts, "opts": opts,
"app_label": app_label,
'action_checkbox_name': helpers.ACTION_CHECKBOX_NAME, 'action_checkbox_name': helpers.ACTION_CHECKBOX_NAME,
} }

View File

@ -0,0 +1,8 @@
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
class AdminConfig(AppConfig):
name = 'django.contrib.admin'
verbose_name = _("administration")

View File

@ -46,9 +46,12 @@ from django.views.decorators.csrf import csrf_protect
IS_POPUP_VAR = '_popup' IS_POPUP_VAR = '_popup'
TO_FIELD_VAR = '_to_field' TO_FIELD_VAR = '_to_field'
HORIZONTAL, VERTICAL = 1, 2 HORIZONTAL, VERTICAL = 1, 2
# returns the <ul> class for a given radio_admin field
get_ul_class = lambda x: 'radiolist%s' % (' inline' if x == HORIZONTAL else '')
def get_ul_class(radio_style):
return 'radiolist' if radio_style == VERTICAL else 'radiolist inline'
class IncorrectLookupParameters(Exception): class IncorrectLookupParameters(Exception):
@ -1308,7 +1311,6 @@ class ModelAdmin(BaseModelAdmin):
media=media, media=media,
inline_admin_formsets=inline_admin_formsets, inline_admin_formsets=inline_admin_formsets,
errors=helpers.AdminErrorList(form, formsets), errors=helpers.AdminErrorList(form, formsets),
app_label=opts.app_label,
preserved_filters=self.get_preserved_filters(request), preserved_filters=self.get_preserved_filters(request),
) )
context.update(extra_context or {}) context.update(extra_context or {})
@ -1532,7 +1534,6 @@ class ModelAdmin(BaseModelAdmin):
media=media, media=media,
has_add_permission=self.has_add_permission(request), has_add_permission=self.has_add_permission(request),
opts=cl.opts, opts=cl.opts,
app_label=app_label,
action_form=action_form, action_form=action_form,
actions_on_top=self.actions_on_top, actions_on_top=self.actions_on_top,
actions_on_bottom=self.actions_on_bottom, actions_on_bottom=self.actions_on_bottom,
@ -1627,7 +1628,6 @@ class ModelAdmin(BaseModelAdmin):
action_list=action_list, action_list=action_list,
module_name=capfirst(force_text(opts.verbose_name_plural)), module_name=capfirst(force_text(opts.verbose_name_plural)),
object=obj, object=obj,
app_label=app_label,
opts=opts, opts=opts,
preserved_filters=self.get_preserved_filters(request), preserved_filters=self.get_preserved_filters(request),
) )

View File

@ -6,6 +6,7 @@ from django.contrib.auth import logout as auth_logout, REDIRECT_FIELD_NAME
from django.contrib.contenttypes import views as contenttype_views from django.contrib.contenttypes import views as contenttype_views
from django.views.decorators.csrf import csrf_protect from django.views.decorators.csrf import csrf_protect
from django.db.models.base import ModelBase from django.db.models.base import ModelBase
from django.apps import apps
from django.core.exceptions import ImproperlyConfigured, PermissionDenied from django.core.exceptions import ImproperlyConfigured, PermissionDenied
from django.core.urlresolvers import reverse, NoReverseMatch from django.core.urlresolvers import reverse, NoReverseMatch
from django.template.response import TemplateResponse from django.template.response import TemplateResponse
@ -156,20 +157,16 @@ class AdminSite(object):
""" """
Check that all things needed to run the admin have been correctly installed. Check that all things needed to run the admin have been correctly installed.
The default implementation checks that LogEntry, ContentType and the The default implementation checks that admin and contenttypes apps are
auth context processor are installed. installed, as well as the auth context processor.
""" """
from django.contrib.admin.models import LogEntry if not apps.is_installed('django.contrib.admin'):
from django.contrib.contenttypes.models import ContentType
if not LogEntry._meta.installed:
raise ImproperlyConfigured("Put 'django.contrib.admin' in your " raise ImproperlyConfigured("Put 'django.contrib.admin' in your "
"INSTALLED_APPS setting in order to use the admin application.") "INSTALLED_APPS setting in order to use the admin application.")
if not ContentType._meta.installed: if not apps.is_installed('django.contrib.contenttypes'):
raise ImproperlyConfigured("Put 'django.contrib.contenttypes' in " raise ImproperlyConfigured("Put 'django.contrib.contenttypes' in "
"your INSTALLED_APPS setting in order to use the admin application.") "your INSTALLED_APPS setting in order to use the admin application.")
if not ('django.contrib.auth.context_processors.auth' in settings.TEMPLATE_CONTEXT_PROCESSORS or if 'django.contrib.auth.context_processors.auth' not in settings.TEMPLATE_CONTEXT_PROCESSORS:
'django.core.context_processors.auth' in settings.TEMPLATE_CONTEXT_PROCESSORS):
raise ImproperlyConfigured("Put 'django.contrib.auth.context_processors.auth' " raise ImproperlyConfigured("Put 'django.contrib.auth.context_processors.auth' "
"in your TEMPLATE_CONTEXT_PROCESSORS setting in order to use the admin application.") "in your TEMPLATE_CONTEXT_PROCESSORS setting in order to use the admin application.")
@ -383,7 +380,7 @@ class AdminSite(object):
app_dict[app_label]['models'].append(model_dict) app_dict[app_label]['models'].append(model_dict)
else: else:
app_dict[app_label] = { app_dict[app_label] = {
'name': app_label.title(), 'name': apps.get_app_config(app_label).verbose_name,
'app_label': app_label, 'app_label': app_label,
'app_url': reverse('admin:app_list', kwargs={'app_label': app_label}, current_app=self.name), 'app_url': reverse('admin:app_list', kwargs={'app_label': app_label}, current_app=self.name),
'has_module_perms': has_module_perms, 'has_module_perms': has_module_perms,
@ -392,7 +389,7 @@ class AdminSite(object):
# Sort the apps alphabetically. # Sort the apps alphabetically.
app_list = list(six.itervalues(app_dict)) app_list = list(six.itervalues(app_dict))
app_list.sort(key=lambda x: x['name']) app_list.sort(key=lambda x: x['name'].lower())
# Sort the models alphabetically within each app. # Sort the models alphabetically within each app.
for app in app_list: for app in app_list:
@ -410,6 +407,7 @@ class AdminSite(object):
def app_index(self, request, app_label, extra_context=None): def app_index(self, request, app_label, extra_context=None):
user = request.user user = request.user
app_name = apps.get_app_config(app_label).verbose_name
has_module_perms = user.has_module_perms(app_label) has_module_perms = user.has_module_perms(app_label)
if not has_module_perms: if not has_module_perms:
raise PermissionDenied raise PermissionDenied
@ -444,7 +442,7 @@ class AdminSite(object):
# something to display, add in the necessary meta # something to display, add in the necessary meta
# information. # information.
app_dict = { app_dict = {
'name': app_label.title(), 'name': app_name,
'app_label': app_label, 'app_label': app_label,
'app_url': '', 'app_url': '',
'has_module_perms': has_module_perms, 'has_module_perms': has_module_perms,
@ -455,7 +453,7 @@ class AdminSite(object):
# Sort the models alphabetically within each app. # Sort the models alphabetically within each app.
app_dict['models'].sort(key=lambda x: x['name']) app_dict['models'].sort(key=lambda x: x['name'])
context = dict(self.each_context(), context = dict(self.each_context(),
title=_('%s administration') % capfirst(app_label), title=_('%(app)s administration') % {'app': app_name},
app_list=[app_dict], app_list=[app_dict],
app_label=app_label, app_label=app_label,
) )

View File

@ -6,12 +6,12 @@
<script type="text/javascript" src="{% url 'admin:jsi18n' %}"></script> <script type="text/javascript" src="{% url 'admin:jsi18n' %}"></script>
{% endblock %} {% endblock %}
{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}" />{% endblock %} {% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}" />{% endblock %}
{% block bodyclass %}{{ opts.app_label }}-{{ opts.object_name.lower }} change-form{% endblock %} {% block bodyclass %}{{ opts.app_label }}-{{ opts.model_name }} change-form{% endblock %}
{% if not is_popup %} {% if not is_popup %}
{% block breadcrumbs %} {% block breadcrumbs %}
<div class="breadcrumbs"> <div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a> <a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_label|capfirst|escape }}</a> &rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
&rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a> &rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>
&rsaquo; <a href="{% url opts|admin_urlname:'change' original.pk|admin_urlquote %}">{{ original|truncatewords:"18" }}</a> &rsaquo; <a href="{% url opts|admin_urlname:'change' original.pk|admin_urlquote %}">{{ original|truncatewords:"18" }}</a>
&rsaquo; {% trans 'Change password' %} &rsaquo; {% trans 'Change password' %}

View File

@ -10,13 +10,13 @@
{% block coltype %}colM{% endblock %} {% block coltype %}colM{% endblock %}
{% block bodyclass %}app-{{ opts.app_label }} model-{{ opts.object_name.lower }} change-form{% endblock %} {% block bodyclass %}app-{{ opts.app_label }} model-{{ opts.model_name }} change-form{% endblock %}
{% if not is_popup %} {% if not is_popup %}
{% block breadcrumbs %} {% block breadcrumbs %}
<div class="breadcrumbs"> <div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a> <a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ app_label|capfirst|escape }}</a> &rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
&rsaquo; {% if has_change_permission %}<a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>{% else %}{{ opts.verbose_name_plural|capfirst }}{% endif %} &rsaquo; {% if has_change_permission %}<a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>{% else %}{{ opts.verbose_name_plural|capfirst }}{% endif %}
&rsaquo; {% if add %}{% trans 'Add' %} {{ opts.verbose_name }}{% else %}{{ original|truncatewords:"18" }}{% endif %} &rsaquo; {% if add %}{% trans 'Add' %} {{ opts.verbose_name }}{% else %}{{ original|truncatewords:"18" }}{% endif %}
</div> </div>

View File

@ -32,13 +32,13 @@
{% endif %}{% endif %} {% endif %}{% endif %}
{% endblock %} {% endblock %}
{% block bodyclass %}app-{{ opts.app_label }} model-{{ opts.object_name.lower }} change-list{% endblock %} {% block bodyclass %}app-{{ opts.app_label }} model-{{ opts.model_name }} change-list{% endblock %}
{% if not is_popup %} {% if not is_popup %}
{% block breadcrumbs %} {% block breadcrumbs %}
<div class="breadcrumbs"> <div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a> <a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
&rsaquo; <a href="{% url 'admin:app_list' app_label=cl.opts.app_label %}">{{ app_label|capfirst|escape }}</a> &rsaquo; <a href="{% url 'admin:app_list' app_label=cl.opts.app_label %}">{{ cl.opts.app_config.verbose_name }}</a>
&rsaquo; {{ cl.opts.verbose_name_plural|capfirst }} &rsaquo; {{ cl.opts.verbose_name_plural|capfirst }}
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,12 +1,12 @@
{% extends "admin/base_site.html" %} {% extends "admin/base_site.html" %}
{% load i18n admin_urls %} {% load i18n admin_urls %}
{% block bodyclass %}app-{{ opts.app_label }} model-{{ opts.object_name.lower }} delete-confirmation{% endblock %} {% block bodyclass %}app-{{ opts.app_label }} model-{{ opts.model_name }} delete-confirmation{% endblock %}
{% block breadcrumbs %} {% block breadcrumbs %}
<div class="breadcrumbs"> <div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a> <a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ app_label|capfirst }}</a> &rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
&rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst|escape }}</a> &rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst|escape }}</a>
&rsaquo; <a href="{% url opts|admin_urlname:'change' object.pk|admin_urlquote %}">{{ object|truncatewords:"18" }}</a> &rsaquo; <a href="{% url opts|admin_urlname:'change' object.pk|admin_urlquote %}">{{ object|truncatewords:"18" }}</a>
&rsaquo; {% trans 'Delete' %} &rsaquo; {% trans 'Delete' %}

View File

@ -1,12 +1,12 @@
{% extends "admin/base_site.html" %} {% extends "admin/base_site.html" %}
{% load i18n l10n admin_urls %} {% load i18n l10n admin_urls %}
{% block bodyclass %}app-{{ opts.app_label }} model-{{ opts.object_name.lower }} delete-confirmation delete-selected-confirmation{% endblock %} {% block bodyclass %}app-{{ opts.app_label }} model-{{ opts.model_name }} delete-confirmation delete-selected-confirmation{% endblock %}
{% block breadcrumbs %} {% block breadcrumbs %}
<div class="breadcrumbs"> <div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a> <a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
&rsaquo; <a href="{% url 'admin:app_list' app_label=app_label %}">{{ app_label|capfirst|escape }}</a> &rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
&rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a> &rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>
&rsaquo; {% trans 'Delete multiple objects' %} &rsaquo; {% trans 'Delete multiple objects' %}
</div> </div>

View File

@ -4,7 +4,7 @@
{% block breadcrumbs %} {% block breadcrumbs %}
<div class="breadcrumbs"> <div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a> <a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
&rsaquo; <a href="{% url 'admin:app_list' app_label=app_label %}">{{ app_label|capfirst|escape }}</a> &rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
&rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ module_name }}</a> &rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ module_name }}</a>
&rsaquo; <a href="{% url opts|admin_urlname:'change' object.pk|admin_urlquote %}">{{ object|truncatewords:"18" }}</a> &rsaquo; <a href="{% url opts|admin_urlname:'change' object.pk|admin_urlquote %}">{{ object|truncatewords:"18" }}</a>
&rsaquo; {% trans 'History' %} &rsaquo; {% trans 'History' %}

View File

@ -333,7 +333,7 @@ def date_hierarchy(cl):
month_lookup = cl.params.get(month_field) month_lookup = cl.params.get(month_field)
day_lookup = cl.params.get(day_field) day_lookup = cl.params.get(day_field)
link = lambda d: cl.get_query_string(d, [field_generic]) link = lambda filters: cl.get_query_string(filters, [field_generic])
if not (year_lookup or month_lookup or day_lookup): if not (year_lookup or month_lookup or day_lookup):
# select appropriate start level # select appropriate start level

View File

@ -1,9 +1,9 @@
from django.conf import settings from django.apps import apps
from django.template import Library from django.template import Library
register = Library() register = Library()
if 'django.contrib.staticfiles' in settings.INSTALLED_APPS: if apps.is_installed('django.contrib.staticfiles'):
from django.contrib.staticfiles.templatetags.staticfiles import static from django.contrib.staticfiles.templatetags.staticfiles import static
else: else:
from django.templatetags.static import static from django.templatetags.static import static

View File

@ -32,7 +32,6 @@ class AdminSeleniumWebDriverTestCase(StaticLiveServerCase):
@classmethod @classmethod
def _tearDownClassInternal(cls): def _tearDownClassInternal(cls):
if hasattr(cls, 'selenium'): if hasattr(cls, 'selenium'):
cls.selenium.refresh() # see ticket #21227
cls.selenium.quit() cls.selenium.quit()
super(AdminSeleniumWebDriverTestCase, cls)._tearDownClassInternal() super(AdminSeleniumWebDriverTestCase, cls)._tearDownClassInternal()
@ -51,8 +50,40 @@ class AdminSeleniumWebDriverTestCase(StaticLiveServerCase):
Helper function that blocks until the element with the given tag name Helper function that blocks until the element with the given tag name
is found on the page. is found on the page.
""" """
self.wait_for(tag_name, timeout)
def wait_for(self, css_selector, timeout=10):
"""
Helper function that blocks until an css selector is found on the page.
"""
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as ec
self.wait_until( self.wait_until(
lambda driver: driver.find_element_by_tag_name(tag_name), ec.presence_of_element_located((By.CSS_SELECTOR, css_selector)),
timeout
)
def wait_for_text(self, css_selector, text, timeout=10):
"""
Helper function that blocks until the text is found in the css selector.
"""
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as ec
self.wait_until(
ec.text_to_be_present_in_element(
(By.CSS_SELECTOR, css_selector), text),
timeout
)
def wait_for_value(self, css_selector, text, timeout=10):
"""
Helper function that blocks until the value is found in the css selector.
"""
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as ec
self.wait_until(
ec.text_to_be_present_in_element_value(
(By.CSS_SELECTOR, css_selector), text),
timeout timeout
) )

View File

@ -1,4 +1,3 @@
from django.core.apps import app_cache
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.db import models from django.db import models
from django.db.models.fields import FieldDoesNotExist from django.db.models.fields import FieldDoesNotExist
@ -15,10 +14,6 @@ __all__ = ['BaseValidator', 'InlineValidator']
class BaseValidator(object): class BaseValidator(object):
def __init__(self):
# Before we can introspect models, they need the app cache to be fully
# loaded so that inter-relations are set up correctly.
app_cache.populate()
def validate(self, cls, model): def validate(self, cls, model):
for m in dir(self): for m in dir(self):

View File

@ -0,0 +1,8 @@
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
class AdminDocsConfig(AppConfig):
name = 'django.contrib.admindocs'
verbose_name = _("administrative documentation")

View File

@ -27,7 +27,7 @@
<table class="xfull"> <table class="xfull">
{% for model in group.list %} {% for model in group.list %}
<tr> <tr>
<th><a href="{% url 'django-admindocs-models-detail' app_label=model.app_label model_name=model.object_name.lower %}">{{ model.object_name }}</a></th> <th><a href="{% url 'django-admindocs-models-detail' app_label=model.app_label model_name=model.model_name %}">{{ model.object_name }}</a></th>
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>

View File

@ -5,10 +5,10 @@ import re
import warnings import warnings
from django import template from django import template
from django.apps import apps
from django.conf import settings from django.conf import settings
from django.contrib import admin from django.contrib import admin
from django.contrib.admin.views.decorators import staff_member_required from django.contrib.admin.views.decorators import staff_member_required
from django.core.apps import app_cache
from django.db import models from django.db import models
from django.core.exceptions import ViewDoesNotExist from django.core.exceptions import ViewDoesNotExist
from django.http import Http404 from django.http import Http404
@ -174,7 +174,7 @@ class ModelIndexView(BaseAdminDocsView):
template_name = 'admin_doc/model_index.html' template_name = 'admin_doc/model_index.html'
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
m_list = [m._meta for m in app_cache.get_models()] m_list = [m._meta for m in apps.get_models()]
kwargs.update({'models': m_list}) kwargs.update({'models': m_list})
return super(ModelIndexView, self).get_context_data(**kwargs) return super(ModelIndexView, self).get_context_data(**kwargs)
@ -185,11 +185,12 @@ class ModelDetailView(BaseAdminDocsView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
# Get the model class. # Get the model class.
try: try:
app_cache.get_app_config(self.kwargs['app_label']) apps.get_app_config(self.kwargs['app_label'])
except LookupError: except LookupError:
raise Http404(_("App %(app_label)r not found") % self.kwargs) raise Http404(_("App %(app_label)r not found") % self.kwargs)
model = app_cache.get_model(self.kwargs['app_label'], self.kwargs['model_name']) try:
if model is None: model = apps.get_model(self.kwargs['app_label'], self.kwargs['model_name'])
except LookupError:
raise Http404(_("Model %(model_name)r not found in app %(app_label)r") % self.kwargs) raise Http404(_("Model %(model_name)r not found in app %(app_label)r") % self.kwargs)
opts = model._meta opts = model._meta

View File

@ -123,14 +123,15 @@ def get_user_model():
""" """
Returns the User model that is active in this project. Returns the User model that is active in this project.
""" """
from django.core.apps import app_cache from django.apps import apps
try: try:
app_label, model_name = settings.AUTH_USER_MODEL.split('.') app_label, model_name = settings.AUTH_USER_MODEL.split('.')
except ValueError: except ValueError:
raise ImproperlyConfigured("AUTH_USER_MODEL must be of the form 'app_label.model_name'") raise ImproperlyConfigured("AUTH_USER_MODEL must be of the form 'app_label.model_name'")
user_model = app_cache.get_model(app_label, model_name) try:
if user_model is None: user_model = apps.get_model(app_label, model_name)
except LookupError:
raise ImproperlyConfigured("AUTH_USER_MODEL refers to model '%s' that has not been installed" % settings.AUTH_USER_MODEL) raise ImproperlyConfigured("AUTH_USER_MODEL refers to model '%s' that has not been installed" % settings.AUTH_USER_MODEL)
return user_model return user_model

View File

@ -0,0 +1,8 @@
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
class AuthConfig(AppConfig):
name = 'django.contrib.auth'
verbose_name = _("authentication and authorization")

View File

@ -21,7 +21,11 @@ from django.contrib.sites.models import get_current_site
UNMASKED_DIGITS_TO_SHOW = 6 UNMASKED_DIGITS_TO_SHOW = 6
mask_password = lambda p: "%s%s" % (p[:UNMASKED_DIGITS_TO_SHOW], "*" * max(len(p) - UNMASKED_DIGITS_TO_SHOW, 0))
def mask_password(password):
shown = password[:UNMASKED_DIGITS_TO_SHOW]
masked = "*" * max(len(password) - UNMASKED_DIGITS_TO_SHOW, 0)
return shown + masked
class ReadOnlyPasswordHashWidget(forms.Widget): class ReadOnlyPasswordHashWidget(forms.Widget):

View File

@ -6,9 +6,9 @@ from __future__ import unicode_literals
import getpass import getpass
import unicodedata import unicodedata
from django.apps import apps
from django.contrib.auth import (models as auth_app, get_permission_codename, from django.contrib.auth import (models as auth_app, get_permission_codename,
get_user_model) get_user_model)
from django.core.apps import app_cache, UnavailableApp
from django.core import exceptions from django.core import exceptions
from django.core.management.base import CommandError from django.core.management.base import CommandError
from django.db import DEFAULT_DB_ALIAS, router from django.db import DEFAULT_DB_ALIAS, router
@ -60,25 +60,26 @@ def _check_permission_clashing(custom, builtin, ctype):
pool.add(codename) pool.add(codename)
def create_permissions(app, created_models, verbosity, db=DEFAULT_DB_ALIAS, **kwargs): def create_permissions(app_config, verbosity=2, interactive=True, db=DEFAULT_DB_ALIAS, **kwargs):
try: if not app_config.models_module:
app_cache.get_model('auth', 'Permission')
except UnavailableApp:
return return
if not router.allow_migrate(db, auth_app.Permission): try:
Permission = apps.get_model('auth', 'Permission')
except LookupError:
return
if not router.allow_migrate(db, Permission):
return return
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
app_models = app_cache.get_models(app)
# This will hold the permissions we're looking for as # This will hold the permissions we're looking for as
# (content_type, (codename, name)) # (content_type, (codename, name))
searched_perms = list() searched_perms = list()
# The codenames and ctypes that should exist. # The codenames and ctypes that should exist.
ctypes = set() ctypes = set()
for klass in app_models: for klass in app_config.get_models():
# Force looking up the content types in the current database # Force looking up the content types in the current database
# before creating foreign keys to them. # before creating foreign keys to them.
ctype = ContentType.objects.db_manager(db).get_for_model(klass) ctype = ContentType.objects.db_manager(db).get_for_model(klass)
@ -89,20 +90,20 @@ def create_permissions(app, created_models, verbosity, db=DEFAULT_DB_ALIAS, **kw
# Find all the Permissions that have a content_type for a model we're # Find all the Permissions that have a content_type for a model we're
# looking for. We don't need to check for codenames since we already have # looking for. We don't need to check for codenames since we already have
# a list of the ones we're going to create. # a list of the ones we're going to create.
all_perms = set(auth_app.Permission.objects.using(db).filter( all_perms = set(Permission.objects.using(db).filter(
content_type__in=ctypes, content_type__in=ctypes,
).values_list( ).values_list(
"content_type", "codename" "content_type", "codename"
)) ))
perms = [ perms = [
auth_app.Permission(codename=codename, name=name, content_type=ctype) Permission(codename=codename, name=name, content_type=ctype)
for ctype, (codename, name) in searched_perms for ctype, (codename, name) in searched_perms
if (ctype.pk, codename) not in all_perms if (ctype.pk, codename) not in all_perms
] ]
# Validate the permissions before bulk_creation to avoid cryptic # Validate the permissions before bulk_creation to avoid cryptic
# database error when the verbose_name is longer than 50 characters # database error when the verbose_name is longer than 50 characters
permission_name_max_length = auth_app.Permission._meta.get_field('name').max_length permission_name_max_length = Permission._meta.get_field('name').max_length
verbose_name_max_length = permission_name_max_length - 11 # len('Can change ') prefix verbose_name_max_length = permission_name_max_length - 11 # len('Can change ') prefix
for perm in perms: for perm in perms:
if len(perm.name) > permission_name_max_length: if len(perm.name) > permission_name_max_length:
@ -112,23 +113,24 @@ def create_permissions(app, created_models, verbosity, db=DEFAULT_DB_ALIAS, **kw
verbose_name_max_length, verbose_name_max_length,
) )
) )
auth_app.Permission.objects.using(db).bulk_create(perms) Permission.objects.using(db).bulk_create(perms)
if verbosity >= 2: if verbosity >= 2:
for perm in perms: for perm in perms:
print("Adding permission '%s'" % perm) print("Adding permission '%s'" % perm)
def create_superuser(app, created_models, verbosity, db, **kwargs): def create_superuser(app_config, verbosity=2, interactive=True, db=DEFAULT_DB_ALIAS, **kwargs):
try: try:
app_cache.get_model('auth', 'Permission') apps.get_model('auth', 'Permission')
UserModel = get_user_model() except LookupError:
except UnavailableApp:
return return
UserModel = get_user_model()
from django.core.management import call_command from django.core.management import call_command
if UserModel in created_models and kwargs.get('interactive', True): if not UserModel.objects.exists() and interactive:
msg = ("\nYou just installed Django's auth system, which means you " msg = ("\nYou have installed Django's auth system, and "
"don't have any superusers defined.\nWould you like to create one " "don't have any superusers defined.\nWould you like to create one "
"now? (yes/no): ") "now? (yes/no): ")
confirm = input(msg) confirm = input(msg)
@ -202,7 +204,9 @@ def get_default_username(check_db=True):
return '' return ''
return default_username return default_username
signals.post_migrate.connect(create_permissions, signals.post_migrate.connect(create_permissions,
dispatch_uid="django.contrib.auth.management.create_permissions") dispatch_uid="django.contrib.auth.management.create_permissions")
signals.post_migrate.connect(create_superuser, signals.post_migrate.connect(create_superuser,
sender=auth_app, dispatch_uid="django.contrib.auth.management.create_superuser") sender=apps.get_app_config('auth'),
dispatch_uid="django.contrib.auth.management.create_superuser")

View File

@ -1,3 +1,5 @@
from __future__ import unicode_literals
import getpass import getpass
from optparse import make_option from optparse import make_option

View File

@ -10,8 +10,7 @@ from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ImproperlyConfigured, PermissionDenied from django.core.exceptions import ImproperlyConfigured, PermissionDenied
from django.contrib.auth import authenticate, get_user from django.contrib.auth import authenticate, get_user
from django.http import HttpRequest from django.http import HttpRequest
from django.test import TestCase from django.test import TestCase, override_settings
from django.test.utils import override_settings
from django.contrib.auth.hashers import MD5PasswordHasher from django.contrib.auth.hashers import MD5PasswordHasher

View File

@ -3,6 +3,7 @@ from __future__ import unicode_literals
import locale import locale
from django.apps import apps
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.auth.management.commands import createsuperuser from django.contrib.auth.management.commands import createsuperuser
from django.contrib.auth.models import User, AnonymousUser from django.contrib.auth.models import User, AnonymousUser
@ -11,9 +12,8 @@ from django.contrib.auth.tests.utils import skipIfCustomUser
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.core.management import call_command from django.core.management import call_command
from django.dispatch import receiver from django.dispatch import receiver
from django.test import TestCase from django.test import TestCase, override_settings
from django.test.signals import setting_changed from django.test.signals import setting_changed
from django.test.utils import override_settings
from django.utils import translation from django.utils import translation
from django.utils.encoding import force_str from django.utils.encoding import force_str
from django.utils.six import binary_type, PY2, StringIO from django.utils.six import binary_type, PY2, StringIO
@ -26,6 +26,7 @@ def user_model_swapped(**kwargs):
# Reset User manager # Reset User manager
setattr(User, 'objects', User._default_manager) setattr(User, 'objects', User._default_manager)
ensure_default_manager(User) ensure_default_manager(User)
apps.clear_cache()
def mock_inputs(inputs): def mock_inputs(inputs):

View File

@ -7,8 +7,7 @@ from django.contrib.auth.models import User, Permission
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.context_processors import PermWrapper, PermLookupDict from django.contrib.auth.context_processors import PermWrapper, PermLookupDict
from django.db.models import Q from django.db.models import Q
from django.test import TestCase from django.test import TestCase, override_settings
from django.test.utils import override_settings
from django.utils._os import upath from django.utils._os import upath

View File

@ -12,8 +12,7 @@ from django.contrib.auth.forms import (UserCreationForm, AuthenticationForm,
from django.contrib.auth.tests.utils import skipIfCustomUser from django.contrib.auth.tests.utils import skipIfCustomUser
from django.core import mail from django.core import mail
from django.forms.fields import Field, CharField from django.forms.fields import Field, CharField
from django.test import TestCase from django.test import TestCase, override_settings
from django.test.utils import override_settings
from django.utils.encoding import force_text from django.utils.encoding import force_text
from django.utils._os import upath from django.utils._os import upath
from django.utils import translation from django.utils import translation

View File

@ -5,7 +5,7 @@ from django.contrib.auth.models import User, Group
from django.contrib.auth.tests.custom_user import CustomUser from django.contrib.auth.tests.custom_user import CustomUser
from django.contrib.auth.tests.utils import skipIfCustomUser from django.contrib.auth.tests.utils import skipIfCustomUser
from django.test import TransactionTestCase from django.test import TransactionTestCase
from django.test.utils import override_settings from django.test import override_settings
# This must be a TransactionTestCase because the WSGI auth handler performs # This must be a TransactionTestCase because the WSGI auth handler performs

View File

@ -1,6 +1,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from datetime import date from datetime import date
from django.apps import apps
from django.contrib.auth import models, management from django.contrib.auth import models, management
from django.contrib.auth.management import create_permissions from django.contrib.auth.management import create_permissions
from django.contrib.auth.management.commands import changepassword from django.contrib.auth.management.commands import changepassword
@ -8,13 +9,11 @@ from django.contrib.auth.models import User
from django.contrib.auth.tests.custom_user import CustomUser from django.contrib.auth.tests.custom_user import CustomUser
from django.contrib.auth.tests.utils import skipIfCustomUser from django.contrib.auth.tests.utils import skipIfCustomUser
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.apps import app_cache
from django.core import exceptions from django.core import exceptions
from django.core.management import call_command from django.core.management import call_command
from django.core.management.base import CommandError from django.core.management.base import CommandError
from django.core.management.validation import get_validation_errors from django.core.management.validation import get_validation_errors
from django.test import TestCase from django.test import TestCase, override_settings
from django.test.utils import override_settings
from django.utils import six from django.utils import six
from django.utils.six import StringIO from django.utils.six import StringIO
@ -83,6 +82,19 @@ class ChangepasswordManagementCommandTestCase(TestCase):
with self.assertRaises(CommandError): with self.assertRaises(CommandError):
command.execute("joe", stdout=self.stdout, stderr=self.stderr) command.execute("joe", stdout=self.stdout, stderr=self.stderr)
def test_that_changepassword_command_works_with_nonascii_output(self):
"""
#21627 -- Executing the changepassword management command should allow
non-ASCII characters from the User object representation.
"""
# 'Julia' with accented 'u':
models.User.objects.create_user(username='J\xfalia', password='qwerty')
command = changepassword.Command()
command._get_pass = lambda *args: 'not qwerty'
command.execute("J\xfalia", stdout=self.stdout)
@skipIfCustomUser @skipIfCustomUser
class CreatesuperuserManagementCommandTestCase(TestCase): class CreatesuperuserManagementCommandTestCase(TestCase):
@ -184,21 +196,21 @@ class CustomUserModelValidationTestCase(TestCase):
def test_required_fields_is_list(self): def test_required_fields_is_list(self):
"REQUIRED_FIELDS should be a list." "REQUIRED_FIELDS should be a list."
new_io = StringIO() new_io = StringIO()
get_validation_errors(new_io, app_cache.get_app_config('auth').models_module) get_validation_errors(new_io, apps.get_app_config('auth'))
self.assertIn("The REQUIRED_FIELDS must be a list or tuple.", new_io.getvalue()) self.assertIn("The REQUIRED_FIELDS must be a list or tuple.", new_io.getvalue())
@override_settings(AUTH_USER_MODEL='auth.CustomUserBadRequiredFields') @override_settings(AUTH_USER_MODEL='auth.CustomUserBadRequiredFields')
def test_username_not_in_required_fields(self): def test_username_not_in_required_fields(self):
"USERNAME_FIELD should not appear in REQUIRED_FIELDS." "USERNAME_FIELD should not appear in REQUIRED_FIELDS."
new_io = StringIO() new_io = StringIO()
get_validation_errors(new_io, app_cache.get_app_config('auth').models_module) get_validation_errors(new_io, apps.get_app_config('auth'))
self.assertIn("The field named as the USERNAME_FIELD should not be included in REQUIRED_FIELDS on a swappable User model.", new_io.getvalue()) self.assertIn("The field named as the USERNAME_FIELD should not be included in REQUIRED_FIELDS on a swappable User model.", new_io.getvalue())
@override_settings(AUTH_USER_MODEL='auth.CustomUserNonUniqueUsername') @override_settings(AUTH_USER_MODEL='auth.CustomUserNonUniqueUsername')
def test_username_non_unique(self): def test_username_non_unique(self):
"A non-unique USERNAME_FIELD should raise a model validation error." "A non-unique USERNAME_FIELD should raise a model validation error."
new_io = StringIO() new_io = StringIO()
get_validation_errors(new_io, app_cache.get_app_config('auth').models_module) get_validation_errors(new_io, apps.get_app_config('auth'))
self.assertIn("The USERNAME_FIELD must be unique. Add unique=True to the field parameters.", new_io.getvalue()) self.assertIn("The USERNAME_FIELD must be unique. Add unique=True to the field parameters.", new_io.getvalue())
@ -220,13 +232,15 @@ class PermissionTestCase(TestCase):
Test that we show proper error message if we are trying to create Test that we show proper error message if we are trying to create
duplicate permissions. duplicate permissions.
""" """
auth_app_config = apps.get_app_config('auth')
# check duplicated default permission # check duplicated default permission
models.Permission._meta.permissions = [ models.Permission._meta.permissions = [
('change_permission', 'Can edit permission (duplicate)')] ('change_permission', 'Can edit permission (duplicate)')]
six.assertRaisesRegex(self, CommandError, six.assertRaisesRegex(self, CommandError,
"The permission codename 'change_permission' clashes with a " "The permission codename 'change_permission' clashes with a "
"builtin permission for model 'auth.Permission'.", "builtin permission for model 'auth.Permission'.",
create_permissions, models, [], verbosity=0) create_permissions, auth_app_config, verbosity=0)
# check duplicated custom permissions # check duplicated custom permissions
models.Permission._meta.permissions = [ models.Permission._meta.permissions = [
@ -237,21 +251,23 @@ class PermissionTestCase(TestCase):
six.assertRaisesRegex(self, CommandError, six.assertRaisesRegex(self, CommandError,
"The permission codename 'my_custom_permission' is duplicated for model " "The permission codename 'my_custom_permission' is duplicated for model "
"'auth.Permission'.", "'auth.Permission'.",
create_permissions, models, [], verbosity=0) create_permissions, auth_app_config, verbosity=0)
# should not raise anything # should not raise anything
models.Permission._meta.permissions = [ models.Permission._meta.permissions = [
('my_custom_permission', 'Some permission'), ('my_custom_permission', 'Some permission'),
('other_one', 'Some other permission'), ('other_one', 'Some other permission'),
] ]
create_permissions(models, [], verbosity=0) create_permissions(auth_app_config, verbosity=0)
def test_default_permissions(self): def test_default_permissions(self):
auth_app_config = apps.get_app_config('auth')
permission_content_type = ContentType.objects.get_by_natural_key('auth', 'permission') permission_content_type = ContentType.objects.get_by_natural_key('auth', 'permission')
models.Permission._meta.permissions = [ models.Permission._meta.permissions = [
('my_custom_permission', 'Some permission'), ('my_custom_permission', 'Some permission'),
] ]
create_permissions(models, [], verbosity=0) create_permissions(auth_app_config, verbosity=0)
# add/change/delete permission by default + custom permission # add/change/delete permission by default + custom permission
self.assertEqual(models.Permission.objects.filter( self.assertEqual(models.Permission.objects.filter(
@ -260,7 +276,7 @@ class PermissionTestCase(TestCase):
models.Permission.objects.filter(content_type=permission_content_type).delete() models.Permission.objects.filter(content_type=permission_content_type).delete()
models.Permission._meta.default_permissions = [] models.Permission._meta.default_permissions = []
create_permissions(models, [], verbosity=0) create_permissions(auth_app_config, verbosity=0)
# custom permission only since default permissions is empty # custom permission only since default permissions is empty
self.assertEqual(models.Permission.objects.filter( self.assertEqual(models.Permission.objects.filter(
@ -268,10 +284,12 @@ class PermissionTestCase(TestCase):
).count(), 1) ).count(), 1)
def test_verbose_name_length(self): def test_verbose_name_length(self):
auth_app_config = apps.get_app_config('auth')
permission_content_type = ContentType.objects.get_by_natural_key('auth', 'permission') permission_content_type = ContentType.objects.get_by_natural_key('auth', 'permission')
models.Permission.objects.filter(content_type=permission_content_type).delete() models.Permission.objects.filter(content_type=permission_content_type).delete()
models.Permission._meta.verbose_name = "some ridiculously long verbose name that is out of control" models.Permission._meta.verbose_name = "some ridiculously long verbose name that is out of control"
six.assertRaisesRegex(self, exceptions.ValidationError, six.assertRaisesRegex(self, exceptions.ValidationError,
"The verbose_name of permission is longer than 39 characters", "The verbose_name of permission is longer than 39 characters",
create_permissions, models, [], verbosity=0) create_permissions, auth_app_config, verbosity=0)

View File

@ -3,8 +3,7 @@ from django.contrib.auth.models import AbstractUser, Group, User, UserManager
from django.contrib.auth.tests.utils import skipIfCustomUser from django.contrib.auth.tests.utils import skipIfCustomUser
from django.core import mail from django.core import mail
from django.db.models.signals import post_save from django.db.models.signals import post_save
from django.test import TestCase from django.test import TestCase, override_settings
from django.test.utils import override_settings
@skipIfCustomUser @skipIfCustomUser

View File

@ -3,7 +3,7 @@ from django.contrib.auth.models import User
from django.contrib.auth.tests.utils import skipIfCustomUser from django.contrib.auth.tests.utils import skipIfCustomUser
from django.test import TestCase from django.test import TestCase
from django.test.client import RequestFactory from django.test.client import RequestFactory
from django.test.utils import override_settings from django.test import override_settings
@skipIfCustomUser @skipIfCustomUser

View File

@ -7,7 +7,7 @@ from django.contrib.auth.views import (
password_reset_complete, password_change, password_change_done, password_reset_complete, password_change, password_change_done,
) )
from django.test import RequestFactory, TestCase from django.test import RequestFactory, TestCase
from django.test.utils import override_settings from django.test import override_settings
from django.utils.encoding import force_bytes, force_text from django.utils.encoding import force_bytes, force_text
from django.utils.http import urlsafe_base64_encode from django.utils.http import urlsafe_base64_encode

View File

@ -14,8 +14,8 @@ from django.utils.encoding import force_text
from django.utils.http import urlquote from django.utils.http import urlquote
from django.utils.six.moves.urllib.parse import urlparse, ParseResult from django.utils.six.moves.urllib.parse import urlparse, ParseResult
from django.utils._os import upath from django.utils._os import upath
from django.test import TestCase from django.test import TestCase, override_settings
from django.test.utils import override_settings, patch_logger from django.test.utils import patch_logger
from django.middleware.csrf import CsrfViewMiddleware from django.middleware.csrf import CsrfViewMiddleware
from django.contrib.sessions.middleware import SessionMiddleware from django.contrib.sessions.middleware import SessionMiddleware

View File

@ -1,5 +1,6 @@
from importlib import import_module from importlib import import_module
import warnings import warnings
from django.apps import apps
from django.conf import settings from django.conf import settings
from django.core import urlresolvers from django.core import urlresolvers
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
@ -14,20 +15,12 @@ def get_comment_app():
""" """
Get the comment app (i.e. "django.contrib.comments") as defined in the settings Get the comment app (i.e. "django.contrib.comments") as defined in the settings
""" """
# Make sure the app's in INSTALLED_APPS
comments_app = get_comment_app_name()
if comments_app not in settings.INSTALLED_APPS:
raise ImproperlyConfigured("The COMMENTS_APP (%r) "\
"must be in INSTALLED_APPS" % settings.COMMENTS_APP)
# Try to import the package
try: try:
package = import_module(comments_app) app_config = apps.get_app_config(get_comment_app_name().rpartition(".")[2])
except ImportError as e: except LookupError:
raise ImproperlyConfigured("The COMMENTS_APP setting refers to "\ raise ImproperlyConfigured("The COMMENTS_APP (%r) "
"a non-existing package. (%s)" % e) "must be in INSTALLED_APPS" % settings.COMMENTS_APP)
return app_config.module
return package
def get_comment_app_name(): def get_comment_app_name():
""" """

View File

@ -0,0 +1,8 @@
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
class CommentsConfig(AppConfig):
name = 'django.contrib.comments'
verbose_name = _("comments")

View File

@ -1,9 +1,9 @@
from django import http from django import http
from django.apps import apps
from django.conf import settings from django.conf import settings
from django.contrib import comments from django.contrib import comments
from django.contrib.comments import signals from django.contrib.comments import signals
from django.contrib.comments.views.utils import next_redirect, confirmation_view from django.contrib.comments.views.utils import next_redirect, confirmation_view
from django.core.apps import app_cache
from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.db import models from django.db import models
from django.shortcuts import render_to_response from django.shortcuts import render_to_response
@ -49,12 +49,12 @@ def post_comment(request, next=None, using=None):
if ctype is None or object_pk is None: if ctype is None or object_pk is None:
return CommentPostBadRequest("Missing content_type or object_pk field.") return CommentPostBadRequest("Missing content_type or object_pk field.")
try: try:
model = app_cache.get_model(*ctype.split(".", 1)) model = apps.get_model(*ctype.split(".", 1))
target = model._default_manager.using(using).get(pk=object_pk) target = model._default_manager.using(using).get(pk=object_pk)
except TypeError: except TypeError:
return CommentPostBadRequest( return CommentPostBadRequest(
"Invalid content_type value: %r" % escape(ctype)) "Invalid content_type value: %r" % escape(ctype))
except AttributeError: except LookupError:
return CommentPostBadRequest( return CommentPostBadRequest(
"The given content-type %r does not resolve to a valid model." % \ "The given content-type %r does not resolve to a valid model." % \
escape(ctype)) escape(ctype))

View File

@ -0,0 +1,8 @@
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
class ContentTypesConfig(AppConfig):
name = 'django.contrib.contenttypes'
verbose_name = _("content types")

View File

@ -1,5 +1,4 @@
from django.contrib.contenttypes.models import ContentType from django.apps import apps
from django.core.apps import app_cache, UnavailableApp
from django.db import DEFAULT_DB_ALIAS, router from django.db import DEFAULT_DB_ALIAS, router
from django.db.models import signals from django.db.models import signals
from django.utils.encoding import smart_text from django.utils.encoding import smart_text
@ -7,29 +6,32 @@ from django.utils import six
from django.utils.six.moves import input from django.utils.six.moves import input
def update_contenttypes(app, created_models, verbosity=2, db=DEFAULT_DB_ALIAS, **kwargs): def update_contenttypes(app_config, verbosity=2, interactive=True, db=DEFAULT_DB_ALIAS, **kwargs):
""" """
Creates content types for models in the given app, removing any model Creates content types for models in the given app, removing any model
entries that no longer have a matching model class. entries that no longer have a matching model class.
""" """
if not app_config.models_module:
return
try: try:
app_cache.get_model('contenttypes', 'ContentType') ContentType = apps.get_model('contenttypes', 'ContentType')
except UnavailableApp: except LookupError:
return return
if not router.allow_migrate(db, ContentType): if not router.allow_migrate(db, ContentType):
return return
ContentType.objects.clear_cache() ContentType.objects.clear_cache()
app_models = app_cache.get_models(app)
if not app_models: app_label = app_config.label
return
# They all have the same app_label, get the first one.
app_label = app_models[0]._meta.app_label
app_models = dict( app_models = dict(
(model._meta.model_name, model) (model._meta.model_name, model)
for model in app_models for model in app_config.get_models())
)
if not app_models:
return
# Get all the content types # Get all the content types
content_types = dict( content_types = dict(
@ -85,11 +87,13 @@ If you're unsure, answer 'no'.
print("Stale content types remain.") print("Stale content types remain.")
def update_all_contenttypes(verbosity=2, **kwargs): def update_all_contenttypes(**kwargs):
for app_config in app_cache.get_app_configs(only_with_models_module=True): for app_config in apps.get_app_configs():
update_contenttypes(app_config.models_module, None, verbosity, **kwargs) update_contenttypes(app_config, **kwargs)
signals.post_migrate.connect(update_contenttypes) signals.post_migrate.connect(update_contenttypes)
if __name__ == "__main__": if __name__ == "__main__":
update_all_contenttypes() update_all_contenttypes()

View File

@ -1,4 +1,4 @@
from django.core.apps import app_cache from django.apps import apps
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import smart_text, force_text from django.utils.encoding import smart_text, force_text
@ -157,8 +157,10 @@ class ContentType(models.Model):
def model_class(self): def model_class(self):
"Returns the Python model class for this type of content." "Returns the Python model class for this type of content."
return app_cache.get_model(self.app_label, self.model, try:
only_installed=False) return apps.get_model(self.app_label, self.model)
except LookupError:
return None
def get_object_for_this_type(self, **kwargs): def get_object_for_this_type(self, **kwargs):
""" """

View File

@ -1,12 +1,11 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import models
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.views import shortcut from django.contrib.contenttypes.views import shortcut
from django.contrib.sites.models import Site, get_current_site from django.contrib.sites.models import get_current_site
from django.db import models
from django.http import HttpRequest, Http404 from django.http import HttpRequest, Http404
from django.test import TestCase from django.test import TestCase, override_settings
from django.test.utils import override_settings
from django.utils.http import urlquote from django.utils.http import urlquote
from django.utils import six from django.utils import six
from django.utils.encoding import python_2_unicode_compatible from django.utils.encoding import python_2_unicode_compatible
@ -54,11 +53,9 @@ class FooWithBrokenAbsoluteUrl(FooWithoutUrl):
class ContentTypesTests(TestCase): class ContentTypesTests(TestCase):
def setUp(self): def setUp(self):
self._old_installed = Site._meta.app_config.installed
ContentType.objects.clear_cache() ContentType.objects.clear_cache()
def tearDown(self): def tearDown(self):
Site._meta.app_config.installed = self._old_installed
ContentType.objects.clear_cache() ContentType.objects.clear_cache()
def test_lookup_cache(self): def test_lookup_cache(self):
@ -223,12 +220,12 @@ class ContentTypesTests(TestCase):
user_ct = ContentType.objects.get_for_model(FooWithUrl) user_ct = ContentType.objects.get_for_model(FooWithUrl)
obj = FooWithUrl.objects.create(name="john") obj = FooWithUrl.objects.create(name="john")
Site._meta.app_config.installed = True with self.modify_settings(INSTALLED_APPS={'append': 'django.contrib.sites'}):
response = shortcut(request, user_ct.id, obj.id) response = shortcut(request, user_ct.id, obj.id)
self.assertEqual("http://%s/users/john/" % get_current_site(request).domain, self.assertEqual("http://%s/users/john/" % get_current_site(request).domain,
response._headers.get("location")[1]) response._headers.get("location")[1])
Site._meta.app_config.installed = False with self.modify_settings(INSTALLED_APPS={'remove': 'django.contrib.sites'}):
response = shortcut(request, user_ct.id, obj.id) response = shortcut(request, user_ct.id, obj.id)
self.assertEqual("http://Example.com/users/john/", self.assertEqual("http://Example.com/users/john/",
response._headers.get("location")[1]) response._headers.get("location")[1])

View File

@ -0,0 +1,8 @@
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
class FlatPagesConfig(AppConfig):
name = 'django.contrib.flatpages'
verbose_name = _("flat pages")

View File

@ -2,7 +2,7 @@ import os
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.auth.tests.utils import skipIfCustomUser from django.contrib.auth.tests.utils import skipIfCustomUser
from django.test import TestCase, Client from django.test import TestCase, Client
from django.test.utils import override_settings from django.test import override_settings
@override_settings( @override_settings(

View File

@ -3,8 +3,7 @@ from __future__ import unicode_literals
from django.conf import settings from django.conf import settings
from django.contrib.flatpages.forms import FlatpageForm from django.contrib.flatpages.forms import FlatpageForm
from django.contrib.flatpages.models import FlatPage from django.contrib.flatpages.models import FlatPage
from django.test import TestCase from django.test import TestCase, override_settings
from django.test.utils import override_settings
from django.utils import translation from django.utils import translation

View File

@ -3,8 +3,7 @@ from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.auth.tests.utils import skipIfCustomUser from django.contrib.auth.tests.utils import skipIfCustomUser
from django.contrib.flatpages.models import FlatPage from django.contrib.flatpages.models import FlatPage
from django.test import TestCase from django.test import TestCase, override_settings
from django.test.utils import override_settings
@override_settings( @override_settings(

View File

@ -2,8 +2,7 @@ import os
from django.contrib.auth.models import AnonymousUser, User from django.contrib.auth.models import AnonymousUser, User
from django.contrib.auth.tests.utils import skipIfCustomUser from django.contrib.auth.tests.utils import skipIfCustomUser
from django.template import Template, Context, TemplateSyntaxError from django.template import Template, Context, TemplateSyntaxError
from django.test import TestCase from django.test import TestCase, override_settings
from django.test.utils import override_settings
@override_settings( @override_settings(

View File

@ -3,8 +3,7 @@ from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.auth.tests.utils import skipIfCustomUser from django.contrib.auth.tests.utils import skipIfCustomUser
from django.contrib.flatpages.models import FlatPage from django.contrib.flatpages.models import FlatPage
from django.test import TestCase from django.test import TestCase, override_settings
from django.test.utils import override_settings
@override_settings( @override_settings(

View File

@ -0,0 +1,8 @@
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
class FormToolsConfig(AppConfig):
name = 'django.contrib.formtools'
verbose_name = _("form tools")

View File

@ -8,8 +8,7 @@ import warnings
from django import http from django import http
from django.contrib.formtools import preview, utils from django.contrib.formtools import preview, utils
from django.test import TestCase from django.test import TestCase, override_settings
from django.test.utils import override_settings
from django.utils._os import upath from django.utils._os import upath
from django.contrib.formtools.tests.forms import ( from django.contrib.formtools.tests.forms import (

View File

@ -0,0 +1,8 @@
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
class GISConfig(AppConfig):
name = 'django.contrib.gis'
verbose_name = _("GIS")

View File

@ -89,7 +89,7 @@ class PostGISCreation(DatabaseCreation):
self.connection.close() self.connection.close()
self.connection.settings_dict["NAME"] = test_database_name self.connection.settings_dict["NAME"] = test_database_name
cursor = self.connection.cursor() cursor = self.connection.cursor()
cursor.execute("CREATE EXTENSION postgis") cursor.execute("CREATE EXTENSION IF NOT EXISTS postgis")
cursor.connection.commit() cursor.connection.commit()
return test_database_name return test_database_name

View File

@ -6,9 +6,9 @@ for more details:
__all__ = ['HAS_GEOS'] __all__ = ['HAS_GEOS']
try: try:
from .libgeos import geos_version, geos_version_info, GEOS_PREPARE # NOQA: flake8 detects only the last __all__ from .libgeos import geos_version, geos_version_info # NOQA: flake8 detects only the last __all__
HAS_GEOS = True HAS_GEOS = True
__all__ += ['geos_version', 'geos_version_info', 'GEOS_PREPARE'] __all__ += ['geos_version', 'geos_version_info']
except ImportError: except ImportError:
HAS_GEOS = False HAS_GEOS = False

View File

@ -3,9 +3,8 @@
GeometryCollection, MultiPoint, MultiLineString, and MultiPolygon GeometryCollection, MultiPoint, MultiLineString, and MultiPolygon
""" """
from ctypes import c_int, c_uint, byref from ctypes import c_int, c_uint, byref
from django.contrib.gis.geos.error import GEOSException
from django.contrib.gis.geos.geometry import GEOSGeometry from django.contrib.gis.geos.geometry import GEOSGeometry
from django.contrib.gis.geos.libgeos import get_pointer_arr, GEOS_PREPARE from django.contrib.gis.geos.libgeos import get_pointer_arr
from django.contrib.gis.geos.linestring import LineString, LinearRing from django.contrib.gis.geos.linestring import LineString, LinearRing
from django.contrib.gis.geos.point import Point from django.contrib.gis.geos.point import Point
from django.contrib.gis.geos.polygon import Polygon from django.contrib.gis.geos.polygon import Polygon
@ -119,10 +118,7 @@ class MultiPolygon(GeometryCollection):
@property @property
def cascaded_union(self): def cascaded_union(self):
"Returns a cascaded union of this MultiPolygon." "Returns a cascaded union of this MultiPolygon."
if GEOS_PREPARE:
return GEOSGeometry(capi.geos_cascaded_union(self.ptr), self.srid) return GEOSGeometry(capi.geos_cascaded_union(self.ptr), self.srid)
else:
raise GEOSException('The cascaded union operation requires GEOS 3.1+.')
# Setting the allowed types here since GeometryCollection is defined before # Setting the allowed types here since GeometryCollection is defined before
# its subclasses. # its subclasses.

View File

@ -17,7 +17,7 @@ from django.contrib.gis.gdal.error import SRSException
from django.contrib.gis.geos.base import GEOSBase, gdal from django.contrib.gis.geos.base import GEOSBase, gdal
from django.contrib.gis.geos.coordseq import GEOSCoordSeq from django.contrib.gis.geos.coordseq import GEOSCoordSeq
from django.contrib.gis.geos.error import GEOSException, GEOSIndexError from django.contrib.gis.geos.error import GEOSException, GEOSIndexError
from django.contrib.gis.geos.libgeos import GEOM_PTR, GEOS_PREPARE from django.contrib.gis.geos.libgeos import GEOM_PTR
# All other functions in this module come from the ctypes # All other functions in this module come from the ctypes
# prototypes module -- which handles all interaction with # prototypes module -- which handles all interaction with
@ -289,8 +289,6 @@ class GEOSGeometry(GEOSBase, ListMixin):
""" """
Returns a string containing the reason for any invalidity. Returns a string containing the reason for any invalidity.
""" """
if not GEOS_PREPARE:
raise GEOSException('Upgrade GEOS to 3.1 to get validity reason.')
return capi.geos_isvalidreason(self.ptr).decode() return capi.geos_isvalidreason(self.ptr).decode()
#### Binary predicates. #### #### Binary predicates. ####
@ -411,9 +409,6 @@ class GEOSGeometry(GEOSBase, ListMixin):
extension of the WKB specification that includes SRID value that are extension of the WKB specification that includes SRID value that are
a part of this geometry. a part of this geometry.
""" """
if self.hasz and not GEOS_PREPARE:
# See: http://trac.osgeo.org/geos/ticket/216
raise GEOSException('Upgrade GEOS to 3.1 to get valid 3D HEXEWKB.')
return ewkb_w(3 if self.hasz else 2).write_hex(self) return ewkb_w(3 if self.hasz else 2).write_hex(self)
@property @property
@ -443,9 +438,6 @@ class GEOSGeometry(GEOSBase, ListMixin):
This is an extension of the WKB specification that includes any SRID This is an extension of the WKB specification that includes any SRID
value that are a part of this geometry. value that are a part of this geometry.
""" """
if self.hasz and not GEOS_PREPARE:
# See: http://trac.osgeo.org/geos/ticket/216
raise GEOSException('Upgrade GEOS to 3.1 to get valid 3D EWKB.')
return ewkb_w(3 if self.hasz else 2).write(self) return ewkb_w(3 if self.hasz else 2).write(self)
@property @property
@ -460,10 +452,7 @@ class GEOSGeometry(GEOSBase, ListMixin):
Returns a PreparedGeometry corresponding to this geometry -- it is Returns a PreparedGeometry corresponding to this geometry -- it is
optimized for the contains, intersects, and covers operations. optimized for the contains, intersects, and covers operations.
""" """
if GEOS_PREPARE:
return PreparedGeometry(self) return PreparedGeometry(self)
else:
raise GEOSException('GEOS 3.1+ required for prepared geometry support.')
#### GDAL-specific output routines #### #### GDAL-specific output routines ####
@property @property
@ -707,7 +696,9 @@ from django.contrib.gis.geos.linestring import LineString, LinearRing
from django.contrib.gis.geos.point import Point from django.contrib.gis.geos.point import Point
from django.contrib.gis.geos.polygon import Polygon from django.contrib.gis.geos.polygon import Polygon
from django.contrib.gis.geos.collections import GeometryCollection, MultiPoint, MultiLineString, MultiPolygon from django.contrib.gis.geos.collections import GeometryCollection, MultiPoint, MultiLineString, MultiPolygon
GEOS_CLASSES = {0: Point, from django.contrib.gis.geos.prepared import PreparedGeometry
GEOS_CLASSES = {
0: Point,
1: LineString, 1: LineString,
2: LinearRing, 2: LinearRing,
3: Polygon, 3: Polygon,
@ -715,8 +706,4 @@ GEOS_CLASSES = {0: Point,
5: MultiLineString, 5: MultiLineString,
6: MultiPolygon, 6: MultiPolygon,
7: GeometryCollection, 7: GeometryCollection,
} }
# If supported, import the PreparedGeometry class.
if GEOS_PREPARE:
from django.contrib.gis.geos.prepared import PreparedGeometry

View File

@ -155,22 +155,10 @@ GEOS_MINOR_VERSION = int(_verinfo['minor'])
GEOS_SUBMINOR_VERSION = int(_verinfo['subminor']) GEOS_SUBMINOR_VERSION = int(_verinfo['subminor'])
del _verinfo del _verinfo
GEOS_VERSION = (GEOS_MAJOR_VERSION, GEOS_MINOR_VERSION, GEOS_SUBMINOR_VERSION) GEOS_VERSION = (GEOS_MAJOR_VERSION, GEOS_MINOR_VERSION, GEOS_SUBMINOR_VERSION)
GEOS_PREPARE = GEOS_VERSION >= (3, 1, 0)
if GEOS_PREPARE: # Here we set up the prototypes for the initGEOS_r and finishGEOS_r
# Here we set up the prototypes for the initGEOS_r and finishGEOS_r # routines. These functions aren't actually called until they are
# routines. These functions aren't actually called until they are # attached to a GEOS context handle -- this actually occurs in
# attached to a GEOS context handle -- this actually occurs in # geos/prototypes/threadsafe.py.
# geos/prototypes/threadsafe.py. lgeos.initGEOS_r.restype = CONTEXT_PTR
lgeos.initGEOS_r.restype = CONTEXT_PTR lgeos.finishGEOS_r.argtypes = [CONTEXT_PTR]
lgeos.finishGEOS_r.argtypes = [CONTEXT_PTR]
else:
# When thread-safety isn't available, the initGEOS routine must be called
# first. This function takes the notice and error functions, defined
# as Python callbacks above, as parameters. Here is the C code that is
# wrapped:
# extern void GEOS_DLL initGEOS(GEOSMessageHandler notice_function, GEOSMessageHandler error_function);
lgeos.initGEOS(notice_h, error_h)
# Calling finishGEOS() upon exit of the interpreter.
import atexit
atexit.register(lgeos.finishGEOS)

View File

@ -1,6 +1,8 @@
from django.contrib.gis.geos.base import GEOSBase from .base import GEOSBase
from django.contrib.gis.geos.geometry import GEOSGeometry from .error import GEOSException
from django.contrib.gis.geos.prototypes import prepared as capi from .geometry import GEOSGeometry
from .libgeos import geos_version_info
from .prototypes import prepared as capi
class PreparedGeometry(GEOSBase): class PreparedGeometry(GEOSBase):
@ -12,6 +14,10 @@ class PreparedGeometry(GEOSBase):
ptr_type = capi.PREPGEOM_PTR ptr_type = capi.PREPGEOM_PTR
def __init__(self, geom): def __init__(self, geom):
# Keeping a reference to the original geometry object to prevent it
# from being garbage collected which could then crash the prepared one
# See #21662
self._base_geom = geom
if not isinstance(geom, GEOSGeometry): if not isinstance(geom, GEOSGeometry):
raise TypeError raise TypeError
self.ptr = capi.geos_prepare(geom.ptr) self.ptr = capi.geos_prepare(geom.ptr)
@ -31,3 +37,30 @@ class PreparedGeometry(GEOSBase):
def intersects(self, other): def intersects(self, other):
return capi.prepared_intersects(self.ptr, other.ptr) return capi.prepared_intersects(self.ptr, other.ptr)
# Added in GEOS 3.3:
def crosses(self, other):
if geos_version_info()['version'] < '3.3.0':
raise GEOSException("crosses on prepared geometries requires GEOS >= 3.3.0")
return capi.prepared_crosses(self.ptr, other.ptr)
def disjoint(self, other):
if geos_version_info()['version'] < '3.3.0':
raise GEOSException("disjoint on prepared geometries requires GEOS >= 3.3.0")
return capi.prepared_disjoint(self.ptr, other.ptr)
def overlaps(self, other):
if geos_version_info()['version'] < '3.3.0':
raise GEOSException("overlaps on prepared geometries requires GEOS >= 3.3.0")
return capi.prepared_overlaps(self.ptr, other.ptr)
def touches(self, other):
if geos_version_info()['version'] < '3.3.0':
raise GEOSException("touches on prepared geometries requires GEOS >= 3.3.0")
return capi.prepared_touches(self.ptr, other.ptr)
def within(self, other):
if geos_version_info()['version'] < '3.3.0':
raise GEOSException("within on prepared geometries requires GEOS >= 3.3.0")
return capi.prepared_within(self.ptr, other.ptr)

View File

@ -3,13 +3,13 @@
ones that return the area, distance, and length. ones that return the area, distance, and length.
""" """
from ctypes import c_int, c_double, POINTER from ctypes import c_int, c_double, POINTER
from django.contrib.gis.geos.libgeos import GEOM_PTR, GEOS_PREPARE from django.contrib.gis.geos.libgeos import GEOM_PTR
from django.contrib.gis.geos.prototypes.errcheck import check_dbl, check_string from django.contrib.gis.geos.prototypes.errcheck import check_dbl, check_string
from django.contrib.gis.geos.prototypes.geom import geos_char_p from django.contrib.gis.geos.prototypes.geom import geos_char_p
from django.contrib.gis.geos.prototypes.threadsafe import GEOSFunc from django.contrib.gis.geos.prototypes.threadsafe import GEOSFunc
from django.utils.six.moves import xrange from django.utils.six.moves import xrange
__all__ = ['geos_area', 'geos_distance', 'geos_length'] __all__ = ['geos_area', 'geos_distance', 'geos_length', 'geos_isvalidreason']
### ctypes generator function ### ### ctypes generator function ###
@ -31,11 +31,7 @@ def dbl_from_geom(func, num_geom=1):
geos_area = dbl_from_geom(GEOSFunc('GEOSArea')) geos_area = dbl_from_geom(GEOSFunc('GEOSArea'))
geos_distance = dbl_from_geom(GEOSFunc('GEOSDistance'), num_geom=2) geos_distance = dbl_from_geom(GEOSFunc('GEOSDistance'), num_geom=2)
geos_length = dbl_from_geom(GEOSFunc('GEOSLength')) geos_length = dbl_from_geom(GEOSFunc('GEOSLength'))
geos_isvalidreason = GEOSFunc('GEOSisValidReason')
# Validity reason; only in GEOS 3.1+ geos_isvalidreason.argtypes = [GEOM_PTR]
if GEOS_PREPARE: geos_isvalidreason.restype = geos_char_p
geos_isvalidreason = GEOSFunc('GEOSisValidReason') geos_isvalidreason.errcheck = check_string
geos_isvalidreason.argtypes = [GEOM_PTR]
geos_isvalidreason.restype = geos_char_p
geos_isvalidreason.errcheck = check_string
__all__.append('geos_isvalidreason')

View File

@ -1,5 +1,5 @@
from ctypes import c_char from ctypes import c_char
from django.contrib.gis.geos.libgeos import GEOM_PTR, PREPGEOM_PTR from django.contrib.gis.geos.libgeos import GEOM_PTR, PREPGEOM_PTR, geos_version_info
from django.contrib.gis.geos.prototypes.errcheck import check_predicate from django.contrib.gis.geos.prototypes.errcheck import check_predicate
from django.contrib.gis.geos.prototypes.threadsafe import GEOSFunc from django.contrib.gis.geos.prototypes.threadsafe import GEOSFunc
@ -24,3 +24,10 @@ prepared_contains = prepared_predicate(GEOSFunc('GEOSPreparedContains'))
prepared_contains_properly = prepared_predicate(GEOSFunc('GEOSPreparedContainsProperly')) prepared_contains_properly = prepared_predicate(GEOSFunc('GEOSPreparedContainsProperly'))
prepared_covers = prepared_predicate(GEOSFunc('GEOSPreparedCovers')) prepared_covers = prepared_predicate(GEOSFunc('GEOSPreparedCovers'))
prepared_intersects = prepared_predicate(GEOSFunc('GEOSPreparedIntersects')) prepared_intersects = prepared_predicate(GEOSFunc('GEOSPreparedIntersects'))
if geos_version_info()['version'] > '3.3.0':
prepared_crosses = prepared_predicate(GEOSFunc('GEOSPreparedCrosses'))
prepared_disjoint = prepared_predicate(GEOSFunc('GEOSPreparedDisjoint'))
prepared_overlaps = prepared_predicate(GEOSFunc('GEOSPreparedOverlaps'))
prepared_touches = prepared_predicate(GEOSFunc('GEOSPreparedTouches'))
prepared_within = prepared_predicate(GEOSFunc('GEOSPreparedWithin'))

View File

@ -2,13 +2,14 @@
This module houses the GEOS ctypes prototype functions for the This module houses the GEOS ctypes prototype functions for the
topological operations on geometries. topological operations on geometries.
""" """
__all__ = ['geos_boundary', 'geos_buffer', 'geos_centroid', 'geos_convexhull', __all__ = ['geos_boundary', 'geos_buffer', 'geos_cascaded_union',
'geos_difference', 'geos_envelope', 'geos_intersection', 'geos_centroid', 'geos_convexhull', 'geos_difference',
'geos_linemerge', 'geos_pointonsurface', 'geos_preservesimplify', 'geos_envelope', 'geos_intersection', 'geos_linemerge',
'geos_simplify', 'geos_symdifference', 'geos_union', 'geos_relate'] 'geos_pointonsurface', 'geos_preservesimplify', 'geos_simplify',
'geos_symdifference', 'geos_union', 'geos_relate']
from ctypes import c_double, c_int from ctypes import c_double, c_int
from django.contrib.gis.geos.libgeos import geos_version_info, GEOM_PTR, GEOS_PREPARE from django.contrib.gis.geos.libgeos import geos_version_info, GEOM_PTR
from django.contrib.gis.geos.prototypes.errcheck import check_geom, check_minus_one, check_string from django.contrib.gis.geos.prototypes.errcheck import check_geom, check_minus_one, check_string
from django.contrib.gis.geos.prototypes.geom import geos_char_p from django.contrib.gis.geos.prototypes.geom import geos_char_p
from django.contrib.gis.geos.prototypes.threadsafe import GEOSFunc from django.contrib.gis.geos.prototypes.threadsafe import GEOSFunc
@ -39,19 +40,16 @@ geos_simplify = topology(GEOSFunc('GEOSSimplify'), c_double)
geos_symdifference = topology(GEOSFunc('GEOSSymDifference'), GEOM_PTR) geos_symdifference = topology(GEOSFunc('GEOSSymDifference'), GEOM_PTR)
geos_union = topology(GEOSFunc('GEOSUnion'), GEOM_PTR) geos_union = topology(GEOSFunc('GEOSUnion'), GEOM_PTR)
geos_cascaded_union = GEOSFunc('GEOSUnionCascaded')
geos_cascaded_union.argtypes = [GEOM_PTR]
geos_cascaded_union.restype = GEOM_PTR
# GEOSRelate returns a string, not a geometry. # GEOSRelate returns a string, not a geometry.
geos_relate = GEOSFunc('GEOSRelate') geos_relate = GEOSFunc('GEOSRelate')
geos_relate.argtypes = [GEOM_PTR, GEOM_PTR] geos_relate.argtypes = [GEOM_PTR, GEOM_PTR]
geos_relate.restype = geos_char_p geos_relate.restype = geos_char_p
geos_relate.errcheck = check_string geos_relate.errcheck = check_string
# Routines only in GEOS 3.1+
if GEOS_PREPARE:
geos_cascaded_union = GEOSFunc('GEOSUnionCascaded')
geos_cascaded_union.argtypes = [GEOM_PTR]
geos_cascaded_union.restype = GEOM_PTR
__all__.append('geos_cascaded_union')
# Linear referencing routines # Linear referencing routines
info = geos_version_info() info = geos_version_info()
if info['version'] >= '3.2.0': if info['version'] >= '3.2.0':

View File

@ -22,8 +22,7 @@ from .. import HAS_GEOS
if HAS_GEOS: if HAS_GEOS:
from .. import (GEOSException, GEOSIndexError, GEOSGeometry, from .. import (GEOSException, GEOSIndexError, GEOSGeometry,
GeometryCollection, Point, MultiPoint, Polygon, MultiPolygon, LinearRing, GeometryCollection, Point, MultiPoint, Polygon, MultiPolygon, LinearRing,
LineString, MultiLineString, fromfile, fromstr, geos_version_info, LineString, MultiLineString, fromfile, fromstr, geos_version_info)
GEOS_PREPARE)
from ..base import gdal, numpy, GEOSBase from ..base import gdal, numpy, GEOSBase
@ -121,20 +120,12 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
# a bug in versions prior to 3.1 that puts the X coordinate in # a bug in versions prior to 3.1 that puts the X coordinate in
# place of Z; an exception should be raised on those versions. # place of Z; an exception should be raised on those versions.
self.assertEqual(hexewkb_2d, pnt_2d.hexewkb) self.assertEqual(hexewkb_2d, pnt_2d.hexewkb)
if GEOS_PREPARE:
self.assertEqual(hexewkb_3d, pnt_3d.hexewkb) self.assertEqual(hexewkb_3d, pnt_3d.hexewkb)
self.assertEqual(True, GEOSGeometry(hexewkb_3d).hasz) self.assertEqual(True, GEOSGeometry(hexewkb_3d).hasz)
else:
with self.assertRaises(GEOSException):
pnt_3d.hexewkb
# Same for EWKB. # Same for EWKB.
self.assertEqual(memoryview(a2b_hex(hexewkb_2d)), pnt_2d.ewkb) self.assertEqual(memoryview(a2b_hex(hexewkb_2d)), pnt_2d.ewkb)
if GEOS_PREPARE:
self.assertEqual(memoryview(a2b_hex(hexewkb_3d)), pnt_3d.ewkb) self.assertEqual(memoryview(a2b_hex(hexewkb_3d)), pnt_3d.ewkb)
else:
with self.assertRaises(GEOSException):
pnt_3d.ewkb
# Redundant sanity check. # Redundant sanity check.
self.assertEqual(4326, GEOSGeometry(hexewkb_2d).srid) self.assertEqual(4326, GEOSGeometry(hexewkb_2d).srid)
@ -869,7 +860,6 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
self.assertIsInstance(g1.ogr, gdal.OGRGeometry) self.assertIsInstance(g1.ogr, gdal.OGRGeometry)
self.assertIsNone(g1.srs) self.assertIsNone(g1.srs)
if GEOS_PREPARE:
g1_3d = fromstr('POINT(5 23 8)') g1_3d = fromstr('POINT(5 23 8)')
self.assertIsInstance(g1_3d.ogr, gdal.OGRGeometry) self.assertIsInstance(g1_3d.ogr, gdal.OGRGeometry)
self.assertEqual(g1_3d.ogr.z, 8) self.assertEqual(g1_3d.ogr.z, 8)
@ -918,10 +908,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
def test_transform_3d(self): def test_transform_3d(self):
p3d = GEOSGeometry('POINT (5 23 100)', 4326) p3d = GEOSGeometry('POINT (5 23 100)', 4326)
p3d.transform(2774) p3d.transform(2774)
if GEOS_PREPARE:
self.assertEqual(p3d.z, 100) self.assertEqual(p3d.z, 100)
else:
self.assertIsNone(p3d.z)
@skipUnless(HAS_GDAL, "GDAL is required.") @skipUnless(HAS_GDAL, "GDAL is required.")
def test_transform_noop(self): def test_transform_noop(self):
@ -1030,7 +1017,6 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
if not no_srid: if not no_srid:
self.assertEqual(geom.srid, tmpg.srid) self.assertEqual(geom.srid, tmpg.srid)
@skipUnless(HAS_GEOS and GEOS_PREPARE, "geos >= 3.1.0 is required")
def test_prepared(self): def test_prepared(self):
"Testing PreparedGeometry support." "Testing PreparedGeometry support."
# Creating a simple multipolygon and getting a prepared version. # Creating a simple multipolygon and getting a prepared version.
@ -1046,6 +1032,20 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
self.assertEqual(mpoly.intersects(pnt), prep.intersects(pnt)) self.assertEqual(mpoly.intersects(pnt), prep.intersects(pnt))
self.assertEqual(c, prep.covers(pnt)) self.assertEqual(c, prep.covers(pnt))
if geos_version_info()['version'] > '3.3.0':
self.assertTrue(prep.crosses(fromstr('LINESTRING(1 1, 15 15)')))
self.assertTrue(prep.disjoint(Point(-5, -5)))
poly = Polygon(((-1, -1), (1, 1), (1, 0), (-1, -1)))
self.assertTrue(prep.overlaps(poly))
poly = Polygon(((-5, 0), (-5, 5), (0, 5), (-5, 0)))
self.assertTrue(prep.touches(poly))
poly = Polygon(((-1, -1), (-1, 11), (11, 11), (11, -1), (-1, -1)))
self.assertTrue(prep.within(poly))
# Original geometry deletion should not crash the prepared one (#21662)
del mpoly
self.assertTrue(prep.covers(Point(5, 5)))
def test_line_merge(self): def test_line_merge(self):
"Testing line merge support" "Testing line merge support"
ref_geoms = (fromstr('LINESTRING(1 1, 1 1, 3 3)'), ref_geoms = (fromstr('LINESTRING(1 1, 1 1, 3 3)'),
@ -1057,7 +1057,6 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
for geom, merged in zip(ref_geoms, ref_merged): for geom, merged in zip(ref_geoms, ref_merged):
self.assertEqual(merged, geom.merged) self.assertEqual(merged, geom.merged)
@skipUnless(HAS_GEOS and GEOS_PREPARE, "geos >= 3.1.0 is required")
def test_valid_reason(self): def test_valid_reason(self):
"Testing IsValidReason support" "Testing IsValidReason support"

View File

@ -1,4 +1,6 @@
import inspect
from optparse import make_option from optparse import make_option
from django.contrib.gis import gdal from django.contrib.gis import gdal
from django.core.management.base import LabelCommand, CommandError from django.core.management.base import LabelCommand, CommandError
@ -83,27 +85,21 @@ class Command(LabelCommand):
if not gdal.HAS_GDAL: if not gdal.HAS_GDAL:
raise CommandError('GDAL is required to inspect geospatial data sources.') raise CommandError('GDAL is required to inspect geospatial data sources.')
# Removing options with `None` values.
options = dict((k, v) for k, v in options.items() if not v is None)
# Getting the OGR DataSource from the string parameter. # Getting the OGR DataSource from the string parameter.
try: try:
ds = gdal.DataSource(data_source) ds = gdal.DataSource(data_source)
except gdal.OGRException as msg: except gdal.OGRException as msg:
raise CommandError(msg) raise CommandError(msg)
# Whether the user wants to generate the LayerMapping dictionary as well.
show_mapping = options.pop('mapping', False)
# Getting rid of settings that `_ogrinspect` doesn't like.
options.pop('verbosity', False)
options.pop('settings', False)
# Returning the output of ogrinspect with the given arguments # Returning the output of ogrinspect with the given arguments
# and options. # and options.
from django.contrib.gis.utils.ogrinspect import _ogrinspect, mapping from django.contrib.gis.utils.ogrinspect import _ogrinspect, mapping
output = [s for s in _ogrinspect(ds, model_name, **options)] # Filter options to params accepted by `_ogrinspect`
if show_mapping: ogr_options = dict((k, v) for k, v in options.items()
if k in inspect.getargspec(_ogrinspect).args and v is not None)
output = [s for s in _ogrinspect(ds, model_name, **ogr_options)]
if options['mapping']:
# Constructing the keyword arguments for `mapping`, and # Constructing the keyword arguments for `mapping`, and
# calling it on the data source. # calling it on the data source.
kwargs = {'geom_name': options['geom_name'], kwargs = {'geom_name': options['geom_name'],

View File

@ -1,4 +1,4 @@
from django.core.apps import app_cache from django.apps import apps
from django.core import urlresolvers from django.core import urlresolvers
from django.contrib.sitemaps import Sitemap from django.contrib.sitemaps import Sitemap
from django.contrib.gis.db.models.fields import GeometryField from django.contrib.gis.db.models.fields import GeometryField
@ -26,7 +26,7 @@ class KMLSitemap(Sitemap):
""" """
kml_sources = [] kml_sources = []
if sources is None: if sources is None:
sources = app_cache.get_models() sources = apps.get_models()
for source in sources: for source in sources:
if isinstance(source, models.base.ModelBase): if isinstance(source, models.base.ModelBase):
for field in source._meta.fields: for field in source._meta.fields:

View File

@ -2,7 +2,7 @@ from __future__ import unicode_literals
import warnings import warnings
from django.core.apps import app_cache from django.apps import apps
from django.http import HttpResponse, Http404 from django.http import HttpResponse, Http404
from django.template import loader from django.template import loader
from django.contrib.sites.models import get_current_site from django.contrib.sites.models import get_current_site
@ -81,8 +81,9 @@ def kml(request, label, model, field_name=None, compress=False, using=DEFAULT_DB
must be that of a geographic field. must be that of a geographic field.
""" """
placemarks = [] placemarks = []
klass = app_cache.get_model(label, model) try:
if not klass: klass = apps.get_model(label, model)
except LookupError:
raise Http404('You must supply a valid app label and module name. Got "%s.%s"' % (label, model)) raise Http404('You must supply a valid app label and module name. Got "%s.%s"' % (label, model))
if field_name: if field_name:

View File

@ -3,77 +3,49 @@ from django.utils.encoding import python_2_unicode_compatible
@python_2_unicode_compatible @python_2_unicode_compatible
class SouthTexasCity(models.Model): class NamedModel(models.Model):
name = models.CharField(max_length=30)
objects = models.GeoManager()
class Meta:
abstract = True
app_label = 'distapp'
def __str__(self):
return self.name
class SouthTexasCity(NamedModel):
"City model on projected coordinate system for South Texas." "City model on projected coordinate system for South Texas."
name = models.CharField(max_length=30)
point = models.PointField(srid=32140) point = models.PointField(srid=32140)
objects = models.GeoManager()
def __str__(self):
return self.name
@python_2_unicode_compatible class SouthTexasCityFt(NamedModel):
class SouthTexasCityFt(models.Model):
"Same City model as above, but U.S. survey feet are the units." "Same City model as above, but U.S. survey feet are the units."
name = models.CharField(max_length=30)
point = models.PointField(srid=2278) point = models.PointField(srid=2278)
objects = models.GeoManager()
def __str__(self):
return self.name
@python_2_unicode_compatible class AustraliaCity(NamedModel):
class AustraliaCity(models.Model):
"City model for Australia, using WGS84." "City model for Australia, using WGS84."
name = models.CharField(max_length=30)
point = models.PointField() point = models.PointField()
objects = models.GeoManager()
def __str__(self):
return self.name
@python_2_unicode_compatible class CensusZipcode(NamedModel):
class CensusZipcode(models.Model):
"Model for a few South Texas ZIP codes (in original Census NAD83)." "Model for a few South Texas ZIP codes (in original Census NAD83)."
name = models.CharField(max_length=5)
poly = models.PolygonField(srid=4269) poly = models.PolygonField(srid=4269)
objects = models.GeoManager()
def __str__(self):
return self.name
@python_2_unicode_compatible class SouthTexasZipcode(NamedModel):
class SouthTexasZipcode(models.Model):
"Model for a few South Texas ZIP codes." "Model for a few South Texas ZIP codes."
name = models.CharField(max_length=5)
poly = models.PolygonField(srid=32140, null=True) poly = models.PolygonField(srid=32140, null=True)
objects = models.GeoManager()
def __str__(self):
return self.name
@python_2_unicode_compatible class Interstate(NamedModel):
class Interstate(models.Model):
"Geodetic model for U.S. Interstates." "Geodetic model for U.S. Interstates."
name = models.CharField(max_length=10)
path = models.LineStringField() path = models.LineStringField()
objects = models.GeoManager()
def __str__(self):
return self.name
@python_2_unicode_compatible class SouthTexasInterstate(NamedModel):
class SouthTexasInterstate(models.Model):
"Projected model for South Texas Interstates." "Projected model for South Texas Interstates."
name = models.CharField(max_length=10)
path = models.LineStringField(srid=32140) path = models.LineStringField(srid=32140)
objects = models.GeoManager()
def __str__(self):
return self.name

View File

@ -3,85 +3,63 @@ from django.utils.encoding import python_2_unicode_compatible
@python_2_unicode_compatible @python_2_unicode_compatible
class City3D(models.Model): class NamedModel(models.Model):
name = models.CharField(max_length=30) name = models.CharField(max_length=30)
point = models.PointField(dim=3)
objects = models.GeoManager() objects = models.GeoManager()
class Meta:
abstract = True
app_label = 'geo3d'
def __str__(self): def __str__(self):
return self.name return self.name
@python_2_unicode_compatible class City3D(NamedModel):
class Interstate2D(models.Model): point = models.PointField(dim=3)
name = models.CharField(max_length=30)
class Interstate2D(NamedModel):
line = models.LineStringField(srid=4269) line = models.LineStringField(srid=4269)
objects = models.GeoManager()
def __str__(self):
return self.name
@python_2_unicode_compatible class Interstate3D(NamedModel):
class Interstate3D(models.Model):
name = models.CharField(max_length=30)
line = models.LineStringField(dim=3, srid=4269) line = models.LineStringField(dim=3, srid=4269)
objects = models.GeoManager()
def __str__(self):
return self.name
@python_2_unicode_compatible class InterstateProj2D(NamedModel):
class InterstateProj2D(models.Model):
name = models.CharField(max_length=30)
line = models.LineStringField(srid=32140) line = models.LineStringField(srid=32140)
objects = models.GeoManager()
def __str__(self):
return self.name
@python_2_unicode_compatible class InterstateProj3D(NamedModel):
class InterstateProj3D(models.Model):
name = models.CharField(max_length=30)
line = models.LineStringField(dim=3, srid=32140) line = models.LineStringField(dim=3, srid=32140)
objects = models.GeoManager()
def __str__(self):
return self.name
@python_2_unicode_compatible class Polygon2D(NamedModel):
class Polygon2D(models.Model):
name = models.CharField(max_length=30)
poly = models.PolygonField(srid=32140) poly = models.PolygonField(srid=32140)
objects = models.GeoManager()
def __str__(self):
return self.name
@python_2_unicode_compatible class Polygon3D(NamedModel):
class Polygon3D(models.Model):
name = models.CharField(max_length=30)
poly = models.PolygonField(dim=3, srid=32140) poly = models.PolygonField(dim=3, srid=32140)
class SimpleModel(models.Model):
objects = models.GeoManager() objects = models.GeoManager()
def __str__(self): class Meta:
return self.name abstract = True
app_label = 'geo3d'
class Point2D(models.Model): class Point2D(SimpleModel):
point = models.PointField() point = models.PointField()
objects = models.GeoManager()
class Point3D(models.Model): class Point3D(SimpleModel):
point = models.PointField(dim=3) point = models.PointField(dim=3)
objects = models.GeoManager()
class MultiPoint3D(models.Model): class MultiPoint3D(SimpleModel):
mpoint = models.MultiPointField(dim=3) mpoint = models.MultiPointField(dim=3)
objects = models.GeoManager()

View File

@ -7,8 +7,12 @@ from django.utils.encoding import python_2_unicode_compatible
class City(models.Model): class City(models.Model):
name = models.CharField(max_length=30) name = models.CharField(max_length=30)
point = models.PointField() point = models.PointField()
objects = models.GeoManager() objects = models.GeoManager()
class Meta:
app_label = 'geoadmin'
def __str__(self): def __str__(self):
return self.name return self.name

View File

@ -4,8 +4,7 @@ from unittest import skipUnless
from django.contrib.gis.geos import HAS_GEOS from django.contrib.gis.geos import HAS_GEOS
from django.contrib.gis.tests.utils import HAS_SPATIAL_DB from django.contrib.gis.tests.utils import HAS_SPATIAL_DB
from django.test import TestCase from django.test import TestCase, override_settings
from django.test.utils import override_settings
if HAS_GEOS and HAS_SPATIAL_DB: if HAS_GEOS and HAS_SPATIAL_DB:
from django.contrib.gis import admin from django.contrib.gis import admin

View File

@ -7,66 +7,66 @@ null_flag = not mysql
@python_2_unicode_compatible @python_2_unicode_compatible
class Country(models.Model): class NamedModel(models.Model):
name = models.CharField(max_length=30) name = models.CharField(max_length=30)
objects = models.GeoManager()
class Meta:
abstract = True
app_label = 'geoapp'
def __str__(self):
return self.name
class Country(NamedModel):
mpoly = models.MultiPolygonField() # SRID, by default, is 4326 mpoly = models.MultiPolygonField() # SRID, by default, is 4326
objects = models.GeoManager()
def __str__(self):
return self.name
@python_2_unicode_compatible class City(NamedModel):
class City(models.Model):
name = models.CharField(max_length=30)
point = models.PointField() point = models.PointField()
objects = models.GeoManager()
def __str__(self):
return self.name
# This is an inherited model from City # This is an inherited model from City
class PennsylvaniaCity(City): class PennsylvaniaCity(City):
county = models.CharField(max_length=30) county = models.CharField(max_length=30)
founded = models.DateTimeField(null=True) founded = models.DateTimeField(null=True)
objects = models.GeoManager() # TODO: This should be implicitly inherited.
# TODO: This should be implicitly inherited.
objects = models.GeoManager()
class Meta:
app_label = 'geoapp'
@python_2_unicode_compatible class State(NamedModel):
class State(models.Model):
name = models.CharField(max_length=30)
poly = models.PolygonField(null=null_flag) # Allowing NULL geometries here. poly = models.PolygonField(null=null_flag) # Allowing NULL geometries here.
objects = models.GeoManager()
def __str__(self):
return self.name
@python_2_unicode_compatible class Track(NamedModel):
class Track(models.Model):
name = models.CharField(max_length=30)
line = models.LineStringField() line = models.LineStringField()
objects = models.GeoManager()
def __str__(self):
return self.name
class Truth(models.Model): class Truth(models.Model):
val = models.BooleanField(default=False) val = models.BooleanField(default=False)
objects = models.GeoManager() objects = models.GeoManager()
class Meta:
app_label = 'geoapp'
if not spatialite: if not spatialite:
@python_2_unicode_compatible
class Feature(models.Model):
name = models.CharField(max_length=20)
geom = models.GeometryField()
objects = models.GeoManager()
def __str__(self): class Feature(NamedModel):
return self.name geom = models.GeometryField()
class MinusOneSRID(models.Model): class MinusOneSRID(models.Model):
geom = models.PointField(srid=-1) # Minus one SRID. geom = models.PointField(srid=-1) # Minus one SRID.
objects = models.GeoManager() objects = models.GeoManager()
class Meta:
app_label = 'geoapp'

View File

@ -7,12 +7,13 @@ from django.conf import settings
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django.contrib.gis.geos import HAS_GEOS from django.contrib.gis.geos import HAS_GEOS
from django.contrib.gis.tests.utils import HAS_SPATIAL_DB from django.contrib.gis.tests.utils import HAS_SPATIAL_DB
from django.test import TestCase from django.test import TestCase, modify_settings
if HAS_GEOS: if HAS_GEOS:
from .models import City from .models import City
@modify_settings(INSTALLED_APPS={'append': 'django.contrib.sites'})
@skipUnless(HAS_GEOS and HAS_SPATIAL_DB, "Geos and spatial db are required.") @skipUnless(HAS_GEOS and HAS_SPATIAL_DB, "Geos and spatial db are required.")
class GeoFeedTest(TestCase): class GeoFeedTest(TestCase):
@ -20,11 +21,6 @@ class GeoFeedTest(TestCase):
def setUp(self): def setUp(self):
Site(id=settings.SITE_ID, domain="example.com", name="example.com").save() Site(id=settings.SITE_ID, domain="example.com", name="example.com").save()
self._old_installed = Site._meta.app_config.installed
Site._meta.app_config.installed = True
def tearDown(self):
Site._meta.app_config.installed = self._old_installed
def assertChildNodes(self, elem, expected): def assertChildNodes(self, elem, expected):
"Taken from syndication/tests.py." "Taken from syndication/tests.py."

View File

@ -10,7 +10,7 @@ from django.conf import settings
from django.contrib.gis.geos import HAS_GEOS from django.contrib.gis.geos import HAS_GEOS
from django.contrib.gis.tests.utils import HAS_SPATIAL_DB from django.contrib.gis.tests.utils import HAS_SPATIAL_DB
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django.test import TestCase from django.test import TestCase, modify_settings
from django.test.utils import IgnoreDeprecationWarningsMixin from django.test.utils import IgnoreDeprecationWarningsMixin
from django.utils._os import upath from django.utils._os import upath
@ -18,6 +18,7 @@ if HAS_GEOS:
from .models import City, Country from .models import City, Country
@modify_settings(INSTALLED_APPS={'append': 'django.contrib.sites'})
@skipUnless(HAS_GEOS and HAS_SPATIAL_DB, "Geos and spatial db are required.") @skipUnless(HAS_GEOS and HAS_SPATIAL_DB, "Geos and spatial db are required.")
class GeoSitemapTest(IgnoreDeprecationWarningsMixin, TestCase): class GeoSitemapTest(IgnoreDeprecationWarningsMixin, TestCase):
@ -26,12 +27,6 @@ class GeoSitemapTest(IgnoreDeprecationWarningsMixin, TestCase):
def setUp(self): def setUp(self):
super(GeoSitemapTest, self).setUp() super(GeoSitemapTest, self).setUp()
Site(id=settings.SITE_ID, domain="example.com", name="example.com").save() Site(id=settings.SITE_ID, domain="example.com", name="example.com").save()
self._old_installed = Site._meta.app_config.installed
Site._meta.app_config.installed = True
def tearDown(self):
Site._meta.app_config.installed = self._old_installed
super(GeoSitemapTest, self).tearDown()
def assertChildNodes(self, elem, expected): def assertChildNodes(self, elem, expected):
"Taken from syndication/tests.py." "Taken from syndication/tests.py."

View File

@ -3,31 +3,31 @@ from django.utils.encoding import python_2_unicode_compatible
@python_2_unicode_compatible @python_2_unicode_compatible
class City(models.Model): class NamedModel(models.Model):
name = models.CharField(max_length=30) name = models.CharField(max_length=30)
point = models.PointField(geography=True)
objects = models.GeoManager() objects = models.GeoManager()
class Meta:
abstract = True
app_label = 'geogapp'
def __str__(self): def __str__(self):
return self.name return self.name
@python_2_unicode_compatible class City(NamedModel):
class Zipcode(models.Model): point = models.PointField(geography=True)
class Zipcode(NamedModel):
code = models.CharField(max_length=10) code = models.CharField(max_length=10)
poly = models.PolygonField(geography=True) poly = models.PolygonField(geography=True)
objects = models.GeoManager()
def __str__(self):
return self.code
@python_2_unicode_compatible class County(NamedModel):
class County(models.Model):
name = models.CharField(max_length=25)
state = models.CharField(max_length=20) state = models.CharField(max_length=20)
mpoly = models.MultiPolygonField(geography=True) mpoly = models.MultiPolygonField(geography=True)
objects = models.GeoManager()
def __str__(self): def __str__(self):
return ' County, '.join([self.name, self.state]) return ' County, '.join([self.name, self.state])

View File

@ -2,6 +2,7 @@ from django.contrib.gis.db import models
class AllOGRFields(models.Model): class AllOGRFields(models.Model):
f_decimal = models.FloatField() f_decimal = models.FloatField()
f_float = models.FloatField() f_float = models.FloatField()
f_int = models.IntegerField() f_int = models.IntegerField()
@ -13,3 +14,6 @@ class AllOGRFields(models.Model):
point = models.PointField() point = models.PointField()
objects = models.GeoManager() objects = models.GeoManager()
class Meta:
app_label = 'inspectapp'

View File

@ -115,6 +115,13 @@ class OGRInspectTest(TestCase):
' objects = models.GeoManager()' ' objects = models.GeoManager()'
)) ))
def test_management_command(self):
shp_file = os.path.join(TEST_DATA, 'cities', 'cities.shp')
out = StringIO()
call_command('ogrinspect', shp_file, 'City', stdout=out)
output = out.getvalue()
self.assertIn('class City(models.Model):', output)
def get_ogr_db_string(): def get_ogr_db_string():
""" """

View File

@ -1,61 +1,75 @@
from django.contrib.gis.db import models from django.contrib.gis.db import models
from django.utils.encoding import python_2_unicode_compatible
class State(models.Model): @python_2_unicode_compatible
name = models.CharField(max_length=20) class NamedModel(models.Model):
name = models.CharField(max_length=25)
objects = models.GeoManager() objects = models.GeoManager()
class Meta:
abstract = True
app_label = 'layermap'
class County(models.Model): def __str__(self):
name = models.CharField(max_length=25) return self.name
class State(NamedModel):
pass
class County(NamedModel):
state = models.ForeignKey(State) state = models.ForeignKey(State)
mpoly = models.MultiPolygonField(srid=4269) # Multipolygon in NAD83 mpoly = models.MultiPolygonField(srid=4269) # Multipolygon in NAD83
objects = models.GeoManager()
class CountyFeat(models.Model): class CountyFeat(NamedModel):
name = models.CharField(max_length=25)
poly = models.PolygonField(srid=4269) poly = models.PolygonField(srid=4269)
objects = models.GeoManager()
class City(models.Model): class City(NamedModel):
name = models.CharField(max_length=25)
name_txt = models.TextField(default='') name_txt = models.TextField(default='')
population = models.IntegerField() population = models.IntegerField()
density = models.DecimalField(max_digits=7, decimal_places=1) density = models.DecimalField(max_digits=7, decimal_places=1)
dt = models.DateField() dt = models.DateField()
point = models.PointField() point = models.PointField()
objects = models.GeoManager()
class Interstate(models.Model): class Interstate(NamedModel):
name = models.CharField(max_length=20)
length = models.DecimalField(max_digits=6, decimal_places=2) length = models.DecimalField(max_digits=6, decimal_places=2)
path = models.LineStringField() path = models.LineStringField()
objects = models.GeoManager()
# Same as `City` above, but for testing model inheritance. # Same as `City` above, but for testing model inheritance.
class CityBase(models.Model): class CityBase(NamedModel):
name = models.CharField(max_length=25)
population = models.IntegerField() population = models.IntegerField()
density = models.DecimalField(max_digits=7, decimal_places=1) density = models.DecimalField(max_digits=7, decimal_places=1)
point = models.PointField() point = models.PointField()
objects = models.GeoManager()
class ICity1(CityBase): class ICity1(CityBase):
dt = models.DateField() dt = models.DateField()
class Meta(CityBase.Meta):
pass
class ICity2(ICity1): class ICity2(ICity1):
dt_time = models.DateTimeField(auto_now=True) dt_time = models.DateTimeField(auto_now=True)
class Meta(ICity1.Meta):
pass
class Invalid(models.Model): class Invalid(models.Model):
point = models.PointField() point = models.PointField()
class Meta:
app_label = 'layermap'
# Mapping dictionaries for the models above. # Mapping dictionaries for the models above.
co_mapping = {'name': 'Name', co_mapping = {'name': 'Name',
'state': {'name': 'State'}, # ForeignKey's use another mapping dictionary for the _related_ Model (State in this case). 'state': {'name': 'State'}, # ForeignKey's use another mapping dictionary for the _related_ Model (State in this case).

View File

@ -2,21 +2,28 @@ from django.contrib.gis.db import models
from django.utils.encoding import python_2_unicode_compatible from django.utils.encoding import python_2_unicode_compatible
@python_2_unicode_compatible class SimpleModel(models.Model):
class Location(models.Model):
point = models.PointField()
objects = models.GeoManager() objects = models.GeoManager()
class Meta:
abstract = True
app_label = 'relatedapp'
@python_2_unicode_compatible
class Location(SimpleModel):
point = models.PointField()
def __str__(self): def __str__(self):
return self.point.wkt return self.point.wkt
@python_2_unicode_compatible @python_2_unicode_compatible
class City(models.Model): class City(SimpleModel):
name = models.CharField(max_length=50) name = models.CharField(max_length=50)
state = models.CharField(max_length=2) state = models.CharField(max_length=2)
location = models.ForeignKey(Location) location = models.ForeignKey(Location)
objects = models.GeoManager()
def __str__(self): def __str__(self):
return self.name return self.name
@ -24,17 +31,20 @@ class City(models.Model):
class AugmentedLocation(Location): class AugmentedLocation(Location):
extra_text = models.TextField(blank=True) extra_text = models.TextField(blank=True)
objects = models.GeoManager() objects = models.GeoManager()
class Meta:
app_label = 'relatedapp'
class DirectoryEntry(models.Model):
class DirectoryEntry(SimpleModel):
listing_text = models.CharField(max_length=50) listing_text = models.CharField(max_length=50)
location = models.ForeignKey(AugmentedLocation) location = models.ForeignKey(AugmentedLocation)
objects = models.GeoManager()
@python_2_unicode_compatible @python_2_unicode_compatible
class Parcel(models.Model): class Parcel(SimpleModel):
name = models.CharField(max_length=30) name = models.CharField(max_length=30)
city = models.ForeignKey(City) city = models.ForeignKey(City)
center1 = models.PointField() center1 = models.PointField()
@ -42,26 +52,22 @@ class Parcel(models.Model):
center2 = models.PointField(srid=2276, db_column='mycenter') center2 = models.PointField(srid=2276, db_column='mycenter')
border1 = models.PolygonField() border1 = models.PolygonField()
border2 = models.PolygonField(srid=2276) border2 = models.PolygonField(srid=2276)
objects = models.GeoManager()
def __str__(self): def __str__(self):
return self.name return self.name
# These use the GeoManager but do not have any geographic fields. # These use the GeoManager but do not have any geographic fields.
class Author(models.Model): class Author(SimpleModel):
name = models.CharField(max_length=100) name = models.CharField(max_length=100)
dob = models.DateField() dob = models.DateField()
objects = models.GeoManager()
class Article(models.Model): class Article(SimpleModel):
title = models.CharField(max_length=100) title = models.CharField(max_length=100)
author = models.ForeignKey(Author, unique=True) author = models.ForeignKey(Author, unique=True)
objects = models.GeoManager()
class Book(models.Model): class Book(SimpleModel):
title = models.CharField(max_length=100) title = models.CharField(max_length=100)
author = models.ForeignKey(Author, related_name='books', null=True) author = models.ForeignKey(Author, related_name='books', null=True)
objects = models.GeoManager()

View File

@ -0,0 +1,8 @@
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
class HumanizeConfig(AppConfig):
name = 'django.contrib.humanize'
verbose_name = _("humanize")

View File

@ -11,8 +11,8 @@ except ImportError:
from django.conf import settings from django.conf import settings
from django.contrib.humanize.templatetags import humanize from django.contrib.humanize.templatetags import humanize
from django.template import Template, Context, defaultfilters from django.template import Template, Context, defaultfilters
from django.test import TestCase from django.test import TestCase, override_settings
from django.test.utils import override_settings, TransRealMixin from django.test.utils import TransRealMixin
from django.utils.html import escape from django.utils.html import escape
from django.utils.timezone import utc, get_fixed_timezone from django.utils.timezone import utc, get_fixed_timezone
from django.utils import translation from django.utils import translation

View File

@ -0,0 +1,8 @@
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
class MessagesConfig(AppConfig):
name = 'django.contrib.messages'
verbose_name = _("messages")

View File

@ -1,8 +1,12 @@
from django.conf import settings from django.conf import settings
from django.utils.module_loading import import_by_path as get_storage from django.utils.module_loading import import_by_path
# Callable with the same interface as the storage classes i.e. accepts a def default_storage(request):
# 'request' object. It is wrapped in a lambda to stop 'settings' being used at """
# the module level Callable with the same interface as the storage classes.
default_storage = lambda request: get_storage(settings.MESSAGE_STORAGE)(request)
This isn't just default_storage = import_by_path(settings.MESSAGE_STORAGE)
to avoid accessing the settings at the module level.
"""
return import_by_path(settings.MESSAGE_STORAGE)(request)

View File

@ -1,20 +1,21 @@
from unittest import skipIf from unittest import skipUnless
from django import http from django import http
from django.conf import settings, global_settings from django.apps import apps
from django.conf import global_settings
from django.contrib.messages import constants, utils, get_level, set_level from django.contrib.messages import constants, utils, get_level, set_level
from django.contrib.messages.api import MessageFailure from django.contrib.messages.api import MessageFailure
from django.contrib.messages.constants import DEFAULT_LEVELS from django.contrib.messages.constants import DEFAULT_LEVELS
from django.contrib.messages.storage import default_storage, base from django.contrib.messages.storage import default_storage, base
from django.contrib.messages.storage.base import Message from django.contrib.messages.storage.base import Message
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.test.utils import override_settings from django.test import modify_settings, override_settings
from django.utils.translation import ugettext_lazy from django.utils.translation import ugettext_lazy
def skipUnlessAuthIsInstalled(func): def skipUnlessAuthIsInstalled(func):
return skipIf( return skipUnless(
'django.contrib.auth' not in settings.INSTALLED_APPS, apps.is_installed('django.contrib.auth'),
"django.contrib.auth isn't installed")(func) "django.contrib.auth isn't installed")(func)
@ -218,16 +219,12 @@ class BaseTests(object):
for msg in data['messages']: for msg in data['messages']:
self.assertContains(response, msg) self.assertContains(response, msg)
@override_settings( @modify_settings(
INSTALLED_APPS=filter( INSTALLED_APPS={'remove': 'django.contrib.messages'},
lambda app: app != 'django.contrib.messages', settings.INSTALLED_APPS), MIDDLEWARE_CLASSES={'remove': 'django.contrib.messages.middleware.MessageMiddleware'},
MIDDLEWARE_CLASSES=filter( TEMPLATE_CONTEXT_PROCESSORS={'remove': 'django.contrib.messages.context_processors.messages'},
lambda m: 'MessageMiddleware' not in m, settings.MIDDLEWARE_CLASSES),
TEMPLATE_CONTEXT_PROCESSORS=filter(
lambda p: 'context_processors.messages' not in p,
settings.TEMPLATE_CONTEXT_PROCESSORS),
MESSAGE_LEVEL=constants.DEBUG
) )
@override_settings(MESSAGE_LEVEL=constants.DEBUG)
def test_middleware_disabled(self): def test_middleware_disabled(self):
""" """
Tests that, when the middleware is disabled, an exception is raised Tests that, when the middleware is disabled, an exception is raised
@ -243,15 +240,10 @@ class BaseTests(object):
self.assertRaises(MessageFailure, self.client.post, add_url, self.assertRaises(MessageFailure, self.client.post, add_url,
data, follow=True) data, follow=True)
@override_settings( @modify_settings(
INSTALLED_APPS=filter( INSTALLED_APPS={'remove': 'django.contrib.messages'},
lambda app: app != 'django.contrib.messages', settings.INSTALLED_APPS), MIDDLEWARE_CLASSES={'remove': 'django.contrib.messages.middleware.MessageMiddleware'},
MIDDLEWARE_CLASSES=filter( TEMPLATE_CONTEXT_PROCESSORS={'remove': 'django.contrib.messages.context_processors.messages'},
lambda m: 'MessageMiddleware' not in m, settings.MIDDLEWARE_CLASSES),
TEMPLATE_CONTEXT_PROCESSORS=filter(
lambda p: 'context_processors.messages' not in p,
settings.TEMPLATE_CONTEXT_PROCESSORS),
MESSAGE_LEVEL=constants.DEBUG
) )
def test_middleware_disabled_fail_silently(self): def test_middleware_disabled_fail_silently(self):
""" """

View File

@ -5,8 +5,7 @@ from django.contrib.messages.tests.base import BaseTests
from django.contrib.messages.storage.cookie import (CookieStorage, from django.contrib.messages.storage.cookie import (CookieStorage,
MessageEncoder, MessageDecoder) MessageEncoder, MessageDecoder)
from django.contrib.messages.storage.base import Message from django.contrib.messages.storage.base import Message
from django.test import TestCase from django.test import TestCase, override_settings
from django.test.utils import override_settings
from django.utils.safestring import SafeData, mark_safe from django.utils.safestring import SafeData, mark_safe

View File

@ -0,0 +1,8 @@
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
class RedirectsConfig(AppConfig):
name = 'django.contrib.redirects'
verbose_name = _("redirects")

View File

@ -1,5 +1,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.apps import apps
from django.conf import settings from django.conf import settings
from django.contrib.redirects.models import Redirect from django.contrib.redirects.models import Redirect
from django.contrib.sites.models import get_current_site from django.contrib.sites.models import get_current_site
@ -14,7 +15,7 @@ class RedirectFallbackMiddleware(object):
response_redirect_class = http.HttpResponsePermanentRedirect response_redirect_class = http.HttpResponsePermanentRedirect
def __init__(self): def __init__(self):
if 'django.contrib.sites' not in settings.INSTALLED_APPS: if not apps.is_installed('django.contrib.sites'):
raise ImproperlyConfigured( raise ImproperlyConfigured(
"You cannot use RedirectFallbackMiddleware when " "You cannot use RedirectFallbackMiddleware when "
"django.contrib.sites is not installed." "django.contrib.sites is not installed."

View File

@ -2,20 +2,16 @@ from django import http
from django.conf import settings from django.conf import settings
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.test import TestCase from django.test import TestCase, modify_settings, override_settings
from django.test.utils import override_settings
from django.utils import six from django.utils import six
from .middleware import RedirectFallbackMiddleware from .middleware import RedirectFallbackMiddleware
from .models import Redirect from .models import Redirect
@override_settings( @modify_settings(MIDDLEWARE_CLASSES={'append':
APPEND_SLASH=False, 'django.contrib.redirects.middleware.RedirectFallbackMiddleware'})
MIDDLEWARE_CLASSES=list(settings.MIDDLEWARE_CLASSES) + @override_settings(APPEND_SLASH=False, SITE_ID=1)
['django.contrib.redirects.middleware.RedirectFallbackMiddleware'],
SITE_ID=1,
)
class RedirectTests(TestCase): class RedirectTests(TestCase):
def setUp(self): def setUp(self):
@ -56,9 +52,7 @@ class RedirectTests(TestCase):
response = self.client.get('/initial') response = self.client.get('/initial')
self.assertEqual(response.status_code, 410) self.assertEqual(response.status_code, 410)
@override_settings( @modify_settings(INSTALLED_APPS={'remove': 'django.contrib.sites'})
INSTALLED_APPS=[app for app in settings.INSTALLED_APPS
if app != 'django.contrib.sites'])
def test_sites_not_installed(self): def test_sites_not_installed(self):
with self.assertRaises(ImproperlyConfigured): with self.assertRaises(ImproperlyConfigured):
RedirectFallbackMiddleware() RedirectFallbackMiddleware()
@ -70,11 +64,9 @@ class OverriddenRedirectFallbackMiddleware(RedirectFallbackMiddleware):
response_redirect_class = http.HttpResponseRedirect response_redirect_class = http.HttpResponseRedirect
@override_settings( @modify_settings(MIDDLEWARE_CLASSES={'append':
MIDDLEWARE_CLASSES=list(settings.MIDDLEWARE_CLASSES) + 'django.contrib.redirects.tests.OverriddenRedirectFallbackMiddleware'})
['django.contrib.redirects.tests.OverriddenRedirectFallbackMiddleware'], @override_settings(SITE_ID=1)
SITE_ID=1,
)
class OverriddenRedirectMiddlewareTests(TestCase): class OverriddenRedirectMiddlewareTests(TestCase):
def setUp(self): def setUp(self):

View File

@ -0,0 +1,8 @@
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
class SessionsConfig(AppConfig):
name = 'django.contrib.sessions'
verbose_name = _("sessions")

View File

@ -20,8 +20,8 @@ from django.core.cache.backends.base import InvalidCacheBackendError
from django.core import management from django.core import management
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.http import HttpResponse from django.http import HttpResponse
from django.test import TestCase, RequestFactory from django.test import TestCase, RequestFactory, override_settings
from django.test.utils import override_settings, patch_logger from django.test.utils import patch_logger
from django.utils import six from django.utils import six
from django.utils import timezone from django.utils import timezone

View File

@ -0,0 +1,8 @@
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
class SiteMapsConfig(AppConfig):
name = 'django.contrib.sitemaps'
verbose_name = _("site maps")

View File

@ -0,0 +1,22 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-01-04 23:05+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: apps.py:8
msgid "site maps"
msgstr ""

View File

@ -25,10 +25,6 @@ class SitemapTestsBase(TestCase):
def setUp(self): def setUp(self):
self.base_url = '%s://%s' % (self.protocol, self.domain) self.base_url = '%s://%s' % (self.protocol, self.domain)
self._old_installed = Site._meta.app_config.installed
cache.clear() cache.clear()
# Create an object for sitemap content. # Create an object for sitemap content.
TestModel.objects.create(name='Test Object') TestModel.objects.create(name='Test Object')
def tearDown(self):
Site._meta.app_config.installed = self._old_installed

View File

@ -2,6 +2,7 @@ from __future__ import unicode_literals
from unittest import skipUnless from unittest import skipUnless
from django.apps import apps
from django.conf import settings from django.conf import settings
from .base import SitemapTestsBase from .base import SitemapTestsBase
@ -9,7 +10,7 @@ from .base import SitemapTestsBase
class FlatpagesSitemapTests(SitemapTestsBase): class FlatpagesSitemapTests(SitemapTestsBase):
@skipUnless("django.contrib.flatpages" in settings.INSTALLED_APPS, @skipUnless(apps.is_installed('django.contrib.flatpages'),
"django.contrib.flatpages app not installed.") "django.contrib.flatpages app not installed.")
def test_flatpage_sitemap(self): def test_flatpage_sitemap(self):
"Basic FlatPage sitemap test" "Basic FlatPage sitemap test"

View File

@ -1,6 +1,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.test.utils import override_settings from django.test import override_settings
from .base import TestModel, SitemapTestsBase from .base import TestModel, SitemapTestsBase

View File

@ -4,11 +4,12 @@ import os
from datetime import date from datetime import date
from unittest import skipUnless from unittest import skipUnless
from django.apps import apps
from django.conf import settings from django.conf import settings
from django.contrib.sitemaps import Sitemap, GenericSitemap from django.contrib.sitemaps import Sitemap, GenericSitemap
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.test.utils import override_settings from django.test import modify_settings, override_settings
from django.utils.formats import localize from django.utils.formats import localize
from django.utils._os import upath from django.utils._os import upath
from django.utils.translation import activate, deactivate from django.utils.translation import activate, deactivate
@ -105,11 +106,10 @@ class HTTPSitemapTests(SitemapTestsBase):
self.assertContains(response, '<lastmod>%s</lastmod>' % date.today()) self.assertContains(response, '<lastmod>%s</lastmod>' % date.today())
deactivate() deactivate()
@modify_settings(INSTALLED_APPS={'remove': 'django.contrib.sites'})
def test_requestsite_sitemap(self): def test_requestsite_sitemap(self):
# Make sure hitting the flatpages sitemap without the sites framework # Make sure hitting the flatpages sitemap without the sites framework
# installed doesn't raise an exception. # installed doesn't raise an exception.
# Reset by SitemapTestsBase.tearDown().
Site._meta.app_config.installed = False
response = self.client.get('/simple/sitemap.xml') response = self.client.get('/simple/sitemap.xml')
expected_content = """<?xml version="1.0" encoding="UTF-8"?> expected_content = """<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
@ -118,7 +118,7 @@ class HTTPSitemapTests(SitemapTestsBase):
""" % date.today() """ % date.today()
self.assertXMLEqual(response.content.decode('utf-8'), expected_content) self.assertXMLEqual(response.content.decode('utf-8'), expected_content)
@skipUnless("django.contrib.sites" in settings.INSTALLED_APPS, @skipUnless(apps.is_installed('django.contrib.sites'),
"django.contrib.sites app not installed.") "django.contrib.sites app not installed.")
def test_sitemap_get_urls_no_site_1(self): def test_sitemap_get_urls_no_site_1(self):
""" """
@ -128,14 +128,13 @@ class HTTPSitemapTests(SitemapTestsBase):
Site.objects.all().delete() Site.objects.all().delete()
self.assertRaises(ImproperlyConfigured, Sitemap().get_urls) self.assertRaises(ImproperlyConfigured, Sitemap().get_urls)
@modify_settings(INSTALLED_APPS={'remove': 'django.contrib.sites'})
def test_sitemap_get_urls_no_site_2(self): def test_sitemap_get_urls_no_site_2(self):
""" """
Check we get ImproperlyConfigured when we don't pass a site object to Check we get ImproperlyConfigured when we don't pass a site object to
Sitemap.get_urls if Site objects exists, but the sites framework is not Sitemap.get_urls if Site objects exists, but the sites framework is not
actually installed. actually installed.
""" """
# Reset by SitemapTestsBase.tearDown().
Site._meta.app_config.installed = False
self.assertRaises(ImproperlyConfigured, Sitemap().get_urls) self.assertRaises(ImproperlyConfigured, Sitemap().get_urls)
def test_sitemap_item(self): def test_sitemap_item(self):

View File

@ -2,7 +2,7 @@ from __future__ import unicode_literals
from datetime import date from datetime import date
from django.test.utils import override_settings from django.test import override_settings
from .base import SitemapTestsBase from .base import SitemapTestsBase

View File

@ -0,0 +1,8 @@
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
class SitesConfig(AppConfig):
name = 'django.contrib.sites'
verbose_name = _("sites")

Some files were not shown because too many files have changed in this diff Show More