缓存使用django orm构建的sql查询

django-prepared-queries的Python项目详细描述


Django准备的查询

PyPI versioncodecovBuild Status

django_pq允许缓存使用django orm生成的sql并重用缓存的sql 只替换新参数值的查询。

简短示例

一些开发人员认为django orm很慢。如果你的代码看起来 像这样:

fromdjango.dbimportmodelsfromcountries_field.fieldsimportcountries_isnull,countries_containsdeffilter_queryset(self,domains=None,**kwargs):query=models.Q()ifdomains:query&=((models.Q(allow_domains__name__in=domains)|models.Q(allow_domains__isnull=True))&(~models.Q(deny_domains__name__in=domains)|models.Q(deny_domains__isnull=True)))else:query&=(models.Q(allow_domains__isnull=True)&models.Q(deny_domains__isnull=True))user_agent=kwargs.pop('user_agent',None)ifuser_agent:query&=(models.Q(user_agents=user_agent)|models.Q(user_agents__isnull=True))else:query&=models.Q(user_agents__isnull=True)country=kwargs.pop('country')ifcountry:query&=countries_isnull()|countries_contains([country])else:query&=countries_isnull()returnself.get_queryset().filter(query)

生成的sql查询非常长,在我们的例子中,它占到http的50% 请求处理。如果我们可以缓存生成的sql并替换 实际参数值而不是重复重查询集筛选?

好吧,用django_pq你可以做以下的事情。

fromdjango.dbimportmodelsimportdjango_pq# Add caching decorator for heavy queryset constructing method@django_pq.substitute_lazy()deffilter_queryset_lazy(self,domains=None,**kwargs):query=models.Q()# branches in decorated function must check real value instead of Lazy # wrapper, because actual value this time could be False.ifdjango_pq.reveal(domains):# You pass Lazy wrappers in to any lookup parameters for queryset,# and these Lazy wrappers remain lazy until it's time to query the # database.query&=((models.Q(allow_domains__name__in=domains)|models.Q(allow_domains__isnull=True))&(~models.Q(deny_domains__name__in=domains)|models.Q(deny_domains__isnull=True)))else:query&=(models.Q(allow_domains__isnull=True)&models.Q(deny_domains__isnull=True))# ... # # modify other parts of queryset constuction with respect of lazy nature of# arguments.returnself.get_queryset().filter(query)deffilter_queryset(self,**kwargs):# wrap parameters into context manager so Lazy wrappers could get actual# values when they need.withdjango_pq.LazyContext(**kwargs):queryset=self.filter_queryset_lazy(**kwargs)# queryset is now RawQuerySet with Lazy wrappers in params.# database queries should be performed within LazyContext.returnqueryset.first()

就这样-您的queryset生成代码被缓存。

准备缓存代码的规则

  1. 不要检查lazy wrappers的任何内容-使用reveal()检查实际的 参数值。即Lazy(None) is not None总是正确的(这不是 你真正的意思)。
  2. 不要将模型实例作为参数传递。这允许模型实例方法 调用并可能导致无法从实际中检测到的隐式分支 参数列表。而是传递主键值。
  3. 不要在缓存的方法中查询数据库-无法检测到分支。
  4. 将所有if表达式作为新参数添加到方法中 使用适当的缓存。
  5. 不要将空列表作为参数值传递。django orm检查 空并从wherenode删除空查找(关于布尔值 代数规则)。改为传递None
  6. 不要在queryset过滤中使用任何易失性值,如datetime.now(); 将其作为参数传递。
  7. 在添加缓存之前,使用100%分支覆盖率测试代码。

规范化参数

帮助您规范化传入缓存函数的参数LazyContext 当 正在输入上下文。

fromdjango.dbimportmodelsfromdjango_pqimportLazyContextdefmodel_to_pk(kwargs):fork,vinlist(kwargs.items()):ifisinstance(v,models.Model):kwargs[k]=v.pk# Model -> Model.pkreturnkwargsdefempty_list_to_none(kwargs):fork,vinlist(kwargs.items()):ifisinstance(v,list)andnotv:kwargs[k]=None# [] -> Nonereturnkwargsdeffilter_queryset(self,**kwargs):withLazyContext(model_to_pk,empty_list_to_none,**kwargs)aslazy_kwargs:queryset=self.filter_queryset_lazy(**lazy_kwargs)returnqueryset.first()

工作原理

  1. 首先,substitute_lazy()decorator用惰性包装器包装所有参数, 在sql生成完成之前,with wrapper保持“惰性”。
  2. 您的代码被调用了两次,使用惰性包装作为参数,并使用实际的 值,以确保惰性结果与本机QuerySet相同。
  3. 如果sql和规范化参数匹配,则缓存一个RawQuerySet实例 以惰性包装器作为参数。
  4. 缓存键尊重任何参数和某些常量的存在,如 True, False, 0, 1, None
  5. 在“缓存命中”情况下,新的实际参数值将从 懒散的上下文进入RawQuerySet.params这是缓存的结果。
  6. 如果你做得对,RawQuerySet会表现得很正常 QuerySet,或者(更正确地)作为模型实例迭代器。

欢迎加入QQ群-->: 979659372 Python中文网_新手群

推荐PyPI第三方库


热门话题
java未从Facebook OAuth接收名字、姓氏和姓氏   我自己代码中的java ConcurrentModificationException   java在Android上获得单像素TIFF图像   java图形输出中的swing集成弹出窗口“通知”   将数据库表中的所有数据集插入jTable Java   java如何使用Hibernate获取不完整的集合?   打印无法从java连接到打印机   java使用分隔符拆分带引号的字符串   java Axis2禁用严格验证(wsdl2java中的Eosv)允许什么,如何使其更加严格?   java有人能帮我处理循环代码吗   java将JsonObject数组转换为整数   JavaXWiki扩展:检测事件空间创建   java如何设置图表的文本方向以使用poi旋转所有文本?   eche RecyclerView项的java Set自定义字体   java单元测试带有私有构造函数的spring组件,无需注入   用户界面如何在java中动态显示一组多个元素?   如何从java应用程序调用IDL(交互式数据语言)?   Java的内置库实现