Ticket #21719: 21719-postponing-wip-2.patch

File 21719-postponing-wip-2.patch, 25.4 KB (added by Aymeric Augustin, 11 years ago)
  • django/apps/registry.py

    diff --git a/django/apps/registry.py b/django/apps/registry.py
    index 41095bd..412e34d 100644
    a b from django.utils import lru_cache  
    99from django.utils.deprecation import RemovedInDjango19Warning
    1010from django.utils._os import upath
    1111
    12 from .config import AppConfig
     12from .config import AppConfig, MODELS_MODULE_NAME
    1313
    1414
    1515class Apps(object):
    class Apps(object):  
    4848        # Lock for thread-safe population.
    4949        self._lock = threading.Lock()
    5050
     51        # Models imported before their apps.
     52        self._orphan_models = []
     53
    5154        # Pending lookups for lazy relations.
    5255        self._pending_lookups = {}
    5356
    class Apps(object):  
    100103                    "Application names aren't unique, "
    101104                    "duplicates: %s" % ", ".join(duplicates))
    102105
     106            # Handle prematurely loaded models.
     107            orphan_models = self._orphan_models
     108            # The following line prevents orphaning.
     109            self._orphan_models = None
     110            for orphan_model in orphan_models:
     111                self.register_model(orphan_model)
     112
    103113            # Load models.
    104114            for app_config in self.app_configs.values():
    105115                all_models = self.all_models[app_config.label]
    class Apps(object):  
    189199            app_label, model_name = app_label.split('.')
    190200        return self.get_app_config(app_label).get_model(model_name.lower())
    191201
    192     def register_model(self, app_label, model):
     202    def register_model(self, model):
    193203        # Since this method is called when models are imported, it cannot
    194204        # perform imports because of the risk of import loops. It mustn't
    195205        # call get_app_config().
     206        if model._meta.app_label is None:
     207            app_config = self.get_containing_app_config(model.__module__)
     208        else:
     209            app_config = self.app_configs.get(model._meta.app_label)
     210
     211        if app_config is None:
     212            if self._orphan_models is not None:
     213                # Applications are still loading. Orphaning is allowed.
     214                self._orphan_models.append(model)
     215                return
     216            else:
     217                # If the model isn't in an installed application (#21680), use
     218                # the legacy logic to figure out the app_label by looking one
     219                # level up from the package or module named 'models'. If no
     220                # such package or module exists, fall back to looking one
     221                # level up from the module this model is defined in.
     222
     223                # For 'django.contrib.sites.models', this would be 'sites'.
     224                # For 'geo.models.places' this would be 'geo'.
     225
     226                msg = (
     227                    "Model class %s.%s doesn't declare an explicit app_label "
     228                    "and either isn't in an application in INSTALLED_APPS. " %
     229                    (model.__module__, model.__name__))
     230                if model._meta.abstract:
     231                    msg += "Its app_label will be set to None in Django 1.9."
     232                else:
     233                    msg += "This will no longer be supported in Django 1.9."
     234                warnings.warn(msg, RemovedInDjango19Warning, stacklevel=2)
     235
     236                package_components = model.__module__.split('.')
     237                package_components.reverse()  # find the last occurrence of 'models'
     238                try:
     239                    app_label_index = package_components.index(MODELS_MODULE_NAME) + 1
     240                except ValueError:
     241                    app_label_index = 1
     242                app_label = model._meta.app_label = package_components[app_label_index]
     243        else:
     244            app_label = model._meta.app_label = app_config.label
     245
    196246        model_name = model._meta.model_name
    197247        app_models = self.all_models[app_label]
    198248        if model_name in app_models:
    class Apps(object):  
    202252        app_models[model_name] = model
    203253        self.clear_cache()
    204254
     255        # Inner import because django.db.models.signals imports django.apps.
     256        from django.db.models.signals import class_registered
     257        class_registered.send(sender=model)
     258
    205259    def is_installed(self, app_name):
    206260        """
    207261        Checks whether an application with this name exists in the registry.
    class Apps(object):  
    416470            "register_models(app_label, *models) is deprecated.",
    417471            RemovedInDjango19Warning, stacklevel=2)
    418472        for model in models:
    419             self.register_model(app_label, model)
     473            self.register_model(model)
    420474
    421475
    422476apps = Apps(installed_apps=None)
  • django/db/models/base.py

    diff --git a/django/db/models/base.py b/django/db/models/base.py
    index ba89e6e..b5cc744 100644
    a b  
    11from __future__ import unicode_literals
    22
    33import copy
    4 import sys
    54from functools import update_wrapper
    6 import warnings
    75
    86from django.apps import apps
    9 from django.apps.config import MODELS_MODULE_NAME
    107import django.db.models.manager  # NOQA: Imported to register signal handler.
     8from django.db.models.signals import class_registered
    119from django.conf import settings
    1210from django.core import checks
    1311from django.core.exceptions import (ObjectDoesNotExist,
    from django.db.models.deletion import Collector  
    2321from django.db.models.options import Options
    2422from django.db.models import signals
    2523from django.utils import six
    26 from django.utils.deprecation import RemovedInDjango19Warning
    2724from django.utils.encoding import force_str, force_text
    2825from django.utils.functional import curry
    2926from django.utils.six.moves import zip
    class ModelBase(type):  
    8986            meta = attr_meta
    9087        base_meta = getattr(new_class, '_meta', None)
    9188
    92         # Look for an application configuration to attach the model to.
    93         app_config = apps.get_containing_app_config(module)
    94 
    95         if getattr(meta, 'app_label', None) is None:
    96 
    97             if app_config is None:
    98                 # If the model is imported before the configuration for its
    99                 # application is created (#21719), or isn't in an installed
    100                 # application (#21680), use the legacy logic to figure out the
    101                 # app_label by looking one level up from the package or module
    102                 # named 'models'. If no such package or module exists, fall
    103                 # back to looking one level up from the module this model is
    104                 # defined in.
    105 
    106                 # For 'django.contrib.sites.models', this would be 'sites'.
    107                 # For 'geo.models.places' this would be 'geo'.
    108 
    109                 msg = (
    110                     "Model class %s.%s doesn't declare an explicit app_label "
    111                     "and either isn't in an application in INSTALLED_APPS or "
    112                     "else was imported before its application was loaded. " %
    113                     (module, name))
    114                 if abstract:
    115                     msg += "Its app_label will be set to None in Django 1.9."
     89        new_class.add_to_class('_meta', Options(meta))
     90
     91        def _setup(sender, **kwargs):
     92            assert sender is new_class
     93            if not abstract:
     94                new_class.add_to_class(
     95                    'DoesNotExist',
     96                    subclass_exception(
     97                        str('DoesNotExist'),
     98                        tuple(x.DoesNotExist for x in parents if hasattr(x, '_meta') and not x._meta.abstract) or (ObjectDoesNotExist,),
     99                        module,
     100                        attached_to=new_class))
     101                new_class.add_to_class(
     102                    'MultipleObjectsReturned',
     103                    subclass_exception(
     104                        str('MultipleObjectsReturned'),
     105                        tuple(x.MultipleObjectsReturned for x in parents if hasattr(x, '_meta') and not x._meta.abstract) or (MultipleObjectsReturned,),
     106                        module,
     107                        attached_to=new_class))
     108                if base_meta and not base_meta.abstract:
     109                    # Non-abstract child classes inherit some attributes from their
     110                    # non-abstract parent (unless an ABC comes before it in the
     111                    # method resolution order).
     112                    if not hasattr(meta, 'ordering'):
     113                        new_class._meta.ordering = base_meta.ordering
     114                    if not hasattr(meta, 'get_latest_by'):
     115                        new_class._meta.get_latest_by = base_meta.get_latest_by
     116
     117            is_proxy = new_class._meta.proxy
     118
     119            # If the model is a proxy, ensure that the base class
     120            # hasn't been swapped out.
     121            if is_proxy and base_meta and base_meta.swapped:
     122                raise TypeError("%s cannot proxy the swapped model '%s'." % (name, base_meta.swapped))
     123
     124            if getattr(new_class, '_default_manager', None):
     125                if not is_proxy:
     126                    # Multi-table inheritance doesn't inherit default manager from
     127                    # parents.
     128                    new_class._default_manager = None
     129                    new_class._base_manager = None
    116130                else:
    117                     msg += "This will no longer be supported in Django 1.9."
    118                 warnings.warn(msg, RemovedInDjango19Warning, stacklevel=2)
    119 
    120                 model_module = sys.modules[new_class.__module__]
    121                 package_components = model_module.__name__.split('.')
    122                 package_components.reverse()  # find the last occurrence of 'models'
    123                 try:
    124                     app_label_index = package_components.index(MODELS_MODULE_NAME) + 1
    125                 except ValueError:
    126                     app_label_index = 1
    127                 kwargs = {"app_label": package_components[app_label_index]}
    128 
    129             else:
    130                 kwargs = {"app_label": app_config.label}
     131                    # Proxy classes do inherit parent's default manager, if none is
     132                    # set explicitly.
     133                    new_class._default_manager = new_class._default_manager._copy_to_model(new_class)
     134                    new_class._base_manager = new_class._base_manager._copy_to_model(new_class)
     135
     136            # Add all attributes to the class.
     137            for obj_name, obj in attrs.items():
     138                new_class.add_to_class(obj_name, obj)
     139
     140            # All the fields of any type declared on this model
     141            new_fields = (
     142                new_class._meta.local_fields +
     143                new_class._meta.local_many_to_many +
     144                new_class._meta.virtual_fields
     145            )
     146            field_names = set(f.name for f in new_fields)
    131147
    132         else:
    133             kwargs = {}
    134 
    135         new_class.add_to_class('_meta', Options(meta, **kwargs))
    136         if not abstract:
    137             new_class.add_to_class(
    138                 'DoesNotExist',
    139                 subclass_exception(
    140                     str('DoesNotExist'),
    141                     tuple(x.DoesNotExist for x in parents if hasattr(x, '_meta') and not x._meta.abstract) or (ObjectDoesNotExist,),
    142                     module,
    143                     attached_to=new_class))
    144             new_class.add_to_class(
    145                 'MultipleObjectsReturned',
    146                 subclass_exception(
    147                     str('MultipleObjectsReturned'),
    148                     tuple(x.MultipleObjectsReturned for x in parents if hasattr(x, '_meta') and not x._meta.abstract) or (MultipleObjectsReturned,),
    149                     module,
    150                     attached_to=new_class))
    151             if base_meta and not base_meta.abstract:
    152                 # Non-abstract child classes inherit some attributes from their
    153                 # non-abstract parent (unless an ABC comes before it in the
    154                 # method resolution order).
    155                 if not hasattr(meta, 'ordering'):
    156                     new_class._meta.ordering = base_meta.ordering
    157                 if not hasattr(meta, 'get_latest_by'):
    158                     new_class._meta.get_latest_by = base_meta.get_latest_by
    159 
    160         is_proxy = new_class._meta.proxy
    161 
    162         # If the model is a proxy, ensure that the base class
    163         # hasn't been swapped out.
    164         if is_proxy and base_meta and base_meta.swapped:
    165             raise TypeError("%s cannot proxy the swapped model '%s'." % (name, base_meta.swapped))
    166 
    167         if getattr(new_class, '_default_manager', None):
    168             if not is_proxy:
    169                 # Multi-table inheritance doesn't inherit default manager from
    170                 # parents.
    171                 new_class._default_manager = None
    172                 new_class._base_manager = None
    173             else:
    174                 # Proxy classes do inherit parent's default manager, if none is
    175                 # set explicitly.
    176                 new_class._default_manager = new_class._default_manager._copy_to_model(new_class)
    177                 new_class._base_manager = new_class._base_manager._copy_to_model(new_class)
    178 
    179         # Add all attributes to the class.
    180         for obj_name, obj in attrs.items():
    181             new_class.add_to_class(obj_name, obj)
    182 
    183         # All the fields of any type declared on this model
    184         new_fields = (
    185             new_class._meta.local_fields +
    186             new_class._meta.local_many_to_many +
    187             new_class._meta.virtual_fields
    188         )
    189         field_names = set(f.name for f in new_fields)
    190 
    191         # Basic setup for proxy models.
    192         if is_proxy:
    193             base = None
    194             for parent in [kls for kls in parents if hasattr(kls, '_meta')]:
    195                 if parent._meta.abstract:
    196                     if parent._meta.fields:
    197                         raise TypeError("Abstract base class containing model fields not permitted for proxy model '%s'." % name)
     148            # Basic setup for proxy models.
     149            if is_proxy:
     150                base = None
     151                for parent in [kls for kls in parents if hasattr(kls, '_meta')]:
     152                    if parent._meta.abstract:
     153                        if parent._meta.fields:
     154                            raise TypeError("Abstract base class containing model fields not permitted for proxy model '%s'." % name)
     155                        else:
     156                            continue
     157                    if base is not None:
     158                        raise TypeError("Proxy model '%s' has more than one non-abstract model base class." % name)
    198159                    else:
    199                         continue
    200                 if base is not None:
    201                     raise TypeError("Proxy model '%s' has more than one non-abstract model base class." % name)
    202                 else:
    203                     base = parent
    204             if base is None:
    205                 raise TypeError("Proxy model '%s' has no non-abstract model base class." % name)
    206             if (new_class._meta.local_fields or
    207                     new_class._meta.local_many_to_many):
    208                 raise FieldError("Proxy model '%s' contains model fields." % name)
    209             new_class._meta.setup_proxy(base)
    210             new_class._meta.concrete_model = base._meta.concrete_model
    211         else:
    212             new_class._meta.concrete_model = new_class
     160                        base = parent
     161                if base is None:
     162                    raise TypeError("Proxy model '%s' has no non-abstract model base class." % name)
     163                if (new_class._meta.local_fields or
     164                        new_class._meta.local_many_to_many):
     165                    raise FieldError("Proxy model '%s' contains model fields." % name)
     166                new_class._meta.setup_proxy(base)
     167                new_class._meta.concrete_model = base._meta.concrete_model
     168            else:
     169                new_class._meta.concrete_model = new_class
    213170
    214         # Collect the parent links for multi-table inheritance.
    215         parent_links = {}
    216         for base in reversed([new_class] + parents):
    217             # Conceptually equivalent to `if base is Model`.
    218             if not hasattr(base, '_meta'):
    219                 continue
    220             # Skip concrete parent classes.
    221             if base != new_class and not base._meta.abstract:
    222                 continue
    223             # Locate OneToOneField instances.
    224             for field in base._meta.local_fields:
    225                 if isinstance(field, OneToOneField):
    226                     parent_links[field.rel.to] = field
    227 
    228         # Do the appropriate setup for any model parents.
    229         for base in parents:
    230             original_base = base
    231             if not hasattr(base, '_meta'):
    232                 # Things without _meta aren't functional models, so they're
    233                 # uninteresting parents.
    234                 continue
     171            # Collect the parent links for multi-table inheritance.
     172            parent_links = {}
     173            for base in reversed([new_class] + parents):
     174                # Conceptually equivalent to `if base is Model`.
     175                if not hasattr(base, '_meta'):
     176                    continue
     177                # Skip concrete parent classes.
     178                if base != new_class and not base._meta.abstract:
     179                    continue
     180                # Locate OneToOneField instances.
     181                for field in base._meta.local_fields:
     182                    if isinstance(field, OneToOneField):
     183                        parent_links[field.rel.to] = field
     184
     185            # Do the appropriate setup for any model parents.
     186            for base in parents:
     187                original_base = base
     188                if not hasattr(base, '_meta'):
     189                    # Things without _meta aren't functional models, so they're
     190                    # uninteresting parents.
     191                    continue
    235192
    236             parent_fields = base._meta.local_fields + base._meta.local_many_to_many
    237             # Check for clashes between locally declared fields and those
    238             # on the base classes (we cannot handle shadowed fields at the
    239             # moment).
    240             for field in parent_fields:
    241                 if field.name in field_names:
    242                     raise FieldError(
    243                         'Local field %r in class %r clashes '
    244                         'with field of similar name from '
    245                         'base class %r' % (field.name, name, base.__name__)
    246                     )
    247             if not base._meta.abstract:
    248                 # Concrete classes...
    249                 base = base._meta.concrete_model
    250                 if base in parent_links:
    251                     field = parent_links[base]
    252                 elif not is_proxy:
    253                     attr_name = '%s_ptr' % base._meta.model_name
    254                     field = OneToOneField(base, name=attr_name,
    255                             auto_created=True, parent_link=True)
    256                     # Only add the ptr field if it's not already present;
    257                     # e.g. migrations will already have it specified
    258                     if not hasattr(new_class, attr_name):
    259                         new_class.add_to_class(attr_name, field)
    260                 else:
    261                     field = None
    262                 new_class._meta.parents[base] = field
    263             else:
    264                 # .. and abstract ones.
     193                parent_fields = base._meta.local_fields + base._meta.local_many_to_many
     194                # Check for clashes between locally declared fields and those
     195                # on the base classes (we cannot handle shadowed fields at the
     196                # moment).
    265197                for field in parent_fields:
     198                    if field.name in field_names:
     199                        raise FieldError(
     200                            'Local field %r in class %r clashes '
     201                            'with field of similar name from '
     202                            'base class %r' % (field.name, name, base.__name__)
     203                        )
     204                if not base._meta.abstract:
     205                    # Concrete classes...
     206                    base = base._meta.concrete_model
     207                    if base in parent_links:
     208                        field = parent_links[base]
     209                    elif not is_proxy:
     210                        attr_name = '%s_ptr' % base._meta.model_name
     211                        field = OneToOneField(base, name=attr_name,
     212                                auto_created=True, parent_link=True)
     213                        # Only add the ptr field if it's not already present;
     214                        # e.g. migrations will already have it specified
     215                        if not hasattr(new_class, attr_name):
     216                            new_class.add_to_class(attr_name, field)
     217                    else:
     218                        field = None
     219                    new_class._meta.parents[base] = field
     220                else:
     221                    # .. and abstract ones.
     222                    for field in parent_fields:
     223                        new_class.add_to_class(field.name, copy.deepcopy(field))
     224
     225                    # Pass any non-abstract parent classes onto child.
     226                    new_class._meta.parents.update(base._meta.parents)
     227
     228                # Inherit managers from the abstract base classes.
     229                new_class.copy_managers(base._meta.abstract_managers)
     230
     231                # Proxy models inherit the non-abstract managers from their base,
     232                # unless they have redefined any of them.
     233                if is_proxy:
     234                    new_class.copy_managers(original_base._meta.concrete_managers)
     235
     236                # Inherit virtual fields (like GenericForeignKey) from the parent
     237                # class
     238                for field in base._meta.virtual_fields:
     239                    if base._meta.abstract and field.name in field_names:
     240                        raise FieldError(
     241                            'Local field %r in class %r clashes '
     242                            'with field of similar name from '
     243                            'abstract base class %r' % (field.name, name, base.__name__)
     244                        )
    266245                    new_class.add_to_class(field.name, copy.deepcopy(field))
    267246
    268                 # Pass any non-abstract parent classes onto child.
    269                 new_class._meta.parents.update(base._meta.parents)
    270 
    271             # Inherit managers from the abstract base classes.
    272             new_class.copy_managers(base._meta.abstract_managers)
     247            if abstract:
     248                # Abstract base models can't be instantiated and don't appear in
     249                # the list of models for an app. We do the final setup for them a
     250                # little differently from normal models.
     251                attr_meta.abstract = False
     252                new_class.Meta = attr_meta
     253            else:
     254                new_class._prepare()
    273255
    274             # Proxy models inherit the non-abstract managers from their base,
    275             # unless they have redefined any of them.
    276             if is_proxy:
    277                 new_class.copy_managers(original_base._meta.concrete_managers)
    278 
    279             # Inherit virtual fields (like GenericForeignKey) from the parent
    280             # class
    281             for field in base._meta.virtual_fields:
    282                 if base._meta.abstract and field.name in field_names:
    283                     raise FieldError(
    284                         'Local field %r in class %r clashes '
    285                         'with field of similar name from '
    286                         'abstract base class %r' % (field.name, name, base.__name__)
    287                     )
    288                 new_class.add_to_class(field.name, copy.deepcopy(field))
    289 
    290         if abstract:
    291             # Abstract base models can't be instantiated and don't appear in
    292             # the list of models for an app. We do the final setup for them a
    293             # little differently from normal models.
    294             attr_meta.abstract = False
    295             new_class.Meta = attr_meta
    296             return new_class
    297 
    298         new_class._prepare()
    299         new_class._meta.apps.register_model(new_class._meta.app_label, new_class)
     256        class_registered.connect(_setup, sender=new_class, weak=False)
     257        new_class._meta.apps.register_model(new_class)
    300258        return new_class
    301259
    302260    def copy_managers(cls, base_managers):
  • django/db/models/options.py

    diff --git a/django/db/models/options.py b/django/db/models/options.py
    index d204bcf..cfbbb8b 100644
    a b from django.db.models.fields.related import ManyToManyRel  
    1010from django.db.models.fields import AutoField, FieldDoesNotExist
    1111from django.db.models.fields.proxy import OrderWrt
    1212from django.utils import six
    13 from django.utils.deprecation import RemovedInDjango18Warning
     13from django.utils.deprecation import RemovedInDjango18Warning, RemovedInDjango19Warning
    1414from django.utils.encoding import force_text, smart_text, python_2_unicode_compatible
    1515from django.utils.functional import cached_property
    1616from django.utils.text import camel_case_to_spaces
    def normalize_together(option_together):  
    4949
    5050@python_2_unicode_compatible
    5151class Options(object):
     52
    5253    def __init__(self, meta, app_label=None):
     54        if app_label is not None:
     55            warnings.warn(
     56                "The app_label argument of Options is deprecated.",
     57                RemovedInDjango19Warning, stacklevel=2)
    5358        self.local_fields = []
    5459        self.local_many_to_many = []
    5560        self.virtual_fields = []
  • django/db/models/signals.py

    diff --git a/django/db/models/signals.py b/django/db/models/signals.py
    index 54ce1f0..f4f2d55 100644
    a b from django.dispatch import Signal  
    33from django.utils import six
    44
    55
     6class_registered = Signal(providing_args=["class"])
    67class_prepared = Signal(providing_args=["class"])
    78
    89
Back to Top