跳转到主要内容

django的高级模板缓存标签:版本控制、压缩、部分缓存、易于继承

项目描述

PyPI Version Build Status on Travis CI

django-adv-cache-tag

Django高级缓存模板标签

  • 版本控制

  • 压缩

  • 部分缓存

  • 易于扩展/自定义

有关http://documentup.com/twidi/django-adv-cache-tag上的可读文档

简介

首先,请注意,{% cache %}模板标签的参数与django中包含的默认缓存模板标签的参数相同,因此使用这个新标签非常简单。

使用django-adv-cache-tag,您可以

  • 将版本号(整数、字符串、日期等,将转换为字符串)添加到您的模板标签中:版本号将与缓存中的版本进行比较,并使用相同的缓存键为新的缓存模板,从而避免在缓存中保留旧的未使用键,允许您永久缓存。

  • 无需担心我们的算法不兼容的更新,因为我们还使用一个内部版本号,只有在内部算法更改时才会更新

  • 定义您自己的缓存键(或者简单地说,只需将主键(或您想要的,这是一个模板标签参数)添加到该缓存键中

  • 压缩要缓存的 数据,以减少缓存后端内存消耗和网络延迟(但压缩/解压缩将消耗更多时间和CPU,由您选择)

  • 选择要使用的缓存后端

  • 在您的缓存模板中定义 {% nocache %}...{% endnocache %} 块,这些块仅在请求时才渲染(对于这些部分,模板的内容被缓存,而不是渲染结果)

  • 轻松定义您自己的算法,因为我们提供了一个您可以从其继承的单个类(具有简短的方法),您可以简单地更改选项或任何行为,并为它们定义自己的标签

  • 使用变量来定义您的缓存片段名称

安装

django-adv-cache-tag 可在 PyPI 上找到

pip install django-adv-cache-tag

从版本 1.0 开始,我们仅支持 Python 3。

如果您从版本 < 1 升级,请注意内部版本号已更改,因此所有缓存都将重置。

如果您需要 Python 2 支持,您必须通过传递版本进行安装

pip install 'django-adv-cache-tag<1.0'

或者您可以在 Github 上找到它: https://github.com/twidi/django-adv-cache-tag

(对于 Python 2 版本: https://github.com/twidi/django-adv-cache-tag/tree/python2)

安装后,只需将 adv_cache_tag 添加到您的 django 项目的 settings.py 文件中的 INSTALLED_APPS 中。

请参阅下一节中的示例以了解其工作方式(基本上与默认 django 缓存模板标签相同)

特性

版本控制

描述

使用默认的 django 缓存模板标签,您可以添加任意数量的参数,包括版本号或日期,然后如果此版本更改,缓存键将更改。因此,您的缓存按预期更新。

但是,旧键不会被删除,如果您有较长的过期时间,它将保留很长时间,消耗您宝贵的内存。

django-adv-cache-tag 提供了一种避免这种情况的方法,同时仍然在需要时再生生成缓存。为此,当激活时,我们使用传递给您的模板标签的最后参数作为“版本号”,并将其从用于生成缓存键的参数中删除。

此版本将用于缓存模板的 内容 中,而不是 ,当缓存存在并加载时,缓存版本将与所需的版本进行比较,如果两者匹配,则缓存有效并返回,否则将重新生成。

因此,如果您喜欢给定模板、给定对象/用户或什么的唯一键的原则,请确保始终使用相同的参数,除了最后一个,并激活 ADV_CACHE_VERSIONING 设置。

请注意,我们还管理一个内部版本号,该版本号将始终与缓存的版本号进行比较。这个内部版本号仅在django-adv-cache-tag的内部算法更改时更新。但您可以通过在设置中添加一个ADV_CACHE_VERSION来更新它,以使所有缓存的模板失效(我们的内部版本和此设置中的值将连接起来以获得实际使用的内部版本)

设置

ADV_CACHE_VERSIONING,默认为False

ADV_CACHE_VERSION,默认为""

示例

在以下模板中,如果ADV_CACHE_VERSIONING设置为True,键将始终相同,基于字符串“myobj_main_template”和obj.pk的值,但每当obj.date_last_updated更改时,缓存的值都将重新生成。

因此,我们设置expire_time0,以始终保留缓存的模板,因为我们现在不会有许多副本(旧的和当前的)。

没有过期时间的值可能取决于您的缓存后端(它不总是0)。

{% load adv_cache %}
{% cache 0 myobj_main_template obj.pk obj.date_last_updated %}
  {{ obj }}
{% endcache %}

主键

描述

在默认的django缓存templatetag中,缓存键是这样的

:1:template.cache.your_fragment_name.64223ccf70bbb65a3a4aceac37e21016

您可能希望有更明确的缓存键,因此通过django-adv-cache-tag,您可以在片段名称和哈希之间添加一个“主键”

:1:template.cache.your_fragment_name.your_pk.64223ccf70bbb65a3a4aceac37e21016

虽然这个主键的主要用途是使每个对象有一个缓存的片段,因此我们可以使用对象的键,但您可以使用任何您想要的,比如id、字符串……

要添加主键,只需将ADV_CACHE_INCLUDE_PK设置设置为True,并且第一个参数(在片段名称之后)将被用作主键。

如果您只想为部分缓存templatetags使用它,请稍后阅读本readme中的扩展默认缓存标签部分(它很容易,真的)。

与版本不同,主键将作为生成缓存键哈希的参数保持不变。

设置

ADV_CACHE_INCLUDE_PK,默认为False

示例

django-adv-cache-tag的一个常见用途是只使用主键和版本

{% cache 0 myobj_main_template obj.pk obj.date_last_updated %}

压缩

描述

默认的django缓存templatetag只是将生成的html保存到缓存中。根据您的模板,可能会有很多html,您的缓存内存会迅速增长。更不用说由于模板中的缩进(两种我知道在不使用django-adv-cache-tag的情况下移除它们的方法:django提供的{% spaceless %} templatetag和django-template-preprocessor)会有很多空格。

django-adv-cache-tag可以为您做这些。它能够通过替换为一个简单的空格(以保持html中的空格行为)来删除重复的空格(包括换行符和制表符),并通过zlib(和pickle)模块压缩要缓存的html。

当然,这会消耗一些时间和CPU周期,但您可以在您的缓存后端中节省很多内存,并且可以节省很多带宽,尤其是如果您的后端在远处。我还没有为此做过测试,但对于某些模板,保存的数据可以从2 ko减少到不到一。

要激活这些功能,只需将以下定义的设置之一或两个设置为True

警告:如果使用的缓存后端使用pickle及其默认协议,压缩是无效的,因为二进制文件处理不佳,最终存储在缓存中的大小将远大于压缩后的大小。所以在激活此选项之前请检查这一点。对于默认的django后端(至少在1.4版本中)是可行的,但对于django-redis-cache则不行,等待我的pull-request,但你也可以检查我的版本:https://github.com/twidi/django-redis-cache/tree/pickle_version

设置

ADV_CACHE_COMPRESS,默认为False,通过zlib来激活压缩

ADV_CACHE_COMPRESS_LEVEL,默认为-1,设置zlib的压缩级别(实际上默认是zlib.Z_DEFAULT_COMPRESSION,即-1,实际上为6,这是zlib中实际定义的默认值)

ADV_CACHE_COMPRESS_SPACES,默认为False,激活减少空白字符的功能

示例

没有示例,因为你不需要更改你的templatetag调用,只需设置设置即可使用此功能

选择您的缓存后端

描述

在django中,您可以定义多个缓存后端。但是,使用默认的缓存templatetag,您不能指定使用哪一个,它将自动使用默认后端。

django-adv-cache-tag可以通过提供一个设置,即ADV_CACHE_BACKEND来实现这一点,该设置将使用您在设置中定义的缓存后端名称。通过扩展提供的CacheTag对象,您甚至可以定义许多后端供多个templatetags使用,例如一个用于高度访问的模板,一个用于其他模板……按照您的需求。阅读“扩展默认缓存标签”部分以了解更多信息(实际上很简单,但我已经告诉过您了……)

设置

ADV_CACHE_BACKEND,默认为“default”

示例

没有示例,因为和压缩一样,您不需要更改templatetag来使用此功能,只需设置设置即可

部分缓存

使用默认的django缓存templatetag,您的模板被缓存,您在显示之前不能更新它们,因此您不能将大块的HTML与其中包含的小动态片段一起缓存,例如用户名、当前日期等。您可以作弊并保存围绕您的动态部分的两个模板,但您将更多地访问您的缓存后端。

django-adv-cache-tag允许在{% cache %}块中使用一个或多个{% nocache %}块(通过{% endnocache %}关闭)。这些{% nocache %}块将被“原样”保存在缓存中,而其余的块将被渲染为HTML。只有当模板最终显示时,无缓存的部分才会被渲染。

您可以拥有任意数量的这些块。

设置

此功能没有设置,它将自动激活。

示例
{% cache 0 myobj_main_template obj.pk obj.date_last_updated %}
    <p>This is the cached part of the template for {{ obj }}, evaluated at {% now "r" %}.</p>
    {% nocache %}
        <p>This part will be evaluated each time : {% now "r" %}</p>
    {% endnocache %}
    <p>This is another cached part</p>
{% endcache %}

片段名称

描述

片段名称是创建缓存键时使用的名称,它紧接在过期时间之后定义。

Django文档中说明名称将按原样取用,不要使用变量

django-adv-cache-tag中,通过将ADV_CACHE_RESOLVE_NAME设置为True,未引用的片段名称将被解析为上下文中应该存在的变量。

设置

ADV_CACHE_RESOLVE_NAME,默认为 False

示例

ADV_CACHE_RESOLVE_NAME 设置为 True,如果您在上下文中有一个名为 fragment_name 的变量,您就可以这样做

{% cache 0 fragment_name obj.pk obj.date_last_updated %}

如果您想传递一个名称,您必须用引号将其括起来

{% cache 0 "myobj_main_template" obj.pk obj.date_last_updated %}

ADV_CACHE_RESOLVE_NAME 设置为 False(默认值),名称总是被视为字符串,但如果用引号括起来,则会被移除。

在下面的示例中,您可以看到双引号,但也可以用单引号,甚至没有引号

{% cache 0 "myobj_main_template" obj.pk obj.date_last_updated %}

扩展默认缓存标签

如果您在前面几节中解释的五个设置不足以满足您的要求,或者您想要一个与默认提供的不同的行为的模板标签,您一定会很高兴知道 django-adv-cache-tag 是为了容易扩展而编写的。

它提供了一个名为 CacheTag(位于 adv_cache_tag.tag)的类,该类有许多简短简单的方法,还有一个名为 Meta 的类(灵感来源于 django 模型 :D)。因此,覆盖简单部分非常容易。

Meta 类中定义的所有选项都可以通过 self.options.some_field 在类中访问

下面我们将展示许多扩展此类的方法。

基本覆盖

想象一下,您不想更改默认设置(所有设置为 False,并使用默认后端),但想要一个具有版本控制激活的模板标签

创建一个名为 myapp/templatetags/my_cache_tags.py 的新模板标签文件,内容如下

from adv_cache_tag.tag import CacheTag

class VersionedCacheTag(CacheTag):
    class Meta(CacheTag.Meta):
        versioning = True

from django import template
register = template.Library()

VersionedCacheTag.register(register, 'ver_cache')

有了这几行简单的代码,现在您就有了一个新的模板标签,在您想要版本控制时可以使用

{% load my_cache_tags %}
{% ver_cache 0 myobj_main_template obj.pk obj.date_last_updated %}
    obj
{% endver_cache %}

如您所见,只需将 {% load adv_cache %}(或 django 默认的 {% load cache %})替换为 {% load my_cache_tags %}(您的模板标签模块),并将 {% cache %} 模板标签替换为您自己定义的新标签 {% ver_cache ... %}。别忘了替换结束标签: {% endver_cache %}。但是 {% nocache %} 将保持不变,除非您想要一个新的。为此,只需向 register 方法添加一个参数即可

MyCacheTag.register(register, 'ver_cache', 'ver_nocache')
{% ver_cache ... %}
    cached
    {% ver_nocache %}not cached{% endver_nocache %}
{% endver_cache %}

请注意,如果您知道您的模板中不会加载另一个提供 cache 标签的模板标签模块,您可以保留标签的名称为 cache。为此,最简单的方法是

MyCacheTag.register(register)  # 'cache' and 'nocache' are the default values

django-adv-cache-tag 的所有设置在 Meta 类中都有一个对应的变量,因此您可以在自己的类中覆盖一个或多个。请参阅“设置”部分了解它们。

内部版本

当您的模板文件更新时,使所有缓存的模板版本失效的唯一方法是更新片段名称或传递给模板标签的参数。

使用 django-adv-cache-tag,您可以通过版本控制来做到这一点,通过将版本作为模板标签的最后一个参数来管理自己的版本。但如果你想要使用 django-adv-cache-tag 的版本控制系统的力量,可能会太啰嗦了。

{% load adv_cache %}
{% with template_version=obj.date_last_updated|stringformat:"s"|add:"v1" %}
    {% cache 0 myobj_main_template obj.pk template_version %}
    ...
    {% endcache %}
{% endwith %}

django-adv-cache-tag 提供了一种使用 ADV_CACHE_VERSION 设置轻松实现此功能的方法。但是,通过更新它,所有缓存的版本都将被失效,而不仅仅是您更新的那些。

要这样做,只需创建一个具有特定内部版本的自己的标签即可。

class MyCacheTag(CacheTag):
    class Meta(CacheTag.Meta):
       internal_version = "v1"

MyCacheTag.register('my_cache')

然后,在您的模板中,您可以直接这样做:

{% load my_cache_tags %}
{% my_cache 0 myobj_main_template obj.pk obj.date_last_updated %}
...
{% endmy_cache %}

每次您更新模板内容并希望使其失效时,只需更改 MyCacheTag 类中的 internal_version(或者您也可以为此使用设置)。

更改缓存后端

如果您想更改单个模板标签的缓存后端,这很简单

class MyCacheTag(CacheTag):
    class Meta:
        cache_backend = 'templates'

但您也可以通过覆盖一个方法来实现这一点

from django.core.cache import get_cache

class MyCacheTag(CacheTag):
    def get_cache_object(self):
        return get_cache('templates')

如果您想要为旧对象使用一个缓存后端,而为新对象使用另一个更快的前端

from django.core.cache import get_cache

class MyCacheTag(CacheTag):
    class Meta:
        cache_backend = 'fast_templates'

    def get_cache_object(self):
        cache_backend = self.options.cache_backend
        if self.get_pk() < 1000:
            cache_backend = 'slow_templates'
        return get_cache(cache_backend)

get_cache_object 返回的值应该是一个缓存后端对象,但由于我们只在这个对象上使用 setget 方法,如果它提供这两个方法,它可以满足您的需求。此外,如果您不想使用缓存后端对象的默认 setget 方法,您还可以覆盖 CacheTag 类的 cache_setcache_get 方法。

请注意,我们还支持在模板标签中使用 django 的方式更改缓存后端,使用 using 参数,将其设置为最后一个参数(using 和缓存后端名称之间没有空格)。

{% cache 0 myobj_main_template obj.pk obj.date_last_updated using=foo %}

更改缓存键

CacheTag 类提供了三个类来创建缓存键

  • get_base_cache_key,默认返回一个可格式化的字符串(如果 include_pkTrue,则为“template.%(nodename)s.%(name)s.%(pk)s.%(hash)s”,否则为“template.%(nodename)s.%(name)s.%(hash)s”)

  • get_cache_key_args,返回用于前面字符串的参数

  • get_cache_key,将两者结合

参数如下:

  • nodename 参数是 templatetag 的名称:在 {% my_cache ... %} 中是 “my_cache”

  • name 是您的 templatetag 的“片段名称”,即过期时间之后的值

  • pk 仅在 self.options.include_pkTrue 时使用,并由 this.get_pk() 返回

  • hash 是片段名称之后所有参数的哈希值,排除最后一个版本号(只有当 self.options.versioningTrue 时才排除此值)

如果您想从缓存键的开头移除“template.”部分(如果有专门用于模板缓存的缓存后端,则此部分无意义),可以这样做:

class MyCacheTag(CacheTag):
    def get_base_cache_key(self):
        cache_key = super(MyCacheTag, self).get_base_cache_key()
        return cache_key[len('template:'):]  # or [9:]

向模板标签添加一个参数

默认情况下,CacheTag 提供的模板标签与默认 django 缓存模板标签具有相同的参数。

如果您想添加一个参数,很容易,因为该类提供了一个 get_template_node_arguments 方法,它将像正常 django templatetags 一样工作,接受一个标记列表,并返回将传递给真实 templatetag 的标记,以及与 CacheTag 相关的 Node 类。

假设您想在过期时间和片段名称之间添加一个 foo 参数

from django import template

from adv_cache_tag.tag import CacheTag, Node

class MyNode(Node):
    def __init__(self, nodename, nodelist, expire_time, foo, fragment_name, vary_on):
        """ Save the foo variable in the node (not resolved yet) """
        super(MyNode, self).__init__(self, nodename, nodelist, expire_time, fragment_name, vary_on)
        self.foo = foo


class MyCacheTag(CacheTag):

    Node = MyNode

    def prepare_params(self):
        """ Resolve the foo variable to it's real content """
        super(MyCacheTag, self).prepare_params()
        self.foo = template.Variable(self.node.foo).resolve(self.context)

    @classmethod
    def get_template_node_arguments(cls, tokens):
        """ Check validity of tokens and return them as ready to be passed to the Node class """
        if len(tokens) < 4:
            raise template.TemplateSyntaxError(u"'%r' tag requires at least 3 arguments." % tokens[0])
        return (tokens[1], tokens[2], tokens[3], tokens[4:])

准备模板缓存

这一部分不是关于重写类,但它可能很有用。当一个对象被更新时,在此时重新生成缓存的模板可能比在我们需要显示它时更好。

这很简单。你可以通过捕获你模型的post_save信号,或者简单地通过重写其save方法来实现。在这个例子中,我们将使用后一种解决方案。

唯一特别的地方是知道你的templatetag所在的模板路径。在我的情况下,我有一个仅为此目的的模板(包含在其他一些通用模板中),因此更容易找到它并像以下示例那样重新生成它。

因为我们不在请求中,所以我们这里没有Request对象,因此上下文处理器不起作用,我们必须创建一个将用于渲染模板的上下文对象,其中包含所有必要的变量。

from django.template import loader, Context

class MyModel(models.Model):
    # your fields

    def save(self, *args, **kwargs):
        super(MyModel, self.save(*args, **kwargs)

        template = 'path/to/my_template_file_with_my_cache_block.html'

        context = Context({
            'obj': self,

            # as you have no request, we have to add stuff from context processors manually if we need them
            'STATIC_URL': settings.STATIC_URL,

            # the line below indicates that we force regenerating the cache, even if it exists
            '__regenerate__': True,

            # the line below indicates if we only want html, without parsing the nocache parts
            '__partial__': True,

        })

        loader.get_template(template).render(context)

在渲染之前从数据库加载数据

这是一个特殊情况。比如说你想显示一个对象列表,但你只有从redis(使用ZSET,以id为值,以更新日期(用作版本)为分数)检索到的ids和版本。

如果你知道你总是有一个有效的模板版本在缓存中,因为它们每次保存时都会被重新生成,如上所述,那很好,只需将对象的主体键作为templatetag参数中的pk即可,缓存的模板将被加载。

但如果不是这种情况,你将遇到问题:当Django渲染模板时,上下文中存在的对象的唯一部分是主体键,所以如果你需要名称或任何其他字段来渲染缓存的模板,它将不起作用。

使用django-adv-cache-tag很容易解决这个问题,因为我们可以从数据库中加载对象并将其添加到上下文中。

视图
def my_view(request):
    objects = [
        dict(
            pk=val[0],
            date_last_updated=val[1]
        )
        for val in
            redis.zrevrange('my_objects', 0, 19, withscores=True)
    ]
    return render(request, "my_results.html", dict(objects=objects))
模板“my_results.html”
{% for obj in objects %}
    {% include "my_result.html" %}
{% endfor %}
模板“my_result.html”
{% load my_cache_tags %}
{% my_cache 0 myobj_main_template obj.pk obj.date_last_updated %}
    {{ obj }}
{% endmy_cache %}
templatetag

myapp/templatetags/my_cache_tags

from my_app.models import MyModel

class MyCacheTag(CacheTag):

    class Meta(CacheTag.Meta):
        """ Force options """
        include_pk = True
        versioning = True

    def create_content(self):
        """ If the object in context is not a real model, load it from db """
        if not isinstance(context['obj'], MyObject):
            context['obj'] = MyModel.objects.get(id=self.get_pk())
        super(MyCacheTag, self).create_content()

MyCacheTag.register('my_cache')

注意这一点,它生成的数据库请求与要加载的对象一样多。

还有更多...

如果你想要做更多,请自由查看CacheTag类的源代码(在tag.py中),所有方法都有文档说明。

设置

django-adv-cache-tag提供了5个你可以更改的设置。以下是列表,包括描述、默认值和Meta类中对应的字段(通过self.options.some_fieldCacheTag对象中访问)

  • ADV_CACHE_VERSIONING来激活版本控制,默认为False(在Meta类中的versioning

  • ADV_CACHE_COMPRESS来激活压缩,默认为False(在Meta类中的compress

  • ADV_CACHE_COMPRESS_LEVEL来设置压缩级别(从1(最小压缩)到9(最大压缩),默认为-1(相当于6)在Meta类中的compress_level

  • ADV_CACHE_COMPRESS_SPACES来激活空格压缩,默认为False(在Meta类中的compress_spaces

  • ADV_CACHE_INCLUDE_PK来激活“主键”功能,默认为False(在Meta类中的include_pk

  • ADV_CACHE_BACKEND来选择要使用的缓存后端,默认为"default"(在Meta类中的cache_backend

  • 使用ADV_CACHE_VERSION创建您自己的内部版本(将连接到django-adv-cache-tag的实际内部版本),默认为""Meta类中的internal_version

工作原理

以下是对django-adv-cache-tag中工作原理的简要概述

部分缓存

您的模板

{% load adv_cache %}
{% cache ... %}
    foo
    {% nocache %}
        bar
    {% endnocache %}
    baz
{% endcache %}

缓存版本(此处忽略版本和压缩,只是为了看看它是如何工作的)

foo
{% endRAW_xyz %}
    bar
{% RAW_xyz %}
baz

当加载缓存版本时,我们解析

{% RAW_xyz %}
foo
{% endRAW_xyz %}
    bar
{% RAW_xyz %}
baz
{% endRAW_xyz %}

第一个{% RAW_xyz %}和最后一个{% endRAW_xyz %}不包含在缓存版本中,并在解析前添加,仅为了节省一些字节。

{% RAW_xyz %}{% endRAW_xyz %}之间的部分完全不解析(被django视为TextNode

RAWendRAW模板标记的xyz部分取决于SECRET_KEY,因此对于特定网站是唯一的。

这允许最大程度地避免与缓存版本中解析内容的冲突。

我们本可以使用{% nocache %}{% endnocache %}而不是{% RAW_xyz %}{% endRAW_xyz %},但在缓存的解析模板中,如果html包含这些字符串之一,我们的最终模板就会损坏,所以我们使用带有散列的长字符串(但我们不能100%确定这些字符串不会出现在缓存的html中,但对于常见用法应该足够)

许可证

django-adv-cache-tag根据MIT许可证发布(见LICENSE文件)

运行测试

如果adv_cache_tag在您的项目的INSTALLED_APPS中,只需运行

django-admin test adv_cache_tag

(根据您的安装情况,您可能想使用django-admin./manage.py

如果您在一个新的虚拟环境中工作adv_cache_tag,安装您想要的django版本

pip install django

然后使adv_cache_tag模块可在您的python路径中使用。例如,使用virtualenv-wrapper,假设您在django-adv-cache-tag存储库的根目录下,只需这样做

add2virtualenv .

或者简单地

pip install -e .

然后要运行测试,此库提供了一个测试项目,因此您可以这样启动它们

DJANGO_SETTINGS_MODULE=adv_cache_tag.tests.testproject.settings django-admin.py test adv_cache_tag

或者简单地启动runtests.sh脚本(它将运行此确切命令)

./runtests.sh

支持的版本

Django版本

Python版本

库版本

1.7到1.11

2.7

0.4

1.7

3.4

1.1.3

1.8到1.10

3.4, 3.5

1.1.3

1.11

3.4到3.6

1.1.3

2.0

3.4到3.7

1.1.3

2.1

3.5到3.7

1.1.3

2.2

3.5到3.8

1.1.3

3.0

3.6到3.8

1.1.3

django-adv-cache-tag的第1版以来,已取消对Python 2的支持

项目详情


下载文件

下载适合您平台文件的文件。如果您不确定选择哪个,请了解更多关于安装软件包的信息。

源分布

django-adv-cache-tag-1.1.3.tar.gz (44.2 kB 查看哈希值)

上传于 源代码

构建版本

django_adv_cache_tag-1.1.3-py3-none-any.whl (30.6 kB 查看哈希值)

上传于 Python 3

由以下支持

AWSAWS云计算和安全赞助商DatadogDatadog监控FastlyFastlyCDNGoogleGoogle下载分析MicrosoftMicrosoftPSF赞助商PingdomPingdom监控SentrySentry错误日志StatusPageStatusPage状态页面