Merge branch 'master' into lookups_3
This commit is contained in:
commit
90e7004ec1
|
@ -15,7 +15,8 @@ Extensive contribution guidelines are available in the repository at
|
|||
|
||||
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
|
||||
|
||||
|
|
16
README.rst
16
README.rst
|
@ -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.
|
||||
|
||||
All documentation is in the "docs" directory and online at
|
||||
http://docs.djangoproject.com/en/dev/. If you're just getting started, here's
|
||||
how we recommend you read the docs:
|
||||
https://docs.djangoproject.com/en/stable/. If you're just getting started,
|
||||
here's how we recommend you read the docs:
|
||||
|
||||
* 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.
|
||||
|
||||
Docs are updated rigorously. If you find any problems in the docs, or think they
|
||||
should be clarified in any way, please take 30 seconds to fill out a ticket
|
||||
here:
|
||||
|
||||
http://code.djangoproject.com/newticket
|
||||
Docs are updated rigorously. If you find any problems in the docs, or think
|
||||
they should be clarified in any way, please take 30 seconds to fill out a
|
||||
ticket here: https://code.djangoproject.com/newticket
|
||||
|
||||
To get more help:
|
||||
|
||||
|
@ -31,11 +29,11 @@ To get more help:
|
|||
there. Read the archives at http://django-irc-logs.com/.
|
||||
|
||||
* 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:
|
||||
|
||||
* Check out http://www.djangoproject.com/community/ for information about
|
||||
* Check out https://www.djangoproject.com/community/ for information about
|
||||
getting involved.
|
||||
|
||||
To run Django's test suite:
|
||||
|
|
|
@ -6,3 +6,16 @@ def get_version(*args, **kwargs):
|
|||
# Only import if it's actually called.
|
||||
from django.utils.version import get_version
|
||||
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)
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
from .base import AppConfig # NOQA
|
||||
from .registry import apps # NOQA
|
|
@ -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.
|
||||
"""
|
|
@ -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)
|
|
@ -7,16 +7,12 @@ a list of all possible variables.
|
|||
"""
|
||||
|
||||
import importlib
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import time # Needed for Windows
|
||||
import warnings
|
||||
|
||||
from django.conf import global_settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.utils.functional import LazyObject, empty
|
||||
from django.utils.module_loading import import_by_path
|
||||
from django.utils import six
|
||||
|
||||
ENVIRONMENT_VARIABLE = "DJANGO_SETTINGS_MODULE"
|
||||
|
@ -44,34 +40,12 @@ class LazySettings(LazyObject):
|
|||
% (desc, ENVIRONMENT_VARIABLE))
|
||||
|
||||
self._wrapped = Settings(settings_module)
|
||||
self._configure_logging()
|
||||
|
||||
def __getattr__(self, name):
|
||||
if self._wrapped is empty:
|
||||
self._setup(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):
|
||||
"""
|
||||
Called to manually configure the settings. The 'default_settings'
|
||||
|
@ -84,7 +58,6 @@ class LazySettings(LazyObject):
|
|||
for name, value in options.items():
|
||||
setattr(holder, name, value)
|
||||
self._wrapped = holder
|
||||
self._configure_logging()
|
||||
|
||||
@property
|
||||
def configured(self):
|
||||
|
@ -104,11 +77,6 @@ class BaseSettings(object):
|
|||
elif name == "ALLOWED_INCLUDE_ROOTS" and isinstance(value, six.string_types):
|
||||
raise ValueError("The ALLOWED_INCLUDE_ROOTS setting must be set "
|
||||
"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)
|
||||
|
||||
|
||||
|
|
|
@ -290,7 +290,7 @@ MEDIA_URL = ''
|
|||
|
||||
# Absolute path to the directory static files should be collected to.
|
||||
# Example: "/var/www/example.com/static/"
|
||||
STATIC_ROOT = ''
|
||||
STATIC_ROOT = None
|
||||
|
||||
# URL that handles the static files served from STATIC_ROOT.
|
||||
# Example: "http://example.com/static/", "http://static.example.com/"
|
||||
|
|
|
@ -71,7 +71,6 @@ def delete_selected(modeladmin, request, queryset):
|
|||
"perms_lacking": perms_needed,
|
||||
"protected": protected,
|
||||
"opts": opts,
|
||||
"app_label": app_label,
|
||||
'action_checkbox_name': helpers.ACTION_CHECKBOX_NAME,
|
||||
}
|
||||
|
||||
|
|
|
@ -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")
|
|
@ -46,9 +46,12 @@ from django.views.decorators.csrf import csrf_protect
|
|||
IS_POPUP_VAR = '_popup'
|
||||
TO_FIELD_VAR = '_to_field'
|
||||
|
||||
|
||||
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):
|
||||
|
@ -1308,7 +1311,6 @@ class ModelAdmin(BaseModelAdmin):
|
|||
media=media,
|
||||
inline_admin_formsets=inline_admin_formsets,
|
||||
errors=helpers.AdminErrorList(form, formsets),
|
||||
app_label=opts.app_label,
|
||||
preserved_filters=self.get_preserved_filters(request),
|
||||
)
|
||||
context.update(extra_context or {})
|
||||
|
@ -1532,7 +1534,6 @@ class ModelAdmin(BaseModelAdmin):
|
|||
media=media,
|
||||
has_add_permission=self.has_add_permission(request),
|
||||
opts=cl.opts,
|
||||
app_label=app_label,
|
||||
action_form=action_form,
|
||||
actions_on_top=self.actions_on_top,
|
||||
actions_on_bottom=self.actions_on_bottom,
|
||||
|
@ -1627,7 +1628,6 @@ class ModelAdmin(BaseModelAdmin):
|
|||
action_list=action_list,
|
||||
module_name=capfirst(force_text(opts.verbose_name_plural)),
|
||||
object=obj,
|
||||
app_label=app_label,
|
||||
opts=opts,
|
||||
preserved_filters=self.get_preserved_filters(request),
|
||||
)
|
||||
|
|
|
@ -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.views.decorators.csrf import csrf_protect
|
||||
from django.db.models.base import ModelBase
|
||||
from django.apps import apps
|
||||
from django.core.exceptions import ImproperlyConfigured, PermissionDenied
|
||||
from django.core.urlresolvers import reverse, NoReverseMatch
|
||||
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.
|
||||
|
||||
The default implementation checks that LogEntry, ContentType and the
|
||||
auth context processor are installed.
|
||||
The default implementation checks that admin and contenttypes apps are
|
||||
installed, as well as the auth context processor.
|
||||
"""
|
||||
from django.contrib.admin.models import LogEntry
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
if not LogEntry._meta.installed:
|
||||
if not apps.is_installed('django.contrib.admin'):
|
||||
raise ImproperlyConfigured("Put 'django.contrib.admin' in your "
|
||||
"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 "
|
||||
"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
|
||||
'django.core.context_processors.auth' in settings.TEMPLATE_CONTEXT_PROCESSORS):
|
||||
if 'django.contrib.auth.context_processors.auth' not in settings.TEMPLATE_CONTEXT_PROCESSORS:
|
||||
raise ImproperlyConfigured("Put 'django.contrib.auth.context_processors.auth' "
|
||||
"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)
|
||||
else:
|
||||
app_dict[app_label] = {
|
||||
'name': app_label.title(),
|
||||
'name': apps.get_app_config(app_label).verbose_name,
|
||||
'app_label': app_label,
|
||||
'app_url': reverse('admin:app_list', kwargs={'app_label': app_label}, current_app=self.name),
|
||||
'has_module_perms': has_module_perms,
|
||||
|
@ -392,7 +389,7 @@ class AdminSite(object):
|
|||
|
||||
# Sort the apps alphabetically.
|
||||
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.
|
||||
for app in app_list:
|
||||
|
@ -410,6 +407,7 @@ class AdminSite(object):
|
|||
|
||||
def app_index(self, request, app_label, extra_context=None):
|
||||
user = request.user
|
||||
app_name = apps.get_app_config(app_label).verbose_name
|
||||
has_module_perms = user.has_module_perms(app_label)
|
||||
if not has_module_perms:
|
||||
raise PermissionDenied
|
||||
|
@ -444,7 +442,7 @@ class AdminSite(object):
|
|||
# something to display, add in the necessary meta
|
||||
# information.
|
||||
app_dict = {
|
||||
'name': app_label.title(),
|
||||
'name': app_name,
|
||||
'app_label': app_label,
|
||||
'app_url': '',
|
||||
'has_module_perms': has_module_perms,
|
||||
|
@ -455,7 +453,7 @@ class AdminSite(object):
|
|||
# Sort the models alphabetically within each app.
|
||||
app_dict['models'].sort(key=lambda x: x['name'])
|
||||
context = dict(self.each_context(),
|
||||
title=_('%s administration') % capfirst(app_label),
|
||||
title=_('%(app)s administration') % {'app': app_name},
|
||||
app_list=[app_dict],
|
||||
app_label=app_label,
|
||||
)
|
||||
|
|
|
@ -6,12 +6,12 @@
|
|||
<script type="text/javascript" src="{% url 'admin:jsi18n' %}"></script>
|
||||
{% 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 %}
|
||||
{% block breadcrumbs %}
|
||||
<div class="breadcrumbs">
|
||||
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
|
||||
› <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_label|capfirst|escape }}</a>
|
||||
› <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
|
||||
› <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>
|
||||
› <a href="{% url opts|admin_urlname:'change' original.pk|admin_urlquote %}">{{ original|truncatewords:"18" }}</a>
|
||||
› {% trans 'Change password' %}
|
||||
|
|
|
@ -10,13 +10,13 @@
|
|||
|
||||
{% 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 %}
|
||||
{% block breadcrumbs %}
|
||||
<div class="breadcrumbs">
|
||||
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
|
||||
› <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ app_label|capfirst|escape }}</a>
|
||||
› <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
|
||||
› {% if has_change_permission %}<a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>{% else %}{{ opts.verbose_name_plural|capfirst }}{% endif %}
|
||||
› {% if add %}{% trans 'Add' %} {{ opts.verbose_name }}{% else %}{{ original|truncatewords:"18" }}{% endif %}
|
||||
</div>
|
||||
|
|
|
@ -32,13 +32,13 @@
|
|||
{% endif %}{% endif %}
|
||||
{% 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 %}
|
||||
{% block breadcrumbs %}
|
||||
<div class="breadcrumbs">
|
||||
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
|
||||
› <a href="{% url 'admin:app_list' app_label=cl.opts.app_label %}">{{ app_label|capfirst|escape }}</a>
|
||||
› <a href="{% url 'admin:app_list' app_label=cl.opts.app_label %}">{{ cl.opts.app_config.verbose_name }}</a>
|
||||
› {{ cl.opts.verbose_name_plural|capfirst }}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
{% extends "admin/base_site.html" %}
|
||||
{% 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 %}
|
||||
<div class="breadcrumbs">
|
||||
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
|
||||
› <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ app_label|capfirst }}</a>
|
||||
› <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
|
||||
› <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst|escape }}</a>
|
||||
› <a href="{% url opts|admin_urlname:'change' object.pk|admin_urlquote %}">{{ object|truncatewords:"18" }}</a>
|
||||
› {% trans 'Delete' %}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
{% extends "admin/base_site.html" %}
|
||||
{% 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 %}
|
||||
<div class="breadcrumbs">
|
||||
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
|
||||
› <a href="{% url 'admin:app_list' app_label=app_label %}">{{ app_label|capfirst|escape }}</a>
|
||||
› <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
|
||||
› <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>
|
||||
› {% trans 'Delete multiple objects' %}
|
||||
</div>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
{% block breadcrumbs %}
|
||||
<div class="breadcrumbs">
|
||||
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
|
||||
› <a href="{% url 'admin:app_list' app_label=app_label %}">{{ app_label|capfirst|escape }}</a>
|
||||
› <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
|
||||
› <a href="{% url opts|admin_urlname:'changelist' %}">{{ module_name }}</a>
|
||||
› <a href="{% url opts|admin_urlname:'change' object.pk|admin_urlquote %}">{{ object|truncatewords:"18" }}</a>
|
||||
› {% trans 'History' %}
|
||||
|
|
|
@ -333,7 +333,7 @@ def date_hierarchy(cl):
|
|||
month_lookup = cl.params.get(month_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):
|
||||
# select appropriate start level
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
from django.conf import settings
|
||||
from django.apps import apps
|
||||
from django.template import 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
|
||||
else:
|
||||
from django.templatetags.static import static
|
||||
|
|
|
@ -32,7 +32,6 @@ class AdminSeleniumWebDriverTestCase(StaticLiveServerCase):
|
|||
@classmethod
|
||||
def _tearDownClassInternal(cls):
|
||||
if hasattr(cls, 'selenium'):
|
||||
cls.selenium.refresh() # see ticket #21227
|
||||
cls.selenium.quit()
|
||||
super(AdminSeleniumWebDriverTestCase, cls)._tearDownClassInternal()
|
||||
|
||||
|
@ -51,8 +50,40 @@ class AdminSeleniumWebDriverTestCase(StaticLiveServerCase):
|
|||
Helper function that blocks until the element with the given tag name
|
||||
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(
|
||||
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
|
||||
)
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
from django.core.apps import app_cache
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.db import models
|
||||
from django.db.models.fields import FieldDoesNotExist
|
||||
|
@ -15,10 +14,6 @@ __all__ = ['BaseValidator', 'InlineValidator']
|
|||
|
||||
|
||||
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):
|
||||
for m in dir(self):
|
||||
|
|
|
@ -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")
|
|
@ -27,7 +27,7 @@
|
|||
<table class="xfull">
|
||||
{% for model in group.list %}
|
||||
<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>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
|
|
@ -5,10 +5,10 @@ import re
|
|||
import warnings
|
||||
|
||||
from django import template
|
||||
from django.apps import apps
|
||||
from django.conf import settings
|
||||
from django.contrib import admin
|
||||
from django.contrib.admin.views.decorators import staff_member_required
|
||||
from django.core.apps import app_cache
|
||||
from django.db import models
|
||||
from django.core.exceptions import ViewDoesNotExist
|
||||
from django.http import Http404
|
||||
|
@ -174,7 +174,7 @@ class ModelIndexView(BaseAdminDocsView):
|
|||
template_name = 'admin_doc/model_index.html'
|
||||
|
||||
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})
|
||||
return super(ModelIndexView, self).get_context_data(**kwargs)
|
||||
|
||||
|
@ -185,11 +185,12 @@ class ModelDetailView(BaseAdminDocsView):
|
|||
def get_context_data(self, **kwargs):
|
||||
# Get the model class.
|
||||
try:
|
||||
app_cache.get_app_config(self.kwargs['app_label'])
|
||||
apps.get_app_config(self.kwargs['app_label'])
|
||||
except LookupError:
|
||||
raise Http404(_("App %(app_label)r not found") % self.kwargs)
|
||||
model = app_cache.get_model(self.kwargs['app_label'], self.kwargs['model_name'])
|
||||
if model is None:
|
||||
try:
|
||||
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)
|
||||
|
||||
opts = model._meta
|
||||
|
|
|
@ -123,14 +123,15 @@ def get_user_model():
|
|||
"""
|
||||
Returns the User model that is active in this project.
|
||||
"""
|
||||
from django.core.apps import app_cache
|
||||
from django.apps import apps
|
||||
|
||||
try:
|
||||
app_label, model_name = settings.AUTH_USER_MODEL.split('.')
|
||||
except ValueError:
|
||||
raise ImproperlyConfigured("AUTH_USER_MODEL must be of the form 'app_label.model_name'")
|
||||
user_model = app_cache.get_model(app_label, model_name)
|
||||
if user_model is None:
|
||||
try:
|
||||
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)
|
||||
return user_model
|
||||
|
||||
|
|
|
@ -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")
|
|
@ -21,7 +21,11 @@ from django.contrib.sites.models import get_current_site
|
|||
|
||||
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):
|
||||
|
|
|
@ -6,9 +6,9 @@ from __future__ import unicode_literals
|
|||
import getpass
|
||||
import unicodedata
|
||||
|
||||
from django.apps import apps
|
||||
from django.contrib.auth import (models as auth_app, get_permission_codename,
|
||||
get_user_model)
|
||||
from django.core.apps import app_cache, UnavailableApp
|
||||
from django.core import exceptions
|
||||
from django.core.management.base import CommandError
|
||||
from django.db import DEFAULT_DB_ALIAS, router
|
||||
|
@ -60,25 +60,26 @@ def _check_permission_clashing(custom, builtin, ctype):
|
|||
pool.add(codename)
|
||||
|
||||
|
||||
def create_permissions(app, created_models, verbosity, db=DEFAULT_DB_ALIAS, **kwargs):
|
||||
try:
|
||||
app_cache.get_model('auth', 'Permission')
|
||||
except UnavailableApp:
|
||||
def create_permissions(app_config, verbosity=2, interactive=True, db=DEFAULT_DB_ALIAS, **kwargs):
|
||||
if not app_config.models_module:
|
||||
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
|
||||
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
app_models = app_cache.get_models(app)
|
||||
|
||||
# This will hold the permissions we're looking for as
|
||||
# (content_type, (codename, name))
|
||||
searched_perms = list()
|
||||
# The codenames and ctypes that should exist.
|
||||
ctypes = set()
|
||||
for klass in app_models:
|
||||
for klass in app_config.get_models():
|
||||
# Force looking up the content types in the current database
|
||||
# before creating foreign keys to them.
|
||||
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
|
||||
# looking for. We don't need to check for codenames since we already have
|
||||
# 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,
|
||||
).values_list(
|
||||
"content_type", "codename"
|
||||
))
|
||||
|
||||
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
|
||||
if (ctype.pk, codename) not in all_perms
|
||||
]
|
||||
# Validate the permissions before bulk_creation to avoid cryptic
|
||||
# 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
|
||||
for perm in perms:
|
||||
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,
|
||||
)
|
||||
)
|
||||
auth_app.Permission.objects.using(db).bulk_create(perms)
|
||||
Permission.objects.using(db).bulk_create(perms)
|
||||
if verbosity >= 2:
|
||||
for perm in perms:
|
||||
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:
|
||||
app_cache.get_model('auth', 'Permission')
|
||||
UserModel = get_user_model()
|
||||
except UnavailableApp:
|
||||
apps.get_model('auth', 'Permission')
|
||||
except LookupError:
|
||||
return
|
||||
|
||||
UserModel = get_user_model()
|
||||
|
||||
from django.core.management import call_command
|
||||
|
||||
if UserModel in created_models and kwargs.get('interactive', True):
|
||||
msg = ("\nYou just installed Django's auth system, which means you "
|
||||
if not UserModel.objects.exists() and interactive:
|
||||
msg = ("\nYou have installed Django's auth system, and "
|
||||
"don't have any superusers defined.\nWould you like to create one "
|
||||
"now? (yes/no): ")
|
||||
confirm = input(msg)
|
||||
|
@ -202,7 +204,9 @@ def get_default_username(check_db=True):
|
|||
return ''
|
||||
return default_username
|
||||
|
||||
|
||||
signals.post_migrate.connect(create_permissions,
|
||||
dispatch_uid="django.contrib.auth.management.create_permissions")
|
||||
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")
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
import getpass
|
||||
from optparse import make_option
|
||||
|
||||
|
|
|
@ -10,8 +10,7 @@ from django.contrib.contenttypes.models import ContentType
|
|||
from django.core.exceptions import ImproperlyConfigured, PermissionDenied
|
||||
from django.contrib.auth import authenticate, get_user
|
||||
from django.http import HttpRequest
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
from django.test import TestCase, override_settings
|
||||
from django.contrib.auth.hashers import MD5PasswordHasher
|
||||
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import unicode_literals
|
|||
|
||||
import locale
|
||||
|
||||
from django.apps import apps
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.management.commands import createsuperuser
|
||||
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.management import call_command
|
||||
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.utils import override_settings
|
||||
from django.utils import translation
|
||||
from django.utils.encoding import force_str
|
||||
from django.utils.six import binary_type, PY2, StringIO
|
||||
|
@ -26,6 +26,7 @@ def user_model_swapped(**kwargs):
|
|||
# Reset User manager
|
||||
setattr(User, 'objects', User._default_manager)
|
||||
ensure_default_manager(User)
|
||||
apps.clear_cache()
|
||||
|
||||
|
||||
def mock_inputs(inputs):
|
||||
|
|
|
@ -7,8 +7,7 @@ from django.contrib.auth.models import User, Permission
|
|||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.contrib.auth.context_processors import PermWrapper, PermLookupDict
|
||||
from django.db.models import Q
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
from django.test import TestCase, override_settings
|
||||
from django.utils._os import upath
|
||||
|
||||
|
||||
|
|
|
@ -12,8 +12,7 @@ from django.contrib.auth.forms import (UserCreationForm, AuthenticationForm,
|
|||
from django.contrib.auth.tests.utils import skipIfCustomUser
|
||||
from django.core import mail
|
||||
from django.forms.fields import Field, CharField
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
from django.test import TestCase, override_settings
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils._os import upath
|
||||
from django.utils import translation
|
||||
|
|
|
@ -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.utils import skipIfCustomUser
|
||||
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
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from __future__ import unicode_literals
|
||||
from datetime import date
|
||||
|
||||
from django.apps import apps
|
||||
from django.contrib.auth import models, management
|
||||
from django.contrib.auth.management import create_permissions
|
||||
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.utils import skipIfCustomUser
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.apps import app_cache
|
||||
from django.core import exceptions
|
||||
from django.core.management import call_command
|
||||
from django.core.management.base import CommandError
|
||||
from django.core.management.validation import get_validation_errors
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
from django.test import TestCase, override_settings
|
||||
from django.utils import six
|
||||
from django.utils.six import StringIO
|
||||
|
||||
|
@ -83,6 +82,19 @@ class ChangepasswordManagementCommandTestCase(TestCase):
|
|||
with self.assertRaises(CommandError):
|
||||
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
|
||||
class CreatesuperuserManagementCommandTestCase(TestCase):
|
||||
|
@ -184,21 +196,21 @@ class CustomUserModelValidationTestCase(TestCase):
|
|||
def test_required_fields_is_list(self):
|
||||
"REQUIRED_FIELDS should be a list."
|
||||
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())
|
||||
|
||||
@override_settings(AUTH_USER_MODEL='auth.CustomUserBadRequiredFields')
|
||||
def test_username_not_in_required_fields(self):
|
||||
"USERNAME_FIELD should not appear in REQUIRED_FIELDS."
|
||||
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())
|
||||
|
||||
@override_settings(AUTH_USER_MODEL='auth.CustomUserNonUniqueUsername')
|
||||
def test_username_non_unique(self):
|
||||
"A non-unique USERNAME_FIELD should raise a model validation error."
|
||||
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())
|
||||
|
||||
|
||||
|
@ -220,13 +232,15 @@ class PermissionTestCase(TestCase):
|
|||
Test that we show proper error message if we are trying to create
|
||||
duplicate permissions.
|
||||
"""
|
||||
auth_app_config = apps.get_app_config('auth')
|
||||
|
||||
# check duplicated default permission
|
||||
models.Permission._meta.permissions = [
|
||||
('change_permission', 'Can edit permission (duplicate)')]
|
||||
six.assertRaisesRegex(self, CommandError,
|
||||
"The permission codename 'change_permission' clashes with a "
|
||||
"builtin permission for model 'auth.Permission'.",
|
||||
create_permissions, models, [], verbosity=0)
|
||||
create_permissions, auth_app_config, verbosity=0)
|
||||
|
||||
# check duplicated custom permissions
|
||||
models.Permission._meta.permissions = [
|
||||
|
@ -237,21 +251,23 @@ class PermissionTestCase(TestCase):
|
|||
six.assertRaisesRegex(self, CommandError,
|
||||
"The permission codename 'my_custom_permission' is duplicated for model "
|
||||
"'auth.Permission'.",
|
||||
create_permissions, models, [], verbosity=0)
|
||||
create_permissions, auth_app_config, verbosity=0)
|
||||
|
||||
# should not raise anything
|
||||
models.Permission._meta.permissions = [
|
||||
('my_custom_permission', 'Some permission'),
|
||||
('other_one', 'Some other permission'),
|
||||
]
|
||||
create_permissions(models, [], verbosity=0)
|
||||
create_permissions(auth_app_config, verbosity=0)
|
||||
|
||||
def test_default_permissions(self):
|
||||
auth_app_config = apps.get_app_config('auth')
|
||||
|
||||
permission_content_type = ContentType.objects.get_by_natural_key('auth', 'permission')
|
||||
models.Permission._meta.permissions = [
|
||||
('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
|
||||
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._meta.default_permissions = []
|
||||
create_permissions(models, [], verbosity=0)
|
||||
create_permissions(auth_app_config, verbosity=0)
|
||||
|
||||
# custom permission only since default permissions is empty
|
||||
self.assertEqual(models.Permission.objects.filter(
|
||||
|
@ -268,10 +284,12 @@ class PermissionTestCase(TestCase):
|
|||
).count(), 1)
|
||||
|
||||
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')
|
||||
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"
|
||||
|
||||
six.assertRaisesRegex(self, exceptions.ValidationError,
|
||||
"The verbose_name of permission is longer than 39 characters",
|
||||
create_permissions, models, [], verbosity=0)
|
||||
create_permissions, auth_app_config, verbosity=0)
|
||||
|
|
|
@ -3,8 +3,7 @@ from django.contrib.auth.models import AbstractUser, Group, User, UserManager
|
|||
from django.contrib.auth.tests.utils import skipIfCustomUser
|
||||
from django.core import mail
|
||||
from django.db.models.signals import post_save
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
from django.test import TestCase, override_settings
|
||||
|
||||
|
||||
@skipIfCustomUser
|
||||
|
|
|
@ -3,7 +3,7 @@ from django.contrib.auth.models import User
|
|||
from django.contrib.auth.tests.utils import skipIfCustomUser
|
||||
from django.test import TestCase
|
||||
from django.test.client import RequestFactory
|
||||
from django.test.utils import override_settings
|
||||
from django.test import override_settings
|
||||
|
||||
|
||||
@skipIfCustomUser
|
||||
|
|
|
@ -7,7 +7,7 @@ from django.contrib.auth.views import (
|
|||
password_reset_complete, password_change, password_change_done,
|
||||
)
|
||||
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.http import urlsafe_base64_encode
|
||||
|
||||
|
|
|
@ -14,8 +14,8 @@ from django.utils.encoding import force_text
|
|||
from django.utils.http import urlquote
|
||||
from django.utils.six.moves.urllib.parse import urlparse, ParseResult
|
||||
from django.utils._os import upath
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings, patch_logger
|
||||
from django.test import TestCase, override_settings
|
||||
from django.test.utils import patch_logger
|
||||
from django.middleware.csrf import CsrfViewMiddleware
|
||||
from django.contrib.sessions.middleware import SessionMiddleware
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from importlib import import_module
|
||||
import warnings
|
||||
from django.apps import apps
|
||||
from django.conf import settings
|
||||
from django.core import urlresolvers
|
||||
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
|
||||
"""
|
||||
# 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:
|
||||
package = import_module(comments_app)
|
||||
except ImportError as e:
|
||||
raise ImproperlyConfigured("The COMMENTS_APP setting refers to "\
|
||||
"a non-existing package. (%s)" % e)
|
||||
|
||||
return package
|
||||
app_config = apps.get_app_config(get_comment_app_name().rpartition(".")[2])
|
||||
except LookupError:
|
||||
raise ImproperlyConfigured("The COMMENTS_APP (%r) "
|
||||
"must be in INSTALLED_APPS" % settings.COMMENTS_APP)
|
||||
return app_config.module
|
||||
|
||||
def get_comment_app_name():
|
||||
"""
|
||||
|
|
|
@ -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")
|
|
@ -1,9 +1,9 @@
|
|||
from django import http
|
||||
from django.apps import apps
|
||||
from django.conf import settings
|
||||
from django.contrib import comments
|
||||
from django.contrib.comments import signals
|
||||
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.db import models
|
||||
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:
|
||||
return CommentPostBadRequest("Missing content_type or object_pk field.")
|
||||
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)
|
||||
except TypeError:
|
||||
return CommentPostBadRequest(
|
||||
"Invalid content_type value: %r" % escape(ctype))
|
||||
except AttributeError:
|
||||
except LookupError:
|
||||
return CommentPostBadRequest(
|
||||
"The given content-type %r does not resolve to a valid model." % \
|
||||
escape(ctype))
|
||||
|
|
|
@ -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")
|
|
@ -1,5 +1,4 @@
|
|||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.apps import app_cache, UnavailableApp
|
||||
from django.apps import apps
|
||||
from django.db import DEFAULT_DB_ALIAS, router
|
||||
from django.db.models import signals
|
||||
from django.utils.encoding import smart_text
|
||||
|
@ -7,29 +6,32 @@ from django.utils import six
|
|||
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
|
||||
entries that no longer have a matching model class.
|
||||
"""
|
||||
if not app_config.models_module:
|
||||
return
|
||||
|
||||
try:
|
||||
app_cache.get_model('contenttypes', 'ContentType')
|
||||
except UnavailableApp:
|
||||
ContentType = apps.get_model('contenttypes', 'ContentType')
|
||||
except LookupError:
|
||||
return
|
||||
|
||||
if not router.allow_migrate(db, ContentType):
|
||||
return
|
||||
|
||||
ContentType.objects.clear_cache()
|
||||
app_models = app_cache.get_models(app)
|
||||
if not app_models:
|
||||
return
|
||||
# They all have the same app_label, get the first one.
|
||||
app_label = app_models[0]._meta.app_label
|
||||
|
||||
app_label = app_config.label
|
||||
|
||||
app_models = dict(
|
||||
(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
|
||||
content_types = dict(
|
||||
|
@ -85,11 +87,13 @@ If you're unsure, answer 'no'.
|
|||
print("Stale content types remain.")
|
||||
|
||||
|
||||
def update_all_contenttypes(verbosity=2, **kwargs):
|
||||
for app_config in app_cache.get_app_configs(only_with_models_module=True):
|
||||
update_contenttypes(app_config.models_module, None, verbosity, **kwargs)
|
||||
def update_all_contenttypes(**kwargs):
|
||||
for app_config in apps.get_app_configs():
|
||||
update_contenttypes(app_config, **kwargs)
|
||||
|
||||
|
||||
signals.post_migrate.connect(update_contenttypes)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
update_all_contenttypes()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from django.core.apps import app_cache
|
||||
from django.apps import apps
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.encoding import smart_text, force_text
|
||||
|
@ -157,8 +157,10 @@ class ContentType(models.Model):
|
|||
|
||||
def model_class(self):
|
||||
"Returns the Python model class for this type of content."
|
||||
return app_cache.get_model(self.app_label, self.model,
|
||||
only_installed=False)
|
||||
try:
|
||||
return apps.get_model(self.app_label, self.model)
|
||||
except LookupError:
|
||||
return None
|
||||
|
||||
def get_object_for_this_type(self, **kwargs):
|
||||
"""
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
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.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
from django.test import TestCase, override_settings
|
||||
from django.utils.http import urlquote
|
||||
from django.utils import six
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
|
@ -54,11 +53,9 @@ class FooWithBrokenAbsoluteUrl(FooWithoutUrl):
|
|||
class ContentTypesTests(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self._old_installed = Site._meta.app_config.installed
|
||||
ContentType.objects.clear_cache()
|
||||
|
||||
def tearDown(self):
|
||||
Site._meta.app_config.installed = self._old_installed
|
||||
ContentType.objects.clear_cache()
|
||||
|
||||
def test_lookup_cache(self):
|
||||
|
@ -223,15 +220,15 @@ class ContentTypesTests(TestCase):
|
|||
user_ct = ContentType.objects.get_for_model(FooWithUrl)
|
||||
obj = FooWithUrl.objects.create(name="john")
|
||||
|
||||
Site._meta.app_config.installed = True
|
||||
response = shortcut(request, user_ct.id, obj.id)
|
||||
self.assertEqual("http://%s/users/john/" % get_current_site(request).domain,
|
||||
response._headers.get("location")[1])
|
||||
with self.modify_settings(INSTALLED_APPS={'append': 'django.contrib.sites'}):
|
||||
response = shortcut(request, user_ct.id, obj.id)
|
||||
self.assertEqual("http://%s/users/john/" % get_current_site(request).domain,
|
||||
response._headers.get("location")[1])
|
||||
|
||||
Site._meta.app_config.installed = False
|
||||
response = shortcut(request, user_ct.id, obj.id)
|
||||
self.assertEqual("http://Example.com/users/john/",
|
||||
response._headers.get("location")[1])
|
||||
with self.modify_settings(INSTALLED_APPS={'remove': 'django.contrib.sites'}):
|
||||
response = shortcut(request, user_ct.id, obj.id)
|
||||
self.assertEqual("http://Example.com/users/john/",
|
||||
response._headers.get("location")[1])
|
||||
|
||||
def test_shortcut_view_without_get_absolute_url(self):
|
||||
"""
|
||||
|
|
|
@ -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")
|
|
@ -2,7 +2,7 @@ import os
|
|||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.tests.utils import skipIfCustomUser
|
||||
from django.test import TestCase, Client
|
||||
from django.test.utils import override_settings
|
||||
from django.test import override_settings
|
||||
|
||||
|
||||
@override_settings(
|
||||
|
|
|
@ -3,8 +3,7 @@ from __future__ import unicode_literals
|
|||
from django.conf import settings
|
||||
from django.contrib.flatpages.forms import FlatpageForm
|
||||
from django.contrib.flatpages.models import FlatPage
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
from django.test import TestCase, override_settings
|
||||
from django.utils import translation
|
||||
|
||||
|
||||
|
|
|
@ -3,8 +3,7 @@ from django.conf import settings
|
|||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.tests.utils import skipIfCustomUser
|
||||
from django.contrib.flatpages.models import FlatPage
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
from django.test import TestCase, override_settings
|
||||
|
||||
|
||||
@override_settings(
|
||||
|
|
|
@ -2,8 +2,7 @@ import os
|
|||
from django.contrib.auth.models import AnonymousUser, User
|
||||
from django.contrib.auth.tests.utils import skipIfCustomUser
|
||||
from django.template import Template, Context, TemplateSyntaxError
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
from django.test import TestCase, override_settings
|
||||
|
||||
|
||||
@override_settings(
|
||||
|
|
|
@ -3,8 +3,7 @@ from django.conf import settings
|
|||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.tests.utils import skipIfCustomUser
|
||||
from django.contrib.flatpages.models import FlatPage
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
from django.test import TestCase, override_settings
|
||||
|
||||
|
||||
@override_settings(
|
||||
|
|
|
@ -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")
|
|
@ -8,8 +8,7 @@ import warnings
|
|||
|
||||
from django import http
|
||||
from django.contrib.formtools import preview, utils
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
from django.test import TestCase, override_settings
|
||||
from django.utils._os import upath
|
||||
|
||||
from django.contrib.formtools.tests.forms import (
|
||||
|
|
|
@ -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")
|
|
@ -89,7 +89,7 @@ class PostGISCreation(DatabaseCreation):
|
|||
self.connection.close()
|
||||
self.connection.settings_dict["NAME"] = test_database_name
|
||||
cursor = self.connection.cursor()
|
||||
cursor.execute("CREATE EXTENSION postgis")
|
||||
cursor.execute("CREATE EXTENSION IF NOT EXISTS postgis")
|
||||
cursor.connection.commit()
|
||||
|
||||
return test_database_name
|
||||
|
|
|
@ -6,9 +6,9 @@ for more details:
|
|||
__all__ = ['HAS_GEOS']
|
||||
|
||||
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
|
||||
__all__ += ['geos_version', 'geos_version_info', 'GEOS_PREPARE']
|
||||
__all__ += ['geos_version', 'geos_version_info']
|
||||
except ImportError:
|
||||
HAS_GEOS = False
|
||||
|
||||
|
|
|
@ -3,9 +3,8 @@
|
|||
GeometryCollection, MultiPoint, MultiLineString, and MultiPolygon
|
||||
"""
|
||||
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.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.point import Point
|
||||
from django.contrib.gis.geos.polygon import Polygon
|
||||
|
@ -119,10 +118,7 @@ class MultiPolygon(GeometryCollection):
|
|||
@property
|
||||
def cascaded_union(self):
|
||||
"Returns a cascaded union of this MultiPolygon."
|
||||
if GEOS_PREPARE:
|
||||
return GEOSGeometry(capi.geos_cascaded_union(self.ptr), self.srid)
|
||||
else:
|
||||
raise GEOSException('The cascaded union operation requires GEOS 3.1+.')
|
||||
return GEOSGeometry(capi.geos_cascaded_union(self.ptr), self.srid)
|
||||
|
||||
# Setting the allowed types here since GeometryCollection is defined before
|
||||
# its subclasses.
|
||||
|
|
|
@ -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.coordseq import GEOSCoordSeq
|
||||
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
|
||||
# prototypes module -- which handles all interaction with
|
||||
|
@ -289,8 +289,6 @@ class GEOSGeometry(GEOSBase, ListMixin):
|
|||
"""
|
||||
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()
|
||||
|
||||
#### Binary predicates. ####
|
||||
|
@ -411,9 +409,6 @@ class GEOSGeometry(GEOSBase, ListMixin):
|
|||
extension of the WKB specification that includes SRID 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 HEXEWKB.')
|
||||
return ewkb_w(3 if self.hasz else 2).write_hex(self)
|
||||
|
||||
@property
|
||||
|
@ -443,9 +438,6 @@ class GEOSGeometry(GEOSBase, ListMixin):
|
|||
This is an extension of the WKB specification that includes any SRID
|
||||
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)
|
||||
|
||||
@property
|
||||
|
@ -460,10 +452,7 @@ class GEOSGeometry(GEOSBase, ListMixin):
|
|||
Returns a PreparedGeometry corresponding to this geometry -- it is
|
||||
optimized for the contains, intersects, and covers operations.
|
||||
"""
|
||||
if GEOS_PREPARE:
|
||||
return PreparedGeometry(self)
|
||||
else:
|
||||
raise GEOSException('GEOS 3.1+ required for prepared geometry support.')
|
||||
return PreparedGeometry(self)
|
||||
|
||||
#### GDAL-specific output routines ####
|
||||
@property
|
||||
|
@ -707,16 +696,14 @@ from django.contrib.gis.geos.linestring import LineString, LinearRing
|
|||
from django.contrib.gis.geos.point import Point
|
||||
from django.contrib.gis.geos.polygon import Polygon
|
||||
from django.contrib.gis.geos.collections import GeometryCollection, MultiPoint, MultiLineString, MultiPolygon
|
||||
GEOS_CLASSES = {0: Point,
|
||||
1: LineString,
|
||||
2: LinearRing,
|
||||
3: Polygon,
|
||||
4: MultiPoint,
|
||||
5: MultiLineString,
|
||||
6: MultiPolygon,
|
||||
7: GeometryCollection,
|
||||
}
|
||||
|
||||
# If supported, import the PreparedGeometry class.
|
||||
if GEOS_PREPARE:
|
||||
from django.contrib.gis.geos.prepared import PreparedGeometry
|
||||
from django.contrib.gis.geos.prepared import PreparedGeometry
|
||||
GEOS_CLASSES = {
|
||||
0: Point,
|
||||
1: LineString,
|
||||
2: LinearRing,
|
||||
3: Polygon,
|
||||
4: MultiPoint,
|
||||
5: MultiLineString,
|
||||
6: MultiPolygon,
|
||||
7: GeometryCollection,
|
||||
}
|
||||
|
|
|
@ -155,22 +155,10 @@ GEOS_MINOR_VERSION = int(_verinfo['minor'])
|
|||
GEOS_SUBMINOR_VERSION = int(_verinfo['subminor'])
|
||||
del _verinfo
|
||||
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
|
||||
# routines. These functions aren't actually called until they are
|
||||
# attached to a GEOS context handle -- this actually occurs in
|
||||
# geos/prototypes/threadsafe.py.
|
||||
lgeos.initGEOS_r.restype = 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)
|
||||
# Here we set up the prototypes for the initGEOS_r and finishGEOS_r
|
||||
# routines. These functions aren't actually called until they are
|
||||
# attached to a GEOS context handle -- this actually occurs in
|
||||
# geos/prototypes/threadsafe.py.
|
||||
lgeos.initGEOS_r.restype = CONTEXT_PTR
|
||||
lgeos.finishGEOS_r.argtypes = [CONTEXT_PTR]
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
from django.contrib.gis.geos.base import GEOSBase
|
||||
from django.contrib.gis.geos.geometry import GEOSGeometry
|
||||
from django.contrib.gis.geos.prototypes import prepared as capi
|
||||
from .base import GEOSBase
|
||||
from .error import GEOSException
|
||||
from .geometry import GEOSGeometry
|
||||
from .libgeos import geos_version_info
|
||||
from .prototypes import prepared as capi
|
||||
|
||||
|
||||
class PreparedGeometry(GEOSBase):
|
||||
|
@ -12,6 +14,10 @@ class PreparedGeometry(GEOSBase):
|
|||
ptr_type = capi.PREPGEOM_PTR
|
||||
|
||||
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):
|
||||
raise TypeError
|
||||
self.ptr = capi.geos_prepare(geom.ptr)
|
||||
|
@ -31,3 +37,30 @@ class PreparedGeometry(GEOSBase):
|
|||
|
||||
def intersects(self, other):
|
||||
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)
|
||||
|
|
|
@ -3,13 +3,13 @@
|
|||
ones that return the area, distance, and length.
|
||||
"""
|
||||
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.geom import geos_char_p
|
||||
from django.contrib.gis.geos.prototypes.threadsafe import GEOSFunc
|
||||
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 ###
|
||||
|
@ -31,11 +31,7 @@ def dbl_from_geom(func, num_geom=1):
|
|||
geos_area = dbl_from_geom(GEOSFunc('GEOSArea'))
|
||||
geos_distance = dbl_from_geom(GEOSFunc('GEOSDistance'), num_geom=2)
|
||||
geos_length = dbl_from_geom(GEOSFunc('GEOSLength'))
|
||||
|
||||
# Validity reason; only in GEOS 3.1+
|
||||
if GEOS_PREPARE:
|
||||
geos_isvalidreason = GEOSFunc('GEOSisValidReason')
|
||||
geos_isvalidreason.argtypes = [GEOM_PTR]
|
||||
geos_isvalidreason.restype = geos_char_p
|
||||
geos_isvalidreason.errcheck = check_string
|
||||
__all__.append('geos_isvalidreason')
|
||||
geos_isvalidreason = GEOSFunc('GEOSisValidReason')
|
||||
geos_isvalidreason.argtypes = [GEOM_PTR]
|
||||
geos_isvalidreason.restype = geos_char_p
|
||||
geos_isvalidreason.errcheck = check_string
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
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.threadsafe import GEOSFunc
|
||||
|
||||
|
@ -24,3 +24,10 @@ prepared_contains = prepared_predicate(GEOSFunc('GEOSPreparedContains'))
|
|||
prepared_contains_properly = prepared_predicate(GEOSFunc('GEOSPreparedContainsProperly'))
|
||||
prepared_covers = prepared_predicate(GEOSFunc('GEOSPreparedCovers'))
|
||||
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'))
|
||||
|
|
|
@ -2,13 +2,14 @@
|
|||
This module houses the GEOS ctypes prototype functions for the
|
||||
topological operations on geometries.
|
||||
"""
|
||||
__all__ = ['geos_boundary', 'geos_buffer', 'geos_centroid', 'geos_convexhull',
|
||||
'geos_difference', 'geos_envelope', 'geos_intersection',
|
||||
'geos_linemerge', 'geos_pointonsurface', 'geos_preservesimplify',
|
||||
'geos_simplify', 'geos_symdifference', 'geos_union', 'geos_relate']
|
||||
__all__ = ['geos_boundary', 'geos_buffer', 'geos_cascaded_union',
|
||||
'geos_centroid', 'geos_convexhull', 'geos_difference',
|
||||
'geos_envelope', 'geos_intersection', 'geos_linemerge',
|
||||
'geos_pointonsurface', 'geos_preservesimplify', 'geos_simplify',
|
||||
'geos_symdifference', 'geos_union', 'geos_relate']
|
||||
|
||||
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.geom import geos_char_p
|
||||
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_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.
|
||||
geos_relate = GEOSFunc('GEOSRelate')
|
||||
geos_relate.argtypes = [GEOM_PTR, GEOM_PTR]
|
||||
geos_relate.restype = geos_char_p
|
||||
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
|
||||
info = geos_version_info()
|
||||
if info['version'] >= '3.2.0':
|
||||
|
|
|
@ -22,8 +22,7 @@ from .. import HAS_GEOS
|
|||
if HAS_GEOS:
|
||||
from .. import (GEOSException, GEOSIndexError, GEOSGeometry,
|
||||
GeometryCollection, Point, MultiPoint, Polygon, MultiPolygon, LinearRing,
|
||||
LineString, MultiLineString, fromfile, fromstr, geos_version_info,
|
||||
GEOS_PREPARE)
|
||||
LineString, MultiLineString, fromfile, fromstr, geos_version_info)
|
||||
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
|
||||
# place of Z; an exception should be raised on those versions.
|
||||
self.assertEqual(hexewkb_2d, pnt_2d.hexewkb)
|
||||
if GEOS_PREPARE:
|
||||
self.assertEqual(hexewkb_3d, pnt_3d.hexewkb)
|
||||
self.assertEqual(True, GEOSGeometry(hexewkb_3d).hasz)
|
||||
else:
|
||||
with self.assertRaises(GEOSException):
|
||||
pnt_3d.hexewkb
|
||||
self.assertEqual(hexewkb_3d, pnt_3d.hexewkb)
|
||||
self.assertEqual(True, GEOSGeometry(hexewkb_3d).hasz)
|
||||
|
||||
# Same for EWKB.
|
||||
self.assertEqual(memoryview(a2b_hex(hexewkb_2d)), pnt_2d.ewkb)
|
||||
if GEOS_PREPARE:
|
||||
self.assertEqual(memoryview(a2b_hex(hexewkb_3d)), pnt_3d.ewkb)
|
||||
else:
|
||||
with self.assertRaises(GEOSException):
|
||||
pnt_3d.ewkb
|
||||
self.assertEqual(memoryview(a2b_hex(hexewkb_3d)), pnt_3d.ewkb)
|
||||
|
||||
# Redundant sanity check.
|
||||
self.assertEqual(4326, GEOSGeometry(hexewkb_2d).srid)
|
||||
|
@ -869,10 +860,9 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
|
|||
self.assertIsInstance(g1.ogr, gdal.OGRGeometry)
|
||||
self.assertIsNone(g1.srs)
|
||||
|
||||
if GEOS_PREPARE:
|
||||
g1_3d = fromstr('POINT(5 23 8)')
|
||||
self.assertIsInstance(g1_3d.ogr, gdal.OGRGeometry)
|
||||
self.assertEqual(g1_3d.ogr.z, 8)
|
||||
g1_3d = fromstr('POINT(5 23 8)')
|
||||
self.assertIsInstance(g1_3d.ogr, gdal.OGRGeometry)
|
||||
self.assertEqual(g1_3d.ogr.z, 8)
|
||||
|
||||
g2 = fromstr('LINESTRING(0 0, 5 5, 23 23)', srid=4326)
|
||||
self.assertIsInstance(g2.ogr, gdal.OGRGeometry)
|
||||
|
@ -918,10 +908,7 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
|
|||
def test_transform_3d(self):
|
||||
p3d = GEOSGeometry('POINT (5 23 100)', 4326)
|
||||
p3d.transform(2774)
|
||||
if GEOS_PREPARE:
|
||||
self.assertEqual(p3d.z, 100)
|
||||
else:
|
||||
self.assertIsNone(p3d.z)
|
||||
self.assertEqual(p3d.z, 100)
|
||||
|
||||
@skipUnless(HAS_GDAL, "GDAL is required.")
|
||||
def test_transform_noop(self):
|
||||
|
@ -1030,7 +1017,6 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
|
|||
if not no_srid:
|
||||
self.assertEqual(geom.srid, tmpg.srid)
|
||||
|
||||
@skipUnless(HAS_GEOS and GEOS_PREPARE, "geos >= 3.1.0 is required")
|
||||
def test_prepared(self):
|
||||
"Testing PreparedGeometry support."
|
||||
# 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(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):
|
||||
"Testing line merge support"
|
||||
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):
|
||||
self.assertEqual(merged, geom.merged)
|
||||
|
||||
@skipUnless(HAS_GEOS and GEOS_PREPARE, "geos >= 3.1.0 is required")
|
||||
def test_valid_reason(self):
|
||||
"Testing IsValidReason support"
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import inspect
|
||||
from optparse import make_option
|
||||
|
||||
from django.contrib.gis import gdal
|
||||
from django.core.management.base import LabelCommand, CommandError
|
||||
|
||||
|
@ -83,27 +85,21 @@ class Command(LabelCommand):
|
|||
if not gdal.HAS_GDAL:
|
||||
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.
|
||||
try:
|
||||
ds = gdal.DataSource(data_source)
|
||||
except gdal.OGRException as 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
|
||||
# and options.
|
||||
from django.contrib.gis.utils.ogrinspect import _ogrinspect, mapping
|
||||
output = [s for s in _ogrinspect(ds, model_name, **options)]
|
||||
if show_mapping:
|
||||
# Filter options to params accepted by `_ogrinspect`
|
||||
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
|
||||
# calling it on the data source.
|
||||
kwargs = {'geom_name': options['geom_name'],
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from django.core.apps import app_cache
|
||||
from django.apps import apps
|
||||
from django.core import urlresolvers
|
||||
from django.contrib.sitemaps import Sitemap
|
||||
from django.contrib.gis.db.models.fields import GeometryField
|
||||
|
@ -26,7 +26,7 @@ class KMLSitemap(Sitemap):
|
|||
"""
|
||||
kml_sources = []
|
||||
if sources is None:
|
||||
sources = app_cache.get_models()
|
||||
sources = apps.get_models()
|
||||
for source in sources:
|
||||
if isinstance(source, models.base.ModelBase):
|
||||
for field in source._meta.fields:
|
||||
|
|
|
@ -2,7 +2,7 @@ from __future__ import unicode_literals
|
|||
|
||||
import warnings
|
||||
|
||||
from django.core.apps import app_cache
|
||||
from django.apps import apps
|
||||
from django.http import HttpResponse, Http404
|
||||
from django.template import loader
|
||||
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.
|
||||
"""
|
||||
placemarks = []
|
||||
klass = app_cache.get_model(label, model)
|
||||
if not klass:
|
||||
try:
|
||||
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))
|
||||
|
||||
if field_name:
|
||||
|
|
|
@ -3,77 +3,49 @@ from django.utils.encoding import 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."
|
||||
name = models.CharField(max_length=30)
|
||||
point = models.PointField(srid=32140)
|
||||
objects = models.GeoManager()
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class SouthTexasCityFt(models.Model):
|
||||
class SouthTexasCityFt(NamedModel):
|
||||
"Same City model as above, but U.S. survey feet are the units."
|
||||
name = models.CharField(max_length=30)
|
||||
point = models.PointField(srid=2278)
|
||||
objects = models.GeoManager()
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class AustraliaCity(models.Model):
|
||||
class AustraliaCity(NamedModel):
|
||||
"City model for Australia, using WGS84."
|
||||
name = models.CharField(max_length=30)
|
||||
point = models.PointField()
|
||||
objects = models.GeoManager()
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class CensusZipcode(models.Model):
|
||||
class CensusZipcode(NamedModel):
|
||||
"Model for a few South Texas ZIP codes (in original Census NAD83)."
|
||||
name = models.CharField(max_length=5)
|
||||
poly = models.PolygonField(srid=4269)
|
||||
objects = models.GeoManager()
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class SouthTexasZipcode(models.Model):
|
||||
class SouthTexasZipcode(NamedModel):
|
||||
"Model for a few South Texas ZIP codes."
|
||||
name = models.CharField(max_length=5)
|
||||
poly = models.PolygonField(srid=32140, null=True)
|
||||
objects = models.GeoManager()
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Interstate(models.Model):
|
||||
class Interstate(NamedModel):
|
||||
"Geodetic model for U.S. Interstates."
|
||||
name = models.CharField(max_length=10)
|
||||
path = models.LineStringField()
|
||||
objects = models.GeoManager()
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class SouthTexasInterstate(models.Model):
|
||||
class SouthTexasInterstate(NamedModel):
|
||||
"Projected model for South Texas Interstates."
|
||||
name = models.CharField(max_length=10)
|
||||
path = models.LineStringField(srid=32140)
|
||||
objects = models.GeoManager()
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
|
|
@ -3,85 +3,63 @@ from django.utils.encoding import python_2_unicode_compatible
|
|||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class City3D(models.Model):
|
||||
class NamedModel(models.Model):
|
||||
name = models.CharField(max_length=30)
|
||||
point = models.PointField(dim=3)
|
||||
|
||||
objects = models.GeoManager()
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
app_label = 'geo3d'
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Interstate2D(models.Model):
|
||||
name = models.CharField(max_length=30)
|
||||
class City3D(NamedModel):
|
||||
point = models.PointField(dim=3)
|
||||
|
||||
|
||||
class Interstate2D(NamedModel):
|
||||
line = models.LineStringField(srid=4269)
|
||||
objects = models.GeoManager()
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Interstate3D(models.Model):
|
||||
name = models.CharField(max_length=30)
|
||||
class Interstate3D(NamedModel):
|
||||
line = models.LineStringField(dim=3, srid=4269)
|
||||
objects = models.GeoManager()
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class InterstateProj2D(models.Model):
|
||||
name = models.CharField(max_length=30)
|
||||
class InterstateProj2D(NamedModel):
|
||||
line = models.LineStringField(srid=32140)
|
||||
objects = models.GeoManager()
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class InterstateProj3D(models.Model):
|
||||
name = models.CharField(max_length=30)
|
||||
class InterstateProj3D(NamedModel):
|
||||
line = models.LineStringField(dim=3, srid=32140)
|
||||
objects = models.GeoManager()
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Polygon2D(models.Model):
|
||||
name = models.CharField(max_length=30)
|
||||
class Polygon2D(NamedModel):
|
||||
poly = models.PolygonField(srid=32140)
|
||||
objects = models.GeoManager()
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Polygon3D(models.Model):
|
||||
name = models.CharField(max_length=30)
|
||||
class Polygon3D(NamedModel):
|
||||
poly = models.PolygonField(dim=3, srid=32140)
|
||||
|
||||
|
||||
class SimpleModel(models.Model):
|
||||
|
||||
objects = models.GeoManager()
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
class Meta:
|
||||
abstract = True
|
||||
app_label = 'geo3d'
|
||||
|
||||
|
||||
class Point2D(models.Model):
|
||||
class Point2D(SimpleModel):
|
||||
point = models.PointField()
|
||||
objects = models.GeoManager()
|
||||
|
||||
|
||||
class Point3D(models.Model):
|
||||
class Point3D(SimpleModel):
|
||||
point = models.PointField(dim=3)
|
||||
objects = models.GeoManager()
|
||||
|
||||
|
||||
class MultiPoint3D(models.Model):
|
||||
class MultiPoint3D(SimpleModel):
|
||||
mpoint = models.MultiPointField(dim=3)
|
||||
objects = models.GeoManager()
|
||||
|
|
|
@ -7,8 +7,12 @@ from django.utils.encoding import python_2_unicode_compatible
|
|||
class City(models.Model):
|
||||
name = models.CharField(max_length=30)
|
||||
point = models.PointField()
|
||||
|
||||
objects = models.GeoManager()
|
||||
|
||||
class Meta:
|
||||
app_label = 'geoadmin'
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
|
|
@ -4,8 +4,7 @@ from unittest import skipUnless
|
|||
|
||||
from django.contrib.gis.geos import HAS_GEOS
|
||||
from django.contrib.gis.tests.utils import HAS_SPATIAL_DB
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
from django.test import TestCase, override_settings
|
||||
|
||||
if HAS_GEOS and HAS_SPATIAL_DB:
|
||||
from django.contrib.gis import admin
|
||||
|
|
|
@ -7,66 +7,66 @@ null_flag = not mysql
|
|||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Country(models.Model):
|
||||
class NamedModel(models.Model):
|
||||
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
|
||||
objects = models.GeoManager()
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class City(models.Model):
|
||||
name = models.CharField(max_length=30)
|
||||
class City(NamedModel):
|
||||
point = models.PointField()
|
||||
objects = models.GeoManager()
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
# This is an inherited model from City
|
||||
class PennsylvaniaCity(City):
|
||||
county = models.CharField(max_length=30)
|
||||
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(models.Model):
|
||||
name = models.CharField(max_length=30)
|
||||
class State(NamedModel):
|
||||
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(models.Model):
|
||||
name = models.CharField(max_length=30)
|
||||
class Track(NamedModel):
|
||||
line = models.LineStringField()
|
||||
objects = models.GeoManager()
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Truth(models.Model):
|
||||
val = models.BooleanField(default=False)
|
||||
|
||||
objects = models.GeoManager()
|
||||
|
||||
if not spatialite:
|
||||
@python_2_unicode_compatible
|
||||
class Feature(models.Model):
|
||||
name = models.CharField(max_length=20)
|
||||
geom = models.GeometryField()
|
||||
objects = models.GeoManager()
|
||||
class Meta:
|
||||
app_label = 'geoapp'
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
if not spatialite:
|
||||
|
||||
class Feature(NamedModel):
|
||||
geom = models.GeometryField()
|
||||
|
||||
class MinusOneSRID(models.Model):
|
||||
geom = models.PointField(srid=-1) # Minus one SRID.
|
||||
|
||||
objects = models.GeoManager()
|
||||
|
||||
class Meta:
|
||||
app_label = 'geoapp'
|
||||
|
|
|
@ -7,12 +7,13 @@ from django.conf import settings
|
|||
from django.contrib.sites.models import Site
|
||||
from django.contrib.gis.geos import HAS_GEOS
|
||||
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:
|
||||
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.")
|
||||
class GeoFeedTest(TestCase):
|
||||
|
||||
|
@ -20,11 +21,6 @@ class GeoFeedTest(TestCase):
|
|||
|
||||
def setUp(self):
|
||||
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):
|
||||
"Taken from syndication/tests.py."
|
||||
|
|
|
@ -10,7 +10,7 @@ from django.conf import settings
|
|||
from django.contrib.gis.geos import HAS_GEOS
|
||||
from django.contrib.gis.tests.utils import HAS_SPATIAL_DB
|
||||
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.utils._os import upath
|
||||
|
||||
|
@ -18,6 +18,7 @@ if HAS_GEOS:
|
|||
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.")
|
||||
class GeoSitemapTest(IgnoreDeprecationWarningsMixin, TestCase):
|
||||
|
||||
|
@ -26,12 +27,6 @@ class GeoSitemapTest(IgnoreDeprecationWarningsMixin, TestCase):
|
|||
def setUp(self):
|
||||
super(GeoSitemapTest, self).setUp()
|
||||
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):
|
||||
"Taken from syndication/tests.py."
|
||||
|
|
|
@ -3,31 +3,31 @@ from django.utils.encoding import python_2_unicode_compatible
|
|||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class City(models.Model):
|
||||
class NamedModel(models.Model):
|
||||
name = models.CharField(max_length=30)
|
||||
point = models.PointField(geography=True)
|
||||
|
||||
objects = models.GeoManager()
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
app_label = 'geogapp'
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Zipcode(models.Model):
|
||||
class City(NamedModel):
|
||||
point = models.PointField(geography=True)
|
||||
|
||||
|
||||
class Zipcode(NamedModel):
|
||||
code = models.CharField(max_length=10)
|
||||
poly = models.PolygonField(geography=True)
|
||||
objects = models.GeoManager()
|
||||
|
||||
def __str__(self):
|
||||
return self.code
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class County(models.Model):
|
||||
name = models.CharField(max_length=25)
|
||||
class County(NamedModel):
|
||||
state = models.CharField(max_length=20)
|
||||
mpoly = models.MultiPolygonField(geography=True)
|
||||
objects = models.GeoManager()
|
||||
|
||||
def __str__(self):
|
||||
return ' County, '.join([self.name, self.state])
|
||||
|
|
|
@ -2,6 +2,7 @@ from django.contrib.gis.db import models
|
|||
|
||||
|
||||
class AllOGRFields(models.Model):
|
||||
|
||||
f_decimal = models.FloatField()
|
||||
f_float = models.FloatField()
|
||||
f_int = models.IntegerField()
|
||||
|
@ -13,3 +14,6 @@ class AllOGRFields(models.Model):
|
|||
point = models.PointField()
|
||||
|
||||
objects = models.GeoManager()
|
||||
|
||||
class Meta:
|
||||
app_label = 'inspectapp'
|
||||
|
|
|
@ -115,6 +115,13 @@ class OGRInspectTest(TestCase):
|
|||
' 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():
|
||||
"""
|
||||
|
|
|
@ -1,61 +1,75 @@
|
|||
from django.contrib.gis.db import models
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
|
||||
|
||||
class State(models.Model):
|
||||
name = models.CharField(max_length=20)
|
||||
@python_2_unicode_compatible
|
||||
class NamedModel(models.Model):
|
||||
name = models.CharField(max_length=25)
|
||||
|
||||
objects = models.GeoManager()
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
app_label = 'layermap'
|
||||
|
||||
class County(models.Model):
|
||||
name = models.CharField(max_length=25)
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class State(NamedModel):
|
||||
pass
|
||||
|
||||
|
||||
class County(NamedModel):
|
||||
state = models.ForeignKey(State)
|
||||
mpoly = models.MultiPolygonField(srid=4269) # Multipolygon in NAD83
|
||||
objects = models.GeoManager()
|
||||
|
||||
|
||||
class CountyFeat(models.Model):
|
||||
name = models.CharField(max_length=25)
|
||||
class CountyFeat(NamedModel):
|
||||
poly = models.PolygonField(srid=4269)
|
||||
objects = models.GeoManager()
|
||||
|
||||
|
||||
class City(models.Model):
|
||||
name = models.CharField(max_length=25)
|
||||
class City(NamedModel):
|
||||
name_txt = models.TextField(default='')
|
||||
population = models.IntegerField()
|
||||
density = models.DecimalField(max_digits=7, decimal_places=1)
|
||||
dt = models.DateField()
|
||||
point = models.PointField()
|
||||
objects = models.GeoManager()
|
||||
|
||||
|
||||
class Interstate(models.Model):
|
||||
name = models.CharField(max_length=20)
|
||||
class Interstate(NamedModel):
|
||||
length = models.DecimalField(max_digits=6, decimal_places=2)
|
||||
path = models.LineStringField()
|
||||
objects = models.GeoManager()
|
||||
|
||||
|
||||
# Same as `City` above, but for testing model inheritance.
|
||||
class CityBase(models.Model):
|
||||
name = models.CharField(max_length=25)
|
||||
class CityBase(NamedModel):
|
||||
population = models.IntegerField()
|
||||
density = models.DecimalField(max_digits=7, decimal_places=1)
|
||||
point = models.PointField()
|
||||
objects = models.GeoManager()
|
||||
|
||||
|
||||
class ICity1(CityBase):
|
||||
dt = models.DateField()
|
||||
|
||||
class Meta(CityBase.Meta):
|
||||
pass
|
||||
|
||||
|
||||
class ICity2(ICity1):
|
||||
dt_time = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta(ICity1.Meta):
|
||||
pass
|
||||
|
||||
|
||||
class Invalid(models.Model):
|
||||
point = models.PointField()
|
||||
|
||||
class Meta:
|
||||
app_label = 'layermap'
|
||||
|
||||
|
||||
# Mapping dictionaries for the models above.
|
||||
co_mapping = {'name': 'Name',
|
||||
'state': {'name': 'State'}, # ForeignKey's use another mapping dictionary for the _related_ Model (State in this case).
|
||||
|
|
|
@ -2,21 +2,28 @@ from django.contrib.gis.db import models
|
|||
from django.utils.encoding import python_2_unicode_compatible
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Location(models.Model):
|
||||
point = models.PointField()
|
||||
class SimpleModel(models.Model):
|
||||
|
||||
objects = models.GeoManager()
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
app_label = 'relatedapp'
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Location(SimpleModel):
|
||||
point = models.PointField()
|
||||
|
||||
def __str__(self):
|
||||
return self.point.wkt
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class City(models.Model):
|
||||
class City(SimpleModel):
|
||||
name = models.CharField(max_length=50)
|
||||
state = models.CharField(max_length=2)
|
||||
location = models.ForeignKey(Location)
|
||||
objects = models.GeoManager()
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
@ -24,17 +31,20 @@ class City(models.Model):
|
|||
|
||||
class AugmentedLocation(Location):
|
||||
extra_text = models.TextField(blank=True)
|
||||
|
||||
objects = models.GeoManager()
|
||||
|
||||
class Meta:
|
||||
app_label = 'relatedapp'
|
||||
|
||||
class DirectoryEntry(models.Model):
|
||||
|
||||
class DirectoryEntry(SimpleModel):
|
||||
listing_text = models.CharField(max_length=50)
|
||||
location = models.ForeignKey(AugmentedLocation)
|
||||
objects = models.GeoManager()
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Parcel(models.Model):
|
||||
class Parcel(SimpleModel):
|
||||
name = models.CharField(max_length=30)
|
||||
city = models.ForeignKey(City)
|
||||
center1 = models.PointField()
|
||||
|
@ -42,26 +52,22 @@ class Parcel(models.Model):
|
|||
center2 = models.PointField(srid=2276, db_column='mycenter')
|
||||
border1 = models.PolygonField()
|
||||
border2 = models.PolygonField(srid=2276)
|
||||
objects = models.GeoManager()
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
# These use the GeoManager but do not have any geographic fields.
|
||||
class Author(models.Model):
|
||||
class Author(SimpleModel):
|
||||
name = models.CharField(max_length=100)
|
||||
dob = models.DateField()
|
||||
objects = models.GeoManager()
|
||||
|
||||
|
||||
class Article(models.Model):
|
||||
class Article(SimpleModel):
|
||||
title = models.CharField(max_length=100)
|
||||
author = models.ForeignKey(Author, unique=True)
|
||||
objects = models.GeoManager()
|
||||
|
||||
|
||||
class Book(models.Model):
|
||||
class Book(SimpleModel):
|
||||
title = models.CharField(max_length=100)
|
||||
author = models.ForeignKey(Author, related_name='books', null=True)
|
||||
objects = models.GeoManager()
|
||||
|
|
|
@ -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")
|
|
@ -11,8 +11,8 @@ except ImportError:
|
|||
from django.conf import settings
|
||||
from django.contrib.humanize.templatetags import humanize
|
||||
from django.template import Template, Context, defaultfilters
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings, TransRealMixin
|
||||
from django.test import TestCase, override_settings
|
||||
from django.test.utils import TransRealMixin
|
||||
from django.utils.html import escape
|
||||
from django.utils.timezone import utc, get_fixed_timezone
|
||||
from django.utils import translation
|
||||
|
|
|
@ -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")
|
|
@ -1,8 +1,12 @@
|
|||
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
|
||||
# 'request' object. It is wrapped in a lambda to stop 'settings' being used at
|
||||
# the module level
|
||||
default_storage = lambda request: get_storage(settings.MESSAGE_STORAGE)(request)
|
||||
def default_storage(request):
|
||||
"""
|
||||
Callable with the same interface as the storage classes.
|
||||
|
||||
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)
|
||||
|
|
|
@ -1,20 +1,21 @@
|
|||
from unittest import skipIf
|
||||
from unittest import skipUnless
|
||||
|
||||
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.api import MessageFailure
|
||||
from django.contrib.messages.constants import DEFAULT_LEVELS
|
||||
from django.contrib.messages.storage import default_storage, base
|
||||
from django.contrib.messages.storage.base import Message
|
||||
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
|
||||
|
||||
|
||||
def skipUnlessAuthIsInstalled(func):
|
||||
return skipIf(
|
||||
'django.contrib.auth' not in settings.INSTALLED_APPS,
|
||||
return skipUnless(
|
||||
apps.is_installed('django.contrib.auth'),
|
||||
"django.contrib.auth isn't installed")(func)
|
||||
|
||||
|
||||
|
@ -218,16 +219,12 @@ class BaseTests(object):
|
|||
for msg in data['messages']:
|
||||
self.assertContains(response, msg)
|
||||
|
||||
@override_settings(
|
||||
INSTALLED_APPS=filter(
|
||||
lambda app: app != 'django.contrib.messages', settings.INSTALLED_APPS),
|
||||
MIDDLEWARE_CLASSES=filter(
|
||||
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
|
||||
@modify_settings(
|
||||
INSTALLED_APPS={'remove': 'django.contrib.messages'},
|
||||
MIDDLEWARE_CLASSES={'remove': 'django.contrib.messages.middleware.MessageMiddleware'},
|
||||
TEMPLATE_CONTEXT_PROCESSORS={'remove': 'django.contrib.messages.context_processors.messages'},
|
||||
)
|
||||
@override_settings(MESSAGE_LEVEL=constants.DEBUG)
|
||||
def test_middleware_disabled(self):
|
||||
"""
|
||||
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,
|
||||
data, follow=True)
|
||||
|
||||
@override_settings(
|
||||
INSTALLED_APPS=filter(
|
||||
lambda app: app != 'django.contrib.messages', settings.INSTALLED_APPS),
|
||||
MIDDLEWARE_CLASSES=filter(
|
||||
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
|
||||
@modify_settings(
|
||||
INSTALLED_APPS={'remove': 'django.contrib.messages'},
|
||||
MIDDLEWARE_CLASSES={'remove': 'django.contrib.messages.middleware.MessageMiddleware'},
|
||||
TEMPLATE_CONTEXT_PROCESSORS={'remove': 'django.contrib.messages.context_processors.messages'},
|
||||
)
|
||||
def test_middleware_disabled_fail_silently(self):
|
||||
"""
|
||||
|
|
|
@ -5,8 +5,7 @@ from django.contrib.messages.tests.base import BaseTests
|
|||
from django.contrib.messages.storage.cookie import (CookieStorage,
|
||||
MessageEncoder, MessageDecoder)
|
||||
from django.contrib.messages.storage.base import Message
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
from django.test import TestCase, override_settings
|
||||
from django.utils.safestring import SafeData, mark_safe
|
||||
|
||||
|
||||
|
|
|
@ -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")
|
|
@ -1,5 +1,6 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
from django.apps import apps
|
||||
from django.conf import settings
|
||||
from django.contrib.redirects.models import Redirect
|
||||
from django.contrib.sites.models import get_current_site
|
||||
|
@ -14,7 +15,7 @@ class RedirectFallbackMiddleware(object):
|
|||
response_redirect_class = http.HttpResponsePermanentRedirect
|
||||
|
||||
def __init__(self):
|
||||
if 'django.contrib.sites' not in settings.INSTALLED_APPS:
|
||||
if not apps.is_installed('django.contrib.sites'):
|
||||
raise ImproperlyConfigured(
|
||||
"You cannot use RedirectFallbackMiddleware when "
|
||||
"django.contrib.sites is not installed."
|
||||
|
|
|
@ -2,20 +2,16 @@ from django import http
|
|||
from django.conf import settings
|
||||
from django.contrib.sites.models import Site
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
from django.test import TestCase, modify_settings, override_settings
|
||||
from django.utils import six
|
||||
|
||||
from .middleware import RedirectFallbackMiddleware
|
||||
from .models import Redirect
|
||||
|
||||
|
||||
@override_settings(
|
||||
APPEND_SLASH=False,
|
||||
MIDDLEWARE_CLASSES=list(settings.MIDDLEWARE_CLASSES) +
|
||||
['django.contrib.redirects.middleware.RedirectFallbackMiddleware'],
|
||||
SITE_ID=1,
|
||||
)
|
||||
@modify_settings(MIDDLEWARE_CLASSES={'append':
|
||||
'django.contrib.redirects.middleware.RedirectFallbackMiddleware'})
|
||||
@override_settings(APPEND_SLASH=False, SITE_ID=1)
|
||||
class RedirectTests(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
@ -56,9 +52,7 @@ class RedirectTests(TestCase):
|
|||
response = self.client.get('/initial')
|
||||
self.assertEqual(response.status_code, 410)
|
||||
|
||||
@override_settings(
|
||||
INSTALLED_APPS=[app for app in settings.INSTALLED_APPS
|
||||
if app != 'django.contrib.sites'])
|
||||
@modify_settings(INSTALLED_APPS={'remove': 'django.contrib.sites'})
|
||||
def test_sites_not_installed(self):
|
||||
with self.assertRaises(ImproperlyConfigured):
|
||||
RedirectFallbackMiddleware()
|
||||
|
@ -70,11 +64,9 @@ class OverriddenRedirectFallbackMiddleware(RedirectFallbackMiddleware):
|
|||
response_redirect_class = http.HttpResponseRedirect
|
||||
|
||||
|
||||
@override_settings(
|
||||
MIDDLEWARE_CLASSES=list(settings.MIDDLEWARE_CLASSES) +
|
||||
['django.contrib.redirects.tests.OverriddenRedirectFallbackMiddleware'],
|
||||
SITE_ID=1,
|
||||
)
|
||||
@modify_settings(MIDDLEWARE_CLASSES={'append':
|
||||
'django.contrib.redirects.tests.OverriddenRedirectFallbackMiddleware'})
|
||||
@override_settings(SITE_ID=1)
|
||||
class OverriddenRedirectMiddlewareTests(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
|
|
@ -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")
|
|
@ -20,8 +20,8 @@ from django.core.cache.backends.base import InvalidCacheBackendError
|
|||
from django.core import management
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.http import HttpResponse
|
||||
from django.test import TestCase, RequestFactory
|
||||
from django.test.utils import override_settings, patch_logger
|
||||
from django.test import TestCase, RequestFactory, override_settings
|
||||
from django.test.utils import patch_logger
|
||||
from django.utils import six
|
||||
from django.utils import timezone
|
||||
|
||||
|
|
|
@ -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")
|
|
@ -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 ""
|
|
@ -25,10 +25,6 @@ class SitemapTestsBase(TestCase):
|
|||
|
||||
def setUp(self):
|
||||
self.base_url = '%s://%s' % (self.protocol, self.domain)
|
||||
self._old_installed = Site._meta.app_config.installed
|
||||
cache.clear()
|
||||
# Create an object for sitemap content.
|
||||
TestModel.objects.create(name='Test Object')
|
||||
|
||||
def tearDown(self):
|
||||
Site._meta.app_config.installed = self._old_installed
|
||||
|
|
|
@ -2,6 +2,7 @@ from __future__ import unicode_literals
|
|||
|
||||
from unittest import skipUnless
|
||||
|
||||
from django.apps import apps
|
||||
from django.conf import settings
|
||||
|
||||
from .base import SitemapTestsBase
|
||||
|
@ -9,7 +10,7 @@ from .base import 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.")
|
||||
def test_flatpage_sitemap(self):
|
||||
"Basic FlatPage sitemap test"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
from django.test.utils import override_settings
|
||||
from django.test import override_settings
|
||||
|
||||
from .base import TestModel, SitemapTestsBase
|
||||
|
||||
|
|
|
@ -4,11 +4,12 @@ import os
|
|||
from datetime import date
|
||||
from unittest import skipUnless
|
||||
|
||||
from django.apps import apps
|
||||
from django.conf import settings
|
||||
from django.contrib.sitemaps import Sitemap, GenericSitemap
|
||||
from django.contrib.sites.models import Site
|
||||
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._os import upath
|
||||
from django.utils.translation import activate, deactivate
|
||||
|
@ -105,11 +106,10 @@ class HTTPSitemapTests(SitemapTestsBase):
|
|||
self.assertContains(response, '<lastmod>%s</lastmod>' % date.today())
|
||||
deactivate()
|
||||
|
||||
@modify_settings(INSTALLED_APPS={'remove': 'django.contrib.sites'})
|
||||
def test_requestsite_sitemap(self):
|
||||
# Make sure hitting the flatpages sitemap without the sites framework
|
||||
# installed doesn't raise an exception.
|
||||
# Reset by SitemapTestsBase.tearDown().
|
||||
Site._meta.app_config.installed = False
|
||||
response = self.client.get('/simple/sitemap.xml')
|
||||
expected_content = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
|
@ -118,7 +118,7 @@ class HTTPSitemapTests(SitemapTestsBase):
|
|||
""" % date.today()
|
||||
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.")
|
||||
def test_sitemap_get_urls_no_site_1(self):
|
||||
"""
|
||||
|
@ -128,14 +128,13 @@ class HTTPSitemapTests(SitemapTestsBase):
|
|||
Site.objects.all().delete()
|
||||
self.assertRaises(ImproperlyConfigured, Sitemap().get_urls)
|
||||
|
||||
@modify_settings(INSTALLED_APPS={'remove': 'django.contrib.sites'})
|
||||
def test_sitemap_get_urls_no_site_2(self):
|
||||
"""
|
||||
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
|
||||
actually installed.
|
||||
"""
|
||||
# Reset by SitemapTestsBase.tearDown().
|
||||
Site._meta.app_config.installed = False
|
||||
self.assertRaises(ImproperlyConfigured, Sitemap().get_urls)
|
||||
|
||||
def test_sitemap_item(self):
|
||||
|
|
|
@ -2,7 +2,7 @@ from __future__ import unicode_literals
|
|||
|
||||
from datetime import date
|
||||
|
||||
from django.test.utils import override_settings
|
||||
from django.test import override_settings
|
||||
|
||||
from .base import SitemapTestsBase
|
||||
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue