def get_concrete_model_key(self, model): ( concrete_models_mapping, _, ) = self._get_concrete_models_mapping_and_proxy_models() model_key = make_model_tuple(model) return concrete_models_mapping[model_key]
def __init__(self, real_apps, models, ignore_swappable=False): # Any apps in self.real_apps should have all their models included # in the render. We don't use the original model instances as there # are some variables that refer to the Apps object. # FKs/M2Ms from real apps are also not included as they just # mess things up with partial states (due to lack of dependencies) self.real_models = [] for app_label in real_apps: app = global_apps.get_app_config(app_label) for model in app.get_models(): self.real_models.append(ModelState.from_model(model, exclude_rels=True)) # Populate the app registry with a stub for each application. app_labels = {model_state.app_label for model_state in models.values()} app_configs = [AppConfigStub(label) for label in sorted(real_apps + list(app_labels))] super(StateApps, self).__init__(app_configs) # The lock gets in the way of copying as implemented in clone(), which # is called whenever Django duplicates a StateApps before updating it. self._lock = None self.render_multiple(list(models.values()) + self.real_models) # There shouldn't be any operations pending at this point. from django.core.checks.model_checks import _check_lazy_references ignore = {make_model_tuple(settings.AUTH_USER_MODEL)} if ignore_swappable else set() errors = _check_lazy_references(self, ignore=ignore) if errors: raise ValueError("\n".join(error.msg for error in errors))
def __init__(self, real_apps, models, ignore_swappable=False): # Any apps in self.real_apps should have all their models included # in the render. We don't use the original model instances as there # are some variables that refer to the Apps object. # FKs/M2Ms from real apps are also not included as they just # mess things up with partial states (due to lack of dependencies) self.real_models = [] for app_label in real_apps: app = global_apps.get_app_config(app_label) for model in app.get_models(): self.real_models.append(ModelState.from_model(model, exclude_rels=True)) # Populate the app registry with a stub for each application. app_labels = {model_state.app_label for model_state in models.values()} app_configs = [AppConfigStub(label) for label in sorted(real_apps + list(app_labels))] super(StateApps, self).__init__(app_configs) self.render_multiple(list(models.values()) + self.real_models) # There shouldn't be any operations pending at this point. pending_models = set(self._pending_operations) if ignore_swappable: pending_models -= {make_model_tuple(settings.AUTH_USER_MODEL)} if pending_models: msg = "Unhandled pending operations for models: %s" labels = (".".join(model_key) for model_key in self._pending_operations) raise ValueError(msg % ", ".join(labels))
def __init__(self, to, max_length=None, min_length=None, strip=True, empty_value='', selector='', **kwargs): super().__init__(**kwargs) self.selector = selector self.to = '' try: self.to = apps.get_model(*utils.make_model_tuple(to)) except AttributeError: assert isinstance(to, str), ( "%s(%r) is invalid. First parameter to ForeignField must be " "either a model, a model name, or the string %r" % ( self.__class__.__name__, to, RECURSIVE_RELATIONSHIP_CONSTANT, )) self.error_messages['target_exists'] = _('The %s must exist.' % self.to.__name__) self.error_messages['unique_target'] = _('The %s must be unique.' % self.to.__name__)
def __init__(self, real_apps, models, ignore_swappable=False): # Any apps in self.real_apps should have all their models included # in the render. We don't use the original model instances as there # are some variables that refer to the Apps object. # FKs/M2Ms from real apps are also not included as they just # mess things up with partial states (due to lack of dependencies) self.real_models = [] for app_label in real_apps: app = global_apps.get_app_config(app_label) for model in app.get_models(): self.real_models.append( ModelState.from_model(model, exclude_rels=True)) # Populate the app registry with a stub for each application. app_labels = {model_state.app_label for model_state in models.values()} app_configs = [ AppConfigStub(label) for label in sorted(real_apps + list(app_labels)) ] super().__init__(app_configs) # The lock gets in the way of copying as implemented in clone(), which # is called whenever Django duplicates a StateApps before updating it. self._lock = None self.render_multiple(list(models.values()) + self.real_models) # There shouldn't be any operations pending at this point. from django.core.checks.model_checks import _check_lazy_references ignore = {make_model_tuple(settings.AUTH_USER_MODEL) } if ignore_swappable else set() errors = _check_lazy_references(self, ignore=ignore) if errors: raise ValueError("\n".join(error.msg for error in errors))
def connect(self, receiver, sender=None, weak=True, dispatch_uid=None, apps=None): # Takes a single optional argument named "sender" connect = partial(super(ModelSignal, self).connect, receiver, weak=weak, dispatch_uid=dispatch_uid) models = [make_model_tuple(sender)] if sender else [] if not apps: from django.db.models.base import Options apps = sender._meta.apps if hasattr(sender, '_meta') else Options.default_apps apps.lazy_model_operation(connect, *models)
def _find_concrete_model_from_proxy(self, proxy_models, model_state): for base in model_state.bases: base_key = make_model_tuple(base) base_state = proxy_models.get(base_key) if not base_state: # Concrete model found, stop looking at bases. return base_key return self._find_concrete_model_from_proxy(proxy_models, base_state)
def create_many_to_many_intermediary_model(field, klass): from django.db import models def set_managed(model, related, through): through._meta.managed = model._meta.managed or related._meta.managed to_model = resolve_relation(klass, field.remote_field.model) name = '%s_%s' % (klass._meta.object_name, field.name) lazy_related_operation(set_managed, klass, to_model, name) to = make_model_tuple(to_model)[1] from_ = klass._meta.model_name if to == from_: to = 'to_%s' % to from_ = 'from_%s' % from_ meta = type( str('Meta'), (object, ), { 'db_table': field._get_m2m_db_table(klass._meta), 'auto_created': klass, 'app_label': klass._meta.app_label, 'db_tablespace': klass._meta.db_tablespace, 'unique_together': (from_, to), 'verbose_name': _('%(from)s-%(to)s relationship') % { 'from': from_, 'to': to }, 'verbose_name_plural': _('%(from)s-%(to)s relationships') % { 'from': from_, 'to': to }, 'apps': field.model._meta.apps, }) # Construct and return the new class. return type( str(name), (models.Model, ), { 'Meta': meta, '__module__': klass.__module__, from_: models.ForeignKey( klass, related_name='%s+' % name, db_tablespace=field.db_tablespace, db_constraint=field.remote_field.db_constraint, on_delete=DATABASE_CASCADE, ), to: models.ForeignKey( to_model, related_name='%s+' % name, db_tablespace=field.db_tablespace, db_constraint=field.remote_field.db_constraint, on_delete=DATABASE_CASCADE, ) })
def _find_concrete_model_from_proxy(self, proxy_models, model_state): for base in model_state.bases: if not (isinstance(base, str) or issubclass(base, models.Model)): continue base_key = make_model_tuple(base) base_state = proxy_models.get(base_key) if not base_state: # Concrete model found, stop looking at bases. return base_key return self._find_concrete_model_from_proxy(proxy_models, base_state)
def _lazy_method(self, method, apps, receiver, sender, **kwargs): from django.db.models.options import Options # This partial takes a single optional argument named "sender". partial_method = partial(method, receiver, **kwargs) if isinstance(sender, str): apps = apps or Options.default_apps apps.lazy_model_operation(partial_method, make_model_tuple(sender)) else: return partial_method(sender)
def _lazy_method(self, method, apps, receiver, sender, **kwargs): from django.db.models.options import Options # This partial takes a single optional argument named "sender". partial_method = partial(method, receiver, **kwargs) if isinstance(sender, six.string_types): apps = apps or Options.default_apps apps.lazy_model_operation(partial_method, make_model_tuple(sender)) else: return partial_method(sender)
def _lazy_method(self, method, apps, receiver, sender, **kwargs): # This partial takes a single optional argument named "sender". partial_method = partial(method, receiver, **kwargs) # import models here to avoid a circular import from django.db import models if isinstance(sender, models.Model) or sender is None: # Skip lazy_model_operation to get a return value for disconnect() return partial_method(sender) apps = apps or models.base.Options.default_apps apps.lazy_model_operation(partial_method, make_model_tuple(sender))
def get_allowed_models() -> Tuple[List[str], List[str]]: """ Возвращает два списка моделей, для которых возможна перенарезка: список обычных моделей и список галерей. """ regular_models = [] gallery_models = [] for app_conf in apps.get_app_configs(): if app_conf.name == 'paper_uploads': continue for model in app_conf.get_models(): if get_allowed_fields(model): if is_gallery(model): gallery_models.append( '{}.{}'.format(*make_model_tuple(model))) else: regular_models.append( '{}.{}'.format(*make_model_tuple(model))) return regular_models, gallery_models
def create_sortable_many_to_many_intermediary_model(field, klass, sort_field_name, base_classes=None): def set_managed(model, related, through): through._meta.managed = model._meta.managed or related._meta.managed to_model = resolve_relation(klass, field.remote_field.model) name = '%s_%s' % (klass._meta.object_name, field.name) lazy_related_operation(set_managed, klass, to_model, name) base_classes = base_classes if base_classes else (models.Model,) # TODO : use autoincrement here ? sort_field = models.IntegerField(default=0) to = make_model_tuple(to_model)[1] from_ = klass._meta.model_name if to == from_: to = 'to_%s' % to from_ = 'from_%s' % from_ meta = type('Meta', (), { 'db_table': field._get_m2m_db_table(klass._meta), # pylint: disable=protected-access 'auto_created': klass, 'app_label': klass._meta.app_label, 'db_tablespace': klass._meta.db_tablespace, 'unique_together': (from_, to), 'ordering': (sort_field_name,), 'verbose_name': _('%(from)s-%(to)s relationship') % {'from': from_, 'to': to}, 'verbose_name_plural': _('%(from)s-%(to)s relationships') % {'from': from_, 'to': to}, 'apps': field.model._meta.apps, }) # Construct and return the new class. return type(force_str(name), base_classes, { 'Meta': meta, '__module__': klass.__module__, from_: models.ForeignKey( klass, related_name='%s+' % name, db_tablespace=field.db_tablespace, db_constraint=field.remote_field.db_constraint, on_delete=models.CASCADE, ), to: models.ForeignKey( to_model, related_name='%s+' % name, db_tablespace=field.db_tablespace, db_constraint=field.remote_field.db_constraint, on_delete=models.CASCADE, ), # Sort fields sort_field_name: sort_field, '_sort_field_name': sort_field_name, })
def connect(self, receiver, sender=None, weak=True, dispatch_uid=None, apps=None): # Takes a single optional argument named "sender" connect = partial(super(ModelSignal, self).connect, receiver, weak=weak, dispatch_uid=dispatch_uid) models = [make_model_tuple(sender)] if sender else [] if not apps: from django.db.models.base import Options apps = sender._meta.apps if hasattr( sender, '_meta') else Options.default_apps apps.lazy_model_operation(connect, *models)
def create_many_to_many_intermediary_model(field, klass): from django.db import models def set_managed(model, related, through): through._meta.managed = model._meta.managed or related._meta.managed to_model = resolve_relation(klass, field.remote_field.model) name = '%s_%s' % (klass._meta.object_name, field.name) lazy_related_operation(set_managed, klass, to_model, name) to = make_model_tuple(to_model)[1] from_ = klass._meta.model_name if to == from_: to = 'to_%s' % to from_ = 'from_%s' % from_ meta = type(str('Meta'), (object,), { 'db_table': field._get_m2m_db_table(klass._meta), 'auto_created': klass, 'app_label': klass._meta.app_label, 'db_tablespace': klass._meta.db_tablespace, 'unique_together': (from_, to), 'verbose_name': _('%(from)s-%(to)s relationship') % {'from': from_, 'to': to}, 'verbose_name_plural': _('%(from)s-%(to)s relationships') % {'from': from_, 'to': to}, 'apps': field.model._meta.apps, }) # Construct and return the new class. return type(str(name), (models.Model,), { 'Meta': meta, '__module__': klass.__module__, from_: models.ForeignKey( klass, related_name='%s+' % name, db_tablespace=field.db_tablespace, db_constraint=field.remote_field.db_constraint, on_delete=DATABASE_CASCADE, ), to: models.ForeignKey( to_model, related_name='%s+' % name, db_tablespace=field.db_tablespace, db_constraint=field.remote_field.db_constraint, on_delete=DATABASE_CASCADE, ) })
def rebuild_photo_index(sender_class, instance_id, action_name): """ Given an individual model instance, determine which backends the update/update should be sent to & update the object on those backends. """ if action_name not in ( 'update_object', 'remove_object', ): logger.error( "Action '{}' is not valid operation for haystack index.".format( action_name), exc_info=True) return try: haystack_signal_processor = apps.get_app_config( 'haystack').signal_processor model = apps.get_model(*make_model_tuple(sender_class)) instance = _get_instance(model, instance_id) except Exception: logger.error( "An error occurred updating photo index id '{}' with the current haystack application " "configuration".format(instance_id), extra={ 'sender': sender_class, 'instance': instance_id }, exc_info=True) return using_backends = haystack_signal_processor.connection_router.for_write( instance=instance) for using in using_backends: try: index = haystack_signal_processor.connections[ using].get_unified_index().get_index(sender_class) getattr(index, action_name)(instance, using=using) except NotHandled: logger.info( "There is not index defined for the sender '{}' class".format( sender_class._meta.label))
def __new__(mcs, name, bases, attrs): from gdpr.loading import purpose_register new_class = super().__new__(mcs, name, bases, attrs) if hasattr(new_class, 'slug') and new_class.slug: if new_class.slug in purpose_register: raise ImproperlyConfigured( 'More anonymization purposes with slug {}'.format( new_class.slug)) purpose_register.register(new_class.slug, new_class) def set_source_model_class(model): new_class.source_model_class = model if isinstance(new_class.source_model_class, str): apps.lazy_model_operation( set_source_model_class, make_model_tuple(new_class.source_model_class)) return new_class
def _lazy_method(self, method, apps, receiver, sender, **kwargs): """ 参数说明: method : 父类中定义的 django.db.models.signals.ModelSignal.connect 方法 apps : None receiver : 信号接收者,通常是一个可调用对象 sender : 信号发送者,可能是一个映射类 kwargs : 字典 {'weak': True, 'dispatch_uid': None} """ from django.db.models.options import Options # 下面这个偏函数是来自父类 django.dispatch.dispatcher.Signal 的 connect 方法 # 要调用此偏函数,只需要提供 sender 参数就行了 partial_method = partial(method, receiver, **kwargs) if isinstance(sender, str): apps = apps or Options.default_apps apps.lazy_model_operation(partial_method, make_model_tuple(sender)) else: # 返回偏函数的调用,也就是调用父类的 connect 方法 partial_method(sender)
def create_versioned_many_to_many_intermediary_model(field, cls, field_name): # TODO: Verify functionality against # django.db.models.fields.related:1048 # Let's not care too much on what flags could potentially be set on # that intermediary class (e.g. managed, etc) # Let's play the game, as if the programmer had specified a class # within his models... Here's how. # FIXME: VersionedManyToManyModels do not get registered in the # apps models. # FIXME: This is usually done at django/db/models/base.py:284, # invoked by create_many_to_many_intermediary_model at # django.db.models.fields.related:1048 def set_managed(model, related, through): through._meta.managed = model._meta.managed or \ related._meta.managed to_model = resolve_relation(cls, field.remote_field.model) name = '%s_%s' % (cls._meta.object_name, field_name) lazy_related_operation(set_managed, cls, to_model, name) # Force 'to' to be a string (and leave the hard work to Django) to = make_model_tuple(to_model)[1] from_ = cls._meta.model_name if to == from_: from_ = 'from_%s' % from_ to = 'to_%s' % to meta = type('Meta', (object,), { 'db_table': field._get_m2m_db_table(cls._meta), 'auto_created': cls, 'app_label': cls._meta.app_label, 'db_tablespace': cls._meta.db_tablespace, # 'unique_together' is not applicable as is, due to multiple # versions to be allowed to exist. # 'unique_together': (from_, to), 'verbose_name': '%(from)s-%(to)s relationship' % {'from': from_, 'to': to}, 'verbose_name_plural': '%(from)s-%(to)s relationships' % { 'from': from_, 'to': to}, 'apps': field.model._meta.apps, }) return type(str(name), (Versionable,), { 'Meta': meta, '__module__': cls.__module__, from_: VersionedForeignKey( cls, related_name='%s+' % name, db_tablespace=field.db_tablespace, db_constraint=field.remote_field.db_constraint, auto_created=name, on_delete=DO_NOTHING, ), to: VersionedForeignKey( to_model, related_name='%s+' % name, db_tablespace=field.db_tablespace, db_constraint=field.remote_field.db_constraint, auto_created=name, on_delete=DO_NOTHING, ), })
def create_versioned_many_to_many_intermediary_model( field, cls, field_name): # TODO: Verify functionality against # django.db.models.fields.related:1048 # Let's not care too much on what flags could potentially be set on # that intermediary class (e.g. managed, etc) # Let's play the game, as if the programmer had specified a class # within his models... Here's how. # FIXME: VersionedManyToManyModels do not get registered in the # apps models. # FIXME: This is usually done at django/db/models/base.py:284, # invoked by create_many_to_many_intermediary_model at # django.db.models.fields.related:1048 def set_managed(model, related, through): through._meta.managed = model._meta.managed or \ related._meta.managed to_model = resolve_relation(cls, field.remote_field.model) name = '%s_%s' % (cls._meta.object_name, field_name) lazy_related_operation(set_managed, cls, to_model, name) # Force 'to' to be a string (and leave the hard work to Django) to = make_model_tuple(to_model)[1] from_ = cls._meta.model_name if to == from_: from_ = 'from_%s' % from_ to = 'to_%s' % to meta = type( 'Meta', (object, ), { 'db_table': field._get_m2m_db_table(cls._meta), 'auto_created': cls, 'app_label': cls._meta.app_label, 'db_tablespace': cls._meta.db_tablespace, # 'unique_together' is not applicable as is, due to multiple # versions to be allowed to exist. # 'unique_together': (from_, to), 'verbose_name': '%(from)s-%(to)s relationship' % { 'from': from_, 'to': to }, 'verbose_name_plural': '%(from)s-%(to)s relationships' % { 'from': from_, 'to': to }, 'apps': field.model._meta.apps, }) return type( str(name), (Versionable, ), { 'Meta': meta, '__module__': cls.__module__, from_: VersionedForeignKey( cls, related_name='%s+' % name, db_tablespace=field.db_tablespace, db_constraint=field.remote_field.db_constraint, auto_created=name, on_delete=DO_NOTHING, ), to: VersionedForeignKey( to_model, related_name='%s+' % name, db_tablespace=field.db_tablespace, db_constraint=field.remote_field.db_constraint, auto_created=name, on_delete=DO_NOTHING, ), })
def __new__(cls, name, bases, attrs): super_new = super(MemModelBase, cls).__new__ # Also ensure initialization is only performed for subclasses of Model # (excluding Model class itself). parents = [b for b in bases if isinstance(b, MemModelBase)] if not parents: return super_new(cls, name, bases, attrs) # Create the class. module = attrs.pop('__module__') new_class = super_new(cls, name, bases, {'__module__': module}) attr_meta = attrs.pop('Meta', None) abstract = getattr(attr_meta, 'abstract', False) if not attr_meta: meta = getattr(new_class, 'Meta', None) else: meta = attr_meta base_meta = getattr(new_class, '_meta', None) app_label = None # Look for an application configuration to attach the model to. app_config = apps.get_containing_app_config(module) if getattr(meta, 'app_label', None) is None: if app_config is None: if not abstract: raise RuntimeError( "Model class %s.%s doesn't declare an explicit " "app_label and isn't in an application in " "INSTALLED_APPS." % (module, name)) else: app_label = app_config.label new_class.add_to_class('_meta', Options(meta, app_label)) if not abstract: new_class.add_to_class( 'DoesNotExist', subclass_exception( str('DoesNotExist'), tuple(x.DoesNotExist for x in parents if hasattr(x, '_meta') and not x._meta.abstract) or (ObjectDoesNotExist, ), module, attached_to=new_class)) new_class.add_to_class( 'MultipleObjectsReturned', subclass_exception( str('MultipleObjectsReturned'), tuple(x.MultipleObjectsReturned for x in parents if hasattr(x, '_meta') and not x._meta.abstract) or (MultipleObjectsReturned, ), module, attached_to=new_class)) if base_meta and not base_meta.abstract: # Non-abstract child classes inherit some attributes from their # non-abstract parent (unless an ABC comes before it in the # method resolution order). if not hasattr(meta, 'ordering'): new_class._meta.ordering = base_meta.ordering if not hasattr(meta, 'get_latest_by'): new_class._meta.get_latest_by = base_meta.get_latest_by if getattr(new_class, '_default_manager', None): # Multi-table inheritance doesn't inherit default manager from # parents. new_class._default_manager = None new_class._base_manager = None # Add all attributes to the class. for obj_name, obj in attrs.items(): new_class.add_to_class(obj_name, obj) # All the fields of any type declared on this model new_fields = chain(new_class._meta.local_fields, new_class._meta.local_many_to_many, new_class._meta.virtual_fields) field_names = {f.name for f in new_fields} new_class._meta.concrete_model = new_class # Collect the parent links for multi-table inheritance. parent_links = {} for base in reversed([new_class] + parents): # Conceptually equivalent to `if base is Model`. if not hasattr(base, '_meta'): continue # Skip concrete parent classes. if base != new_class and not base._meta.abstract: continue # Locate OneToOneField instances. for field in base._meta.local_fields: if isinstance(field, OneToOneField): related = resolve_relation(new_class, field.remote_field.model) parent_links[make_model_tuple(related)] = field # Do the appropriate setup for any model parents. for base in parents: if not hasattr(base, '_meta'): # Things without _meta aren't functional models, so they're # uninteresting parents. continue parent_fields = base._meta.local_fields + base._meta.local_many_to_many # Check for clashes between locally declared fields and those # on the base classes (we cannot handle shadowed fields at the # moment). for field in parent_fields: if field.name in field_names: raise FieldError('Local field %r in class %r clashes ' 'with field of similar name from ' 'base class %r' % (field.name, name, base.__name__)) if not base._meta.abstract: # Concrete classes... base = base._meta.concrete_model base_key = make_model_tuple(base) if base_key in parent_links: field = parent_links[base_key] #elif not is_proxy: else: attr_name = '%s_ptr' % base._meta.model_name field = OneToOneField( base, on_delete=CASCADE, name=attr_name, auto_created=True, parent_link=True, ) # Only add the ptr field if it's not already present; # e.g. migrations will already have it specified if not hasattr(new_class, attr_name): new_class.add_to_class(attr_name, field) new_class._meta.parents[base] = field else: base_parents = base._meta.parents.copy() # .. and abstract ones. for field in parent_fields: new_field = copy.deepcopy(field) new_class.add_to_class(field.name, new_field) # Replace parent links defined on this base by the new # field as it will be appropriately resolved if required. if field.one_to_one: for parent, parent_link in base_parents.items(): if field == parent_link: base_parents[parent] = new_field # Pass any non-abstract parent classes onto child. new_class._meta.parents.update(base_parents) # Inherit managers from the abstract base classes. new_class.copy_managers(base._meta.abstract_managers) # Inherit virtual fields (like GenericForeignKey) from the parent # class for field in base._meta.virtual_fields: if base._meta.abstract and field.name in field_names: raise FieldError('Local field %r in class %r clashes ' 'with field of similar name from ' 'abstract base class %r' % (field.name, name, base.__name__)) new_class.add_to_class(field.name, copy.deepcopy(field)) if abstract: # Abstract base models can't be instantiated and don't appear in # the list of models for an app. We do the final setup for them a # little differently from normal models. attr_meta.abstract = False new_class.Meta = attr_meta return new_class new_class._prepare() new_class._meta.apps.register_model(new_class._meta.app_label, new_class) return new_class
def create_sortable_many_to_many_intermediary_model( field, klass, sort_field_name, base_classes=None ): def set_managed(model, related, through): through._meta.managed = model._meta.managed or related._meta.managed to_model = resolve_relation(klass, field.remote_field.model) name = "%s_%s" % (klass._meta.object_name, field.name) lazy_related_operation(set_managed, klass, to_model, name) base_classes = base_classes if base_classes else (models.Model,) # TODO : use autoincrement here ? sort_field = models.IntegerField(default=0) to = make_model_tuple(to_model)[1] from_ = klass._meta.model_name if to == from_: to = "to_%s" % to from_ = "from_%s" % from_ meta = type( "Meta", (), { "db_table": field._get_m2m_db_table(klass._meta), "auto_created": klass, "app_label": klass._meta.app_label, "db_tablespace": klass._meta.db_tablespace, "unique_together": (from_, to), "ordering": (sort_field_name,), "verbose_name": _("%(from)s-%(to)s relationship") % {"from": from_, "to": to}, "verbose_name_plural": _("%(from)s-%(to)s relationships") % {"from": from_, "to": to}, "apps": field.model._meta.apps, }, ) # Construct and return the new class. return type( force_str(name), base_classes, { "Meta": meta, "__module__": klass.__module__, from_: models.ForeignKey( klass, related_name="%s+" % name, db_tablespace=field.db_tablespace, db_constraint=field.remote_field.db_constraint, on_delete=models.CASCADE, ), to: models.ForeignKey( to_model, related_name="%s+" % name, db_tablespace=field.db_tablespace, db_constraint=field.remote_field.db_constraint, on_delete=models.CASCADE, ), # Sort fields sort_field_name: sort_field, "_sort_field_name": sort_field_name, }, )
def __new__(mcs, name, bases, attrs, **kwargs): super_new = super().__new__ # Also ensure initialization is only performed for subclasses of Model # (excluding Model class itself). parents = [b for b in bases if isinstance(b, ModelMeta)] if not parents: return super_new(mcs, name, bases, attrs, **kwargs) # Create the class. module = attrs.pop('__module__') new_attrs = {'__module__': module} classcell = attrs.pop('__classcell__', None) if classcell is not None: new_attrs['__classcell__'] = classcell attr_meta = attrs.pop('Meta', None) # Pass all attrs without a (Django-specific) contribute_to_class() # method to type.__new__() so that they're properly initialized # (i.e. __set_name__()). contributable_attrs = {} for obj_name, obj in list(attrs.items()): if _has_contribute_to_class(obj): contributable_attrs[obj_name] = obj else: new_attrs[obj_name] = obj new_class = super_new(mcs, name, bases, new_attrs, **kwargs) abstract = getattr(attr_meta, 'abstract', False) meta = attr_meta or getattr(new_class, 'Meta', None) base_meta = getattr(new_class, '_meta', None) app_label = None new_class.add_to_class('_meta', Options(meta, app_label)) if not abstract: new_class.add_to_class( 'DoesNotExist', subclass_exception( 'DoesNotExist', tuple(x.DoesNotExist for x in parents if hasattr(x, '_meta') and not x._meta.abstract) or (ObjectDoesNotExist, ), module, attached_to=new_class)) new_class.add_to_class( 'MultipleObjectsReturned', subclass_exception( 'MultipleObjectsReturned', tuple(x.MultipleObjectsReturned for x in parents if hasattr(x, '_meta') and not x._meta.abstract) or (MultipleObjectsReturned, ), module, attached_to=new_class)) if base_meta and not base_meta.abstract: # Non-abstract child classes inherit some attributes from their # non-abstract parent (unless an ABC comes before it in the # method resolution order). if not hasattr(meta, 'ordering'): new_class._meta.ordering = base_meta.ordering if not hasattr(meta, 'get_latest_by'): new_class._meta.get_latest_by = base_meta.get_latest_by is_proxy = new_class._meta.proxy # If the model is a proxy, ensure that the base class # hasn't been swapped out. if is_proxy and base_meta and base_meta.swapped: raise TypeError("%s cannot proxy the swapped model '%s'." % (name, base_meta.swapped)) # Add remaining attributes (those with a contribute_to_class() method) # to the class. for obj_name, obj in contributable_attrs.items(): new_class.add_to_class(obj_name, obj) # All the fields of any type declared on this model new_fields = chain(new_class._meta.local_fields, new_class._meta.local_many_to_many, new_class._meta.private_fields) field_names = {f.name for f in new_fields} # Basic setup for proxy models. new_class._meta.concrete_model = new_class # Collect the parent links for multi-table inheritance. parent_links = {} for base in reversed([new_class] + parents): # Conceptually equivalent to `if base is Model`. if not hasattr(base, '_meta'): continue # Skip concrete parent classes. if base != new_class and not base._meta.abstract: continue # Locate OneToOneField instances. for field in base._meta.local_fields: if isinstance( field, OneToOneField) and field.remote_field.parent_link: related = resolve_relation(new_class, field.remote_field.model) parent_links[make_model_tuple(related)] = field # Track fields inherited from base models. inherited_attributes = set() # Do the appropriate setup for any model parents. for base in new_class.mro(): if base not in parents or not hasattr(base, '_meta'): # Things without _meta aren't functional models, so they're # uninteresting parents. inherited_attributes.update(base.__dict__) continue parent_fields = base._meta.local_fields + base._meta.local_many_to_many if not base._meta.abstract: # Check for clashes between locally declared fields and those # on the base classes. for field in parent_fields: if field.name in field_names: raise FieldError( 'Local field %r in class %r clashes with field of ' 'the same name from base class %r.' % ( field.name, name, base.__name__, )) else: inherited_attributes.add(field.name) # Concrete classes... base = base._meta.concrete_model base_key = make_model_tuple(base) if base_key in parent_links: field = parent_links[base_key] elif not is_proxy: attr_name = '%s_ptr' % base._meta.model_name field = OneToOneField( base, on_delete=CASCADE, name=attr_name, auto_created=True, parent_link=True, ) if attr_name in field_names: raise FieldError( "Auto-generated field '%s' in class %r for " "parent_link to base class %r clashes with " "declared field of the same name." % ( attr_name, name, base.__name__, )) # Only add the ptr field if it's not already present; # e.g. migrations will already have it specified if not hasattr(new_class, attr_name): new_class.add_to_class(attr_name, field) else: field = None new_class._meta.parents[base] = field else: base_parents = base._meta.parents.copy() # Add fields from abstract base class if it wasn't overridden. for field in parent_fields: if (field.name not in field_names and field.name not in new_class.__dict__ and field.name not in inherited_attributes): new_field = copy.deepcopy(field) new_class.add_to_class(field.name, new_field) # Replace parent links defined on this base by the new # field. It will be appropriately resolved if required. if field.one_to_one: for parent, parent_link in base_parents.items(): if field == parent_link: base_parents[parent] = new_field # Pass any non-abstract parent classes onto child. new_class._meta.parents.update(base_parents) # Inherit private fields (like GenericForeignKey) from the parent # class for field in base._meta.private_fields: if field.name in field_names: if not base._meta.abstract: raise FieldError( 'Local field %r in class %r clashes with field of ' 'the same name from base class %r.' % ( field.name, name, base.__name__, )) else: field = copy.deepcopy(field) if not base._meta.abstract: field.mti_inherited = True new_class.add_to_class(field.name, field) # Copy indexes so that index names are unique when models extend an # abstract model. new_class._meta.indexes = [ copy.deepcopy(idx) for idx in new_class._meta.indexes ] new_class._prepare() return new_class
from __future__ import unicode_literals