找回密码
 立即注册
首页 业界区 安全 Django 懒加载实现方法

Django 懒加载实现方法

处匈跑 2025-8-18 09:48:32
Django 懒加载实现方法

在Django中,我们都知道当我们all之后是不会马上去查询数据的,当我们真正使用的时候,才会去触发查询操作。
模型类

下面我将带着大家从源码的角度一步一步解析。
  1. from django.db import models
  2. # Create your models here.
  3. class Demo1(models.Model):
  4.     name = models.CharField(max_length=100)
  5.     age = models.IntegerField()
  6.     def __str__(self):
  7.         return self.name
复制代码
看一下这几行代码发生了什么
使用是继承了models.Model。去看一下models.Model做了什么
  1. # models.Model
  2. class Model(AltersData, metaclass=ModelBase):
  3.     pass
复制代码
可以看到把ModelBase当成了元类,那我们就去再看一下ModelBase
  1. class ModelBase(type):
  2.     """Metaclass for all models."""
  3.     def __new__(cls, name, bases, attrs, **kwargs):
  4.         super_new = super().__new__
  5.         # Also ensure initialization is only performed for subclasses of Model
  6.         # (excluding Model class itself).
  7.         parents = [b for b in bases if isinstance(b, ModelBase)]
  8.         if not parents:
  9.             return super_new(cls, name, bases, attrs)
  10.         # Create the class.
  11.         module = attrs.pop("__module__")
  12.         new_attrs = {"__module__": module}
  13.         classcell = attrs.pop("__classcell__", None)
  14.         if classcell is not None:
  15.             new_attrs["__classcell__"] = classcell
  16.         attr_meta = attrs.pop("Meta", None)
  17.         # Pass all attrs without a (Django-specific) contribute_to_class()
  18.         # method to type.__new__() so that they're properly initialized
  19.         # (i.e. __set_name__()).
  20.         contributable_attrs = {}
  21.         for obj_name, obj in attrs.items():
  22.             if _has_contribute_to_class(obj):
  23.                 contributable_attrs[obj_name] = obj
  24.             else:
  25.                 new_attrs[obj_name] = obj
  26.         new_class = super_new(cls, name, bases, new_attrs, **kwargs)
  27.         abstract = getattr(attr_meta, "abstract", False)
  28.         meta = attr_meta or getattr(new_class, "Meta", None)
  29.         base_meta = getattr(new_class, "_meta", None)
  30.         app_label = None
  31.         # Look for an application configuration to attach the model to.
  32.         app_config = apps.get_containing_app_config(module)
  33.         if getattr(meta, "app_label", None) is None:
  34.             if app_config is None:
  35.                 if not abstract:
  36.                     raise RuntimeError(
  37.                         "Model class %s.%s doesn't declare an explicit "
  38.                         "app_label and isn't in an application in "
  39.                         "INSTALLED_APPS." % (module, name)
  40.                     )
  41.             else:
  42.                 app_label = app_config.label
  43.         new_class.add_to_class("_meta", Options(meta, app_label))
  44.         if not abstract:
  45.             new_class.add_to_class(
  46.                 "DoesNotExist",
  47.                 subclass_exception(
  48.                     "DoesNotExist",
  49.                     tuple(
  50.                         x.DoesNotExist
  51.                         for x in parents
  52.                         if hasattr(x, "_meta") and not x._meta.abstract
  53.                     )
  54.                     or (ObjectDoesNotExist,),
  55.                     module,
  56.                     attached_to=new_class,
  57.                 ),
  58.             )
  59.             new_class.add_to_class(
  60.                 "MultipleObjectsReturned",
  61.                 subclass_exception(
  62.                     "MultipleObjectsReturned",
  63.                     tuple(
  64.                         x.MultipleObjectsReturned
  65.                         for x in parents
  66.                         if hasattr(x, "_meta") and not x._meta.abstract
  67.                     )
  68.                     or (MultipleObjectsReturned,),
  69.                     module,
  70.                     attached_to=new_class,
  71.                 ),
  72.             )
  73.             if base_meta and not base_meta.abstract:
  74.                 # Non-abstract child classes inherit some attributes from their
  75.                 # non-abstract parent (unless an ABC comes before it in the
  76.                 # method resolution order).
  77.                 if not hasattr(meta, "ordering"):
  78.                     new_class._meta.ordering = base_meta.ordering
  79.                 if not hasattr(meta, "get_latest_by"):
  80.                     new_class._meta.get_latest_by = base_meta.get_latest_by
  81.         is_proxy = new_class._meta.proxy
  82.         # If the model is a proxy, ensure that the base class
  83.         # hasn't been swapped out.
  84.         if is_proxy and base_meta and base_meta.swapped:
  85.             raise TypeError(
  86.                 "%s cannot proxy the swapped model '%s'." % (name, base_meta.swapped)
  87.             )
  88.         # Add remaining attributes (those with a contribute_to_class() method)
  89.         # to the class.
  90.         for obj_name, obj in contributable_attrs.items():
  91.             new_class.add_to_class(obj_name, obj)
  92.         # All the fields of any type declared on this model
  93.         new_fields = chain(
  94.             new_class._meta.local_fields,
  95.             new_class._meta.local_many_to_many,
  96.             new_class._meta.private_fields,
  97.         )
  98.         field_names = {f.name for f in new_fields}
  99.         # Basic setup for proxy models.
  100.         if is_proxy:
  101.             base = None
  102.             for parent in [kls for kls in parents if hasattr(kls, "_meta")]:
  103.                 if parent._meta.abstract:
  104.                     if parent._meta.fields:
  105.                         raise TypeError(
  106.                             "Abstract base class containing model fields not "
  107.                             "permitted for proxy model '%s'." % name
  108.                         )
  109.                     else:
  110.                         continue
  111.                 if base is None:
  112.                     base = parent
  113.                 elif parent._meta.concrete_model is not base._meta.concrete_model:
  114.                     raise TypeError(
  115.                         "Proxy model '%s' has more than one non-abstract model base "
  116.                         "class." % name
  117.                     )
  118.             if base is None:
  119.                 raise TypeError(
  120.                     "Proxy model '%s' has no non-abstract model base class." % name
  121.                 )
  122.             new_class._meta.setup_proxy(base)
  123.             new_class._meta.concrete_model = base._meta.concrete_model
  124.         else:
  125.             new_class._meta.concrete_model = new_class
  126.         # Collect the parent links for multi-table inheritance.
  127.         parent_links = {}
  128.         for base in reversed([new_class] + parents):
  129.             # Conceptually equivalent to `if base is Model`.
  130.             if not hasattr(base, "_meta"):
  131.                 continue
  132.             # Skip concrete parent classes.
  133.             if base != new_class and not base._meta.abstract:
  134.                 continue
  135.             # Locate OneToOneField instances.
  136.             for field in base._meta.local_fields:
  137.                 if isinstance(field, OneToOneField) and field.remote_field.parent_link:
  138.                     related = resolve_relation(new_class, field.remote_field.model)
  139.                     parent_links[make_model_tuple(related)] = field
  140.         # Track fields inherited from base models.
  141.         inherited_attributes = set()
  142.         # Do the appropriate setup for any model parents.
  143.         for base in new_class.mro():
  144.             if base not in parents or not hasattr(base, "_meta"):
  145.                 # Things without _meta aren't functional models, so they're
  146.                 # uninteresting parents.
  147.                 inherited_attributes.update(base.__dict__)
  148.                 continue
  149.             parent_fields = base._meta.local_fields + base._meta.local_many_to_many
  150.             if not base._meta.abstract:
  151.                 # Check for clashes between locally declared fields and those
  152.                 # on the base classes.
  153.                 for field in parent_fields:
  154.                     if field.name in field_names:
  155.                         raise FieldError(
  156.                             "Local field %r in class %r clashes with field of "
  157.                             "the same name from base class %r."
  158.                             % (
  159.                                 field.name,
  160.                                 name,
  161.                                 base.__name__,
  162.                             )
  163.                         )
  164.                     else:
  165.                         inherited_attributes.add(field.name)
  166.                 # Concrete classes...
  167.                 base = base._meta.concrete_model
  168.                 base_key = make_model_tuple(base)
  169.                 if base_key in parent_links:
  170.                     field = parent_links[base_key]
  171.                 elif not is_proxy:
  172.                     attr_name = "%s_ptr" % base._meta.model_name
  173.                     field = OneToOneField(
  174.                         base,
  175.                         on_delete=CASCADE,
  176.                         name=attr_name,
  177.                         auto_created=True,
  178.                         parent_link=True,
  179.                     )
  180.                     if attr_name in field_names:
  181.                         raise FieldError(
  182.                             "Auto-generated field '%s' in class %r for "
  183.                             "parent_link to base class %r clashes with "
  184.                             "declared field of the same name."
  185.                             % (
  186.                                 attr_name,
  187.                                 name,
  188.                                 base.__name__,
  189.                             )
  190.                         )
  191.                     # Only add the ptr field if it's not already present;
  192.                     # e.g. migrations will already have it specified
  193.                     if not hasattr(new_class, attr_name):
  194.                         new_class.add_to_class(attr_name, field)
  195.                 else:
  196.                     field = None
  197.                 new_class._meta.parents[base] = field
  198.             else:
  199.                 base_parents = base._meta.parents.copy()
  200.                 # Add fields from abstract base class if it wasn't overridden.
  201.                 for field in parent_fields:
  202.                     if (
  203.                         field.name not in field_names
  204.                         and field.name not in new_class.__dict__
  205.                         and field.name not in inherited_attributes
  206.                     ):
  207.                         new_field = copy.deepcopy(field)
  208.                         new_class.add_to_class(field.name, new_field)
  209.                         # Replace parent links defined on this base by the new
  210.                         # field. It will be appropriately resolved if required.
  211.                         if field.one_to_one:
  212.                             for parent, parent_link in base_parents.items():
  213.                                 if field == parent_link:
  214.                                     base_parents[parent] = new_field
  215.                 # Pass any non-abstract parent classes onto child.
  216.                 new_class._meta.parents.update(base_parents)
  217.             # Inherit private fields (like GenericForeignKey) from the parent
  218.             # class
  219.             for field in base._meta.private_fields:
  220.                 if field.name in field_names:
  221.                     if not base._meta.abstract:
  222.                         raise FieldError(
  223.                             "Local field %r in class %r clashes with field of "
  224.                             "the same name from base class %r."
  225.                             % (
  226.                                 field.name,
  227.                                 name,
  228.                                 base.__name__,
  229.                             )
  230.                         )
  231.                 else:
  232.                     field = copy.deepcopy(field)
  233.                     if not base._meta.abstract:
  234.                         field.mti_inherited = True
  235.                     new_class.add_to_class(field.name, field)
  236.         # Copy indexes so that index names are unique when models extend an
  237.         # abstract model.
  238.         new_class._meta.indexes = [
  239.             copy.deepcopy(idx) for idx in new_class._meta.indexes
  240.         ]
  241.         if abstract:
  242.             # Abstract base models can't be instantiated and don't appear in
  243.             # the list of models for an app. We do the final setup for them a
  244.             # little differently from normal models.
  245.             attr_meta.abstract = False
  246.             new_class.Meta = attr_meta
  247.             return new_class
  248.         new_class._prepare()
  249.         new_class._meta.apps.register_model(new_class._meta.app_label, new_class)
  250.         return new_class
  251.       
  252.     def _prepare(cls):
  253.         """Create some methods once self._meta has been populated."""
  254.         opts = cls._meta
  255.         opts._prepare(cls)
  256.         if opts.order_with_respect_to:
  257.             cls.get_next_in_order = partialmethod(
  258.                 cls._get_next_or_previous_in_order, is_next=True
  259.             )
  260.             cls.get_previous_in_order = partialmethod(
  261.                 cls._get_next_or_previous_in_order, is_next=False
  262.             )
  263.             # Defer creating accessors on the foreign class until it has been
  264.             # created and registered. If remote_field is None, we're ordering
  265.             # with respect to a GenericForeignKey and don't know what the
  266.             # foreign class is - we'll add those accessors later in
  267.             # contribute_to_class().
  268.             if opts.order_with_respect_to.remote_field:
  269.                 wrt = opts.order_with_respect_to
  270.                 remote = wrt.remote_field.model
  271.                 lazy_related_operation(make_foreign_order_accessors, cls, remote)
  272.         # Give the class a docstring -- its definition.
  273.         if cls.__doc__ is None:
  274.             cls.__doc__ = "%s(%s)" % (
  275.                 cls.__name__,
  276.                 ", ".join(f.name for f in opts.fields),
  277.             )
  278.         get_absolute_url_override = settings.ABSOLUTE_URL_OVERRIDES.get(
  279.             opts.label_lower
  280.         )
  281.         if get_absolute_url_override:
  282.             setattr(cls, "get_absolute_url", get_absolute_url_override)
  283.         if not opts.managers:
  284.             if any(f.name == "objects" for f in opts.fields):
  285.                 raise ValueError(
  286.                     "Model %s must specify a custom Manager, because it has a "
  287.                     "field named 'objects'." % cls.__name__
  288.                 )
  289.             manager = Manager()
  290.             manager.auto_created = True
  291.             cls.add_to_class("objects", manager)
  292.         # Set the name of _meta.indexes. This can't be done in
  293.         # Options.contribute_to_class() because fields haven't been added to
  294.         # the model at that point.
  295.         for index in cls._meta.indexes:
  296.             if not index.name:
  297.                 index.set_name_with_model(cls)
  298.         class_prepared.send(sender=cls)
复制代码
可以看到在_prepare这个方法里面判断了一下有没有managers。如果没有的话就会去创建一个Manager类。并设置给当前对象的objects类。我们去看一下Managr类
  1. class Manager(BaseManager.from_queryset(QuerySet)):
  2.       def get_queryset(self):
  3.         """
  4.         Return a new QuerySet object. Subclasses can override this method to
  5.         customize the behavior of the Manager.
  6.         """
  7.         return self._queryset_class(model=self.model, using=self._db, hints=self._hints)
  8.     def all(self):
  9.         # We can't proxy this method through the `QuerySet` like we do for the
  10.         # rest of the `QuerySet` methods. This is because `QuerySet.all()`
  11.         # works by creating a "copy" of the current queryset and in making said
  12.         # copy, all the cached `prefetch_related` lookups are lost. See the
  13.         # implementation of `RelatedManager.get_queryset()` for a better
  14.         # understanding of how this comes into play.
  15.         return self.get_queryset()
复制代码
可以看到这里是继承了BaseManager.from_queryset(QuerySet)。那继续看BaseManager.from_queryset(QuerySet)方法
  1.   class BaseManager:  
  2.           @classmethod
  3.     def from_queryset(cls, queryset_class, class_name=None):
  4.         if class_name is None:
  5.             class_name = "%sFrom%s" % (cls.__name__, queryset_class.__name__)
  6.         return type(
  7.             class_name,
  8.             (cls,),
  9.             {
  10.                 "_queryset_class": queryset_class,
  11.                 **cls._get_queryset_methods(queryset_class),
  12.             },
  13.         )
复制代码
可以看到是使用type创建了类,然后继承了当前类,最后定义了一个_queryset_class方法。并把传入的queryset_class(QuerySet)设置成了_queryset_class属性的值。
  1. class QuerySet(AltersData):
  2.     """Represent a lazy database lookup for a set of objects."""
  3.     def __init__(self, model=None, query=None, using=None, hints=None):
  4.         self.model = model
  5.         self._db = using
  6.         self._hints = hints or {}
  7.         self._query = query or sql.Query(self.model)
  8.         self._result_cache = None
  9.         self._sticky_filter = False
  10.         self._for_write = False
  11.         self._prefetch_related_lookups = ()
  12.         self._prefetch_done = False
  13.         self._known_related_objects = {}  # {rel_field: {pk: rel_obj}}
  14.         self._iterable_class = ModelIterable
  15.         self._fields = None
  16.         self._defer_next_filter = False
  17.         self._deferred_filter = None
  18.         
  19.       def __iter__(self):
  20.         """
  21.         The queryset iterator protocol uses three nested iterators in the
  22.         default case:
  23.             1. sql.compiler.execute_sql()
  24.                - Returns 100 rows at time (constants.GET_ITERATOR_CHUNK_SIZE)
  25.                  using cursor.fetchmany(). This part is responsible for
  26.                  doing some column masking, and returning the rows in chunks.
  27.             2. sql.compiler.results_iter()
  28.                - Returns one row at time. At this point the rows are still just
  29.                  tuples. In some cases the return values are converted to
  30.                  Python values at this location.
  31.             3. self.iterator()
  32.                - Responsible for turning the rows into model objects.
  33.         """
  34.         self._fetch_all()
  35.         return iter(self._result_cache)
  36.       
  37.       
  38.     def _fetch_all(self):
  39.         if self._result_cache is None:
  40.             self._result_cache = list(self._iterable_class(self))
  41.         if self._prefetch_related_lookups and not self._prefetch_done:
  42.             self._prefetch_related_objects()
复制代码
开始查询
  1. Demo1.objects.all()
  2. [test_model for test_model in demos]
复制代码
看一下这几行代码发生了什么。
DemoModes.objects 返回的就是上面的Manager。然后调用了Manager的all。然后在all里面返回了了_queryset_class。由上面我们可以知道queryset_class是传入的QuerySet。
查询生命周期
flowchart TDmodel[Demo] --> objects[objects] -- Manager --> all --> get_queryset[返回了QuerySet]这里现在返回了QuerySet,但是实际上没有和数据库交互呢。真正和数据库交互是在[test_model for test_model in demos]这里。
当开始遍历demos的时候会触发__iter__方法,然后在这个魔法函数里面会触发__fetch_all【和数据交互】方法,并返回一个一个迭代对象
查询生命周期
flowchart TDfor[循环] --> iter[_\_iter\_\_] --> _fetch_all --数据库交互--> 返回结果
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册