缓存使用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使用ApachePOI将excel文件导入postgreSQL表   java多线程从iText pdf提取文本   winapi Java和SetWindowDisplayAffinity   eclipse juno的java Websphere 6.1插件   java MPAndroidChart:为Y轴提供一些偏移   java中作为参数传递枚举类型的继承   java Gui jframe的工作原理与netbeans不同   使用Bouncy Castle和PDFBox在Java中验证PDF签名   优化缩小Java代码   java无法在安卓中从Firebase取回子数据   返回的java方法?我应该什么时候用?   java错误处理已完成,退出代码为1。与穿过阵列的for循环有关   多线程Java volatile是否阻止缓存或强制执行写缓存?   java Multi-collectItems如何提前终止并返回已收集的项目   java为什么不在服务(请求,响应)中直接调用processRequest(请求,响应)?   java如何从字符串生成int数组?   打印获取用户输入的值并在其他预选文本中显示。JAVA   未显示java DynamicAsper UTF8字符   java Eclipse RCP:不启动应用程序的命令行参数