🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
### QuerySet源码阅读 目前Django2.0的QuerySet源代码是保存在**django.db.models**的**query**目录下<br> QuerySet本身的设计没有基于任何Django的其他类,所以代码较好分析,不需要上溯找其他类的代码 <br/> #### __init__函数 ~~~ class QuerySet(object): """ Represents a lazy database lookup for a set of objects. """ def __init__(self, model=None, query=None, using=None, hints=None): self.model = model self._db = using self._hints = hints or {} self.query = query or sql.Query(self.model) self._result_cache = None self._sticky_filter = False self._for_write = False self._prefetch_related_lookups = [] self._prefetch_done = False self._known_related_objects = {} # {rel_field, {pk: rel_obj}} self._iterable_class = ModelIterable self._fields = None ~~~ 构造函数,这里的self.query是一个Django的Query类的实例,里面保存了添加到这个QuerySet中的SQL条件,Query类本身也是一个比较复杂的类,需要另外阅读,QuerySet是从数据库拿信息还是从缓存拿就取决于self._result_cache这个值了,如果当前QuerySet从数据库取过值,结果一定保存在这个变量里面,其他变量暂时还么有研究 <br> #### __deepcopy__函数 ~~~ def __deepcopy__(self, memo): """ Deep copy of a QuerySet doesn't populate the cache """ obj = self.__class__() for k, v in self.__dict__.items(): if k == '_result_cache': obj.__dict__[k] = None else: obj.__dict__[k] = copy.deepcopy(v, memo) return obj ~~~ <br> Python的深拷贝魔法函数,这里的obj就是创建一个新的QuerySet实例,再利用dict属性字典逐个将当前QuerySet的值赋给新的QuerySet实例,同时这里对_result_cache不赋值,这也属于容易理解的设计,另外这里指的一提的是memo这个参数,Stack Overflow里面对这个参数的作用解释是它是一个系统用来管理各变量的地址与内容映射的字典,memo里面的key就是当前作用域里变量的id值 <br> #### iterator函数 ~~~ def iterator(self): """ An iterator over the results from applying this QuerySet to the database. """ return iter(self._iterable_class(self)) ~~~ 此函数会调用一个叫ModelIterable的类将QuerySet实例本身迭代化,同时因为ModelIterable里面实现了__iter__魔法函数,同时里面的逻辑对数据库进行了操作,所以iterator函数就是一个会从数据库中取数据的函数了 <br> #### count函数 ~~~ def count(self): """ Performs a SELECT COUNT() and returns the number of records as an integer. If the QuerySet is already fully cached this simply returns the length of the cached results set to avoid multiple SELECT COUNT(*) calls. """ if self._result_cache is not None: return len(self._result_cache) return self.query.get_count(using=self.db) ~~~ 还是检查_result_cache,如果有数据就直接返回长度了,如果没有则对数据库进行一次查询,然而这里如果进行了数据库查询 <br> #### get函数 get函数是常用的QuerySet函数之一,此函数严格规定了最后SQL查询的结果只能返回一个实例,多了少了都会有错 ~~~ def get(self, *args, **kwargs): """ Performs the query and returns a single object matching the given keyword arguments. """ clone = self.filter(*args, **kwargs) if self.query.can_filter() and not self.query.distinct_fields: clone = clone.order_by() num = len(clone) if num == 1: return clone._result_cache[0] if not num: raise self.model.DoesNotExist( "%s matching query does not exist." % self.model._meta.object_name ) raise self.model.MultipleObjectsReturned( "get() returned more than one %s -- it returned %s!" % (self.model._meta.object_name, num) ) ~~~ 从这个函数也很容易看出来num等于0的时候抛出一个DoesNotExist异常,而num大于1时抛出一个MultipleObjectsReturned异常,只有长度为1时才能正常返回结果,而这里的结果也是通过clone也就是一个新的QuerySet中的_result_cache返回的,另外这里还可以明显看到get函数是对另一个更加常用的filter函数有调用的 <br> #### create函数 此函数按照说明是一个直接创建实例到数据库而不需要走常规的model类的save函数的方法 ~~~ def create(self, **kwargs): """ Creates a new object with the given kwargs, saving it to the database and returning the created object. """ obj = self.model(**kwargs) self._for_write = True obj.save(force_insert=True, using=self.db) return obj ~~~ 代码很简单,就是根据此QuerySet实例所属的模型类新建一个模型实例,同时改变了一个私有变量_for_write的值(暂时还不知道这行代码的作用),然后再调用模型的save方法存入数据库,同时将这个新建的实例返回 <br> #### first, last函数 ~~~ def first(self): """ Returns the first object of a query, returns None if no match is found. """ objects = list((self if self.ordered else self.order_by('pk'))[:1]) if objects: return objects[0] return None ~~~ 这个也相对简单,从这里就可以看到first首先将QuerySet实例本身转化成了数组然后只取了第一个元素,同时如果实例本身已经排序过了则直接返回,否则还要用主键排序过后返回第一个数据 <br> ~~~ def last(self): """ Returns the last object of a query, returns None if no match is found. """ objects = list((self.reverse() if self.ordered else self.order_by('-pk'))[:1]) if objects: return objects[0] return None ~~~ last函数几乎和first一模一样,唯一区别是反向了,同时在实例为已排序的情况下会去调用实例的reverse方法反向 #### filter, exclude函数 <br> ~~~ def filter(self, *args, **kwargs): """ Returns a new QuerySet instance with the args ANDed to the existing set. """ return self._filter_or_exclude(False, *args, **kwargs) def exclude(self, *args, **kwargs): """ Returns a new QuerySet instance with NOT (args) ANDed to the existing set. """ return self._filter_or_exclude(True, *args, **kwargs) ~~~ <br> 这两个函数我们可以明显看出来就是一胎双生,它们不过是对私函数_filter_or_exclude函数的不同参数的调用罢了 <br> ~~~ def _filter_or_exclude(self, negate, *args, **kwargs): if args or kwargs: assert self.query.can_filter(), \ "Cannot filter a query once a slice has been taken." clone = self._clone() if negate: clone.query.add_q(~Q(*args, **kwargs)) else: clone.query.add_q(Q(*args, **kwargs)) return clone ~~~ 这里涉及三个函数,逐行分析代码,首先negate参数应该就是是要按传入参数找符合条件的还是找不符合条件的意思,True找不符合传入条件的,False找符合的 <br> ~~~ if args or kwargs: assert self.query.can_filter(), \ "Cannot filter a query once a slice has been taken." ~~~ 这里判断的情况很明显,只要当前这个QuerySet实例在调用filter或exclude函数时引入了参数,那就必须保证当前QuerySet中的Query实例属性是可以filter的,什么是可以filter呢?抽象点说就是当前的self.query没有被切片过,具体来说就是self.query的low_mark和high_mark属性一个为0,一个是None,这里把Query类的can_filter方法放出来: <br> ~~~ def can_filter(self): """ Returns True if adding filters to this instance is still possible. Typically, this means no limits or offsets have been put on the results. """ return not self.low_mark and self.high_mark is None ~~~ 这里逻辑简明易懂,不用说了,就是刚才上面说的意思 <br> 继续后面的逻辑部分 ~~~ clone = self._clone() ~~~ 这里的_clone方法实际作用就是根据QuerySet的原则复制出一个和当前QuerySet实例一模一样的QuerySet对象出来 <br> ~~~ def _clone(self, **kwargs): query = self.query.clone() if self._sticky_filter: query.filter_is_sticky = True clone = self.__class__(model=self.model, query=query, using=self._db, hints=self._hints) clone._for_write = self._for_write clone._prefetch_related_lookups = self._prefetch_related_lookups[:] clone._known_related_objects = self._known_related_objects clone._iterable_class = self._iterable_class clone._fields = self._fields clone.__dict__.update(kwargs) return clone ~~~ 此函数设计较多调用,暂时比较难全部去分析一遍,关键点在于这里把当前QuerySet的query属性复制了一份 <br> ~~~ if negate: clone.query.add_q(~Q(*args, **kwargs)) else: clone.query.add_q(Q(*args, **kwargs)) return clone ~~~ 最后这几行,你如果只看函数的字面意思,也是很再简单不过,对复制出来的新QuerySet对象调用用户对QuerySet传入的查询参数并利用Q来构造,从这里也可以看到实质上我们在最外层利用某个指定参数param=value这样的结构来查询时,**Django最终还是把它们变成了Q对象来进行查询的** 对于filter函数的探究就到此止步,因为add_q函数和_clone函数都属于牵连甚广的那种函数,一时半会难以看完,从这里其实也可以看出,QuerySet在Django的数据结构中并不是属于最原始的基石一类的设计,它只是在其他更加基础的类(如Query类)的基础上建立起来的一种供外层调用的类罢了