Django之ModelSignals

from functools import partial

from django.db.models.utils import make_model_tuple
from django.dispatch import Signal


class_prepared = Signal(providing_args=["class"])


class ModelSignal(Signal):
    """
    Signal subclass that allows the sender to be lazily specified as a string
    of the `app_label.ModelName` form.
    """
    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 connect(self, receiver, sender=None, weak=True, dispatch_uid=None, apps=None):
        self._lazy_method(
            super().connect, apps, receiver, sender,
            weak=weak, dispatch_uid=dispatch_uid,
        )

    def disconnect(self, receiver=None, sender=None, dispatch_uid=None, apps=None):
        return self._lazy_method(
            super().disconnect, apps, receiver, sender, dispatch_uid=dispatch_uid
        )


pre_init = ModelSignal(providing_args=["instance", "args", "kwargs"], use_caching=True)
post_init = ModelSignal(providing_args=["instance"], use_caching=True)

pre_save = ModelSignal(providing_args=["instance", "raw", "using", "update_fields"],
                       use_caching=True)
post_save = ModelSignal(providing_args=["instance", "raw", "created", "using", "update_fields"], use_caching=True)

pre_delete = ModelSignal(providing_args=["instance", "using"], use_caching=True)
post_delete = ModelSignal(providing_args=["instance", "using"], use_caching=True)

m2m_changed = ModelSignal(
    providing_args=["action", "instance", "reverse", "model", "pk_set", "using"],
    use_caching=True,
)

pre_migrate = Signal(providing_args=["app_config", "verbosity", "interactive", "using", "apps", "plan"])
post_migrate = Signal(providing_args=["app_config", "verbosity", "interactive", "using", "apps", "plan"])
先记录下,之后慢慢学习~
class Model(metaclass=ModelBase):
    def __init__(self, *args, **kwargs):
       # Alias some things as locals to avoid repeat global lookups
       cls = self.__class__
       opts = self._meta
       _setattr = setattr
       _DEFERRED = DEFERRED
    #初始化前发送信号
       pre_init.send(sender=cls, args=args, kwargs=kwargs)

       # Set up the storage for instance state
       self._state = ModelState()

       # There is a rather weird disparity here; if kwargs, it's set, then args
       # overrides it. It should be one or the other; don't duplicate the work
       # The reason for the kwargs check is that standard iterator passes in by
       # args, and instantiation for iteration is 33% faster.
       if len(args) > len(opts.concrete_fields):
           # Daft, but matches old exception sans the err msg.
           raise IndexError("Number of args exceeds number of fields")

       if not kwargs:
           fields_iter = iter(opts.concrete_fields)
           # The ordering of the zip calls matter - zip throws StopIteration
           # when an iter throws it. So if the first iter throws it, the second
           # is *not* consumed. We rely on this, so don't change the order
           # without changing the logic.
           for val, field in zip(args, fields_iter):
               if val is _DEFERRED:
                   continue
               _setattr(self, field.attname, val)
       else:
           # Slower, kwargs-ready version.
           fields_iter = iter(opts.fields)
           for val, field in zip(args, fields_iter):
               if val is _DEFERRED:
                   continue
               _setattr(self, field.attname, val)
               kwargs.pop(field.name, None)

       # Now we're left with the unprocessed fields that *must* come from
       # keywords, or default.

       for field in fields_iter:
           is_related_object = False
           # Virtual field
           if field.attname not in kwargs and field.column is None:
               continue
           if kwargs:
               if isinstance(field.remote_field, ForeignObjectRel):
                   try:
                       # Assume object instance was passed in.
                       rel_obj = kwargs.pop(field.name)
                       is_related_object = True
                   except KeyError:
                       try:
                           # Object instance wasn't passed in -- must be an ID.
                           val = kwargs.pop(field.attname)
                       except KeyError:
                           val = field.get_default()
                   else:
                       # Object instance was passed in. Special case: You can
                       # pass in "None" for related objects if it's allowed.
                       if rel_obj is None and field.null:
                           val = None
               else:
                   try:
                       val = kwargs.pop(field.attname)
                   except KeyError:
                       # This is done with an exception rather than the
                       # default argument on pop because we don't want
                       # get_default() to be evaluated, and then not used.
                       # Refs #12057.
                       val = field.get_default()
           else:
               val = field.get_default()

           if is_related_object:
               # If we are passed a related instance, set it using the
               # field.name instead of field.attname (e.g. "user" instead of
               # "user_id") so that the object gets properly cached (and type
               # checked) by the RelatedObjectDescriptor.
               if rel_obj is not _DEFERRED:
                   _setattr(self, field.name, rel_obj)
           else:
               if val is not _DEFERRED:
                   _setattr(self, field.attname, val)

       if kwargs:
           property_names = opts._property_names
           for prop in tuple(kwargs):
               try:
                   # Any remaining kwargs must correspond to properties or
                   # virtual fields.
                   if prop in property_names or opts.get_field(prop):
                       if kwargs[prop] is not _DEFERRED:
                           _setattr(self, prop, kwargs[prop])
                       del kwargs[prop]
               except (AttributeError, FieldDoesNotExist):
                   pass
           for kwarg in kwargs:
               raise TypeError("'%s' is an invalid keyword argument for this function" % kwarg)
       super().__init__()
       #初始化完毕发送信号
       post_init.send(sender=cls, instance=self)
    def save_base(self, raw=False, force_insert=False,
                force_update=False, using=None, update_fields=None):
      """
      Handle the parts of saving which should be done only once per save,
      yet need to be done in raw saves, too. This includes some sanity
      checks and signal sending.

      The 'raw' argument is telling save_base not to save any parent
      models and not to do any changes to the values before save. This
      is used by fixture loading.
      """
      using = using or router.db_for_write(self.__class__, instance=self)
      assert not (force_insert and (force_update or update_fields))
      assert update_fields is None or len(update_fields) > 0
      cls = origin = self.__class__
      # Skip proxies, but keep the origin as the proxy model.
      if cls._meta.proxy:
          cls = cls._meta.concrete_model
      meta = cls._meta
      if not meta.auto_created:
       #数据保存前发送信号
          pre_save.send(
              sender=origin, instance=self, raw=raw, using=using,
              update_fields=update_fields,
          )
      with transaction.atomic(using=using, savepoint=False):
          if not raw:
              self._save_parents(cls, using, update_fields)
          updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
      # Store the database on which the object was saved
      self._state.db = using
      # Once saved, this is no longer a to-be-added instance.
      self._state.adding = False

      # Signal that the save is complete
      if not meta.auto_created:
       #数据保存后发送信号
          post_save.send(
              sender=origin, instance=self, created=(not updated),
              update_fields=update_fields, raw=raw, using=using,
          )

ModelBase类:

class ModelBase(type):
    def _prepare(cls):
        """Create some methods once self._meta has been populated."""
        opts = cls._meta
        opts._prepare(cls)

        if opts.order_with_respect_to:
            cls.get_next_in_order = partialmethod(cls._get_next_or_previous_in_order, is_next=True)
            cls.get_previous_in_order = partialmethod(cls._get_next_or_previous_in_order, is_next=False)

            # Defer creating accessors on the foreign class until it has been
            # created and registered. If remote_field is None, we're ordering
            # with respect to a GenericForeignKey and don't know what the
            # foreign class is - we'll add those accessors later in
            # contribute_to_class().
            if opts.order_with_respect_to.remote_field:
                wrt = opts.order_with_respect_to
                remote = wrt.remote_field.model
                lazy_related_operation(make_foreign_order_accessors, cls, remote)

        # Give the class a docstring -- its definition.
        if cls.__doc__ is None:
            cls.__doc__ = "%s(%s)" % (cls.__name__, ", ".join(f.name for f in opts.fields))

        get_absolute_url_override = settings.ABSOLUTE_URL_OVERRIDES.get(opts.label_lower)
        if get_absolute_url_override:
            setattr(cls, 'get_absolute_url', get_absolute_url_override)

        if not opts.managers:
            if any(f.name == 'objects' for f in opts.fields):
                raise ValueError(
                    "Model %s must specify a custom Manager, because it has a "
                    "field named 'objects'." % cls.__name__
                )
            manager = Manager()
            manager.auto_created = True
            cls.add_to_class('objects', manager)

        # Set the name of _meta.indexes. This can't be done in
        # Options.contribute_to_class() because fields haven't been added to
        # the model at that point.
        for index in cls._meta.indexes:
            if not index.name:
                index.set_name_with_model(cls)
        #类准备完毕发送信号
        class_prepared.send(sender=cls)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值