如何使用django-compressor离线压缩表单媒体
How to use django-compressor to compress form media offline
包含如下代码片段的 Django 模板(包含带有媒体的表单)在将 django-compressor 与 COMPRESS_OFFLINE=True
一起使用时会抛出错误,因为 form
在离线时不可用执行压缩:
# Template snippet
{% compress js %}
{{ form.media.js }}
{% endcompress %}
一般来说,django-compressor 提供了 COMPRESS_OFFLINE_CONTEXT 设置来处理类似的情况。但是,如果站点包含许多此类表单或带有媒体的小部件,则此解决方案并不理想。
例如,目前,我在 settings.py 中执行类似的操作(针对每个小部件的媒体):
# settings.py
...
from my_app1.widgets import Widget1
from my_app1.widgets import Widget2
from my_app1.widgets import Widget3
...
widgets = {
'my_widget1': Widget1(),
'my_widget2': Widget2(),
'my_widget3': Widget3(),
...
}
for name, widget in widgets.items():
COMPRESS_OFFLINE_CONTEXT['{}_css'.format(name)] = widget.media['css']
COMPRESS_OFFLINE_CONTEXT['{}_js'.format(name)] = widget.media['js']
然后在模板中,我这样做:
{% compress js %}
{{ my_widget1_js }}
{% endcompress %}
有没有办法以更类似于 Django 的 {{ form.media }}
方法的方式来处理这种情况,或者可能不需要为站点中的每个小部件或每个表单(包含媒体)枚举特定媒体?
您可以通过使用一些带有 __getattr__
方法的自定义 class 来简化您的设置。这样,如果您将该对象传递到模板并尝试调用
{{ obj.my_widget1_js }}
__getattr__
中的逻辑可以在您的项目中搜索指定的小部件,return 它是媒体。
将自动发现每个小部件的 class 示例:
class WidgetImporter(object):
def __getattr__(self, name):
path = name.split('__') # this will split path on double underscore, so you can define submodule here
path = [(node, "".join(part.capitalize() for part in node.split('_'))) for node in path] # this will convert underscore_names to CamelCase names.
# determining for each module if it's name is CamelCased or underscored
real_path = []
for underscored, cameled in path:
try:
__import__(".".join(real_path + [underscored])) # first trying to import umderscored module
except ImportError:
try:
__import__(".".join(real_path + [cameled])) # now try with cameled
except ImportError:
return "" # or raise some exception here if you like
else:
real_path.append(cameled)
else:
real_path.append(underscored)
last_node = real_path[len(real_path) - 1]
return getattr(__import__(".".join(real_path), fromlist=[last_node]), last_node)() # returning actual class instance
这样你就可以在你的 settings.py 文件中使用它了:
COMPRESS_OFFLINE_CONTEXT = {
'widgets': WidgetImporter(),
}
并在您的模板中使用:
{{ widgets.my_app1__widgets__widget1.media.css }}
Class 将尝试将 my_app1__widgets__widget1
解析为您的 class 的实际路径并尝试将其导入。如果成功,将打印 media['css']
。它不是 100% 最佳代码和 100% 安全(如果有人可以访问您的模板,可能会有一些漏洞),但它可以完成工作。
包含如下代码片段的 Django 模板(包含带有媒体的表单)在将 django-compressor 与 COMPRESS_OFFLINE=True
一起使用时会抛出错误,因为 form
在离线时不可用执行压缩:
# Template snippet
{% compress js %}
{{ form.media.js }}
{% endcompress %}
一般来说,django-compressor 提供了 COMPRESS_OFFLINE_CONTEXT 设置来处理类似的情况。但是,如果站点包含许多此类表单或带有媒体的小部件,则此解决方案并不理想。
例如,目前,我在 settings.py 中执行类似的操作(针对每个小部件的媒体):
# settings.py
...
from my_app1.widgets import Widget1
from my_app1.widgets import Widget2
from my_app1.widgets import Widget3
...
widgets = {
'my_widget1': Widget1(),
'my_widget2': Widget2(),
'my_widget3': Widget3(),
...
}
for name, widget in widgets.items():
COMPRESS_OFFLINE_CONTEXT['{}_css'.format(name)] = widget.media['css']
COMPRESS_OFFLINE_CONTEXT['{}_js'.format(name)] = widget.media['js']
然后在模板中,我这样做:
{% compress js %}
{{ my_widget1_js }}
{% endcompress %}
有没有办法以更类似于 Django 的 {{ form.media }}
方法的方式来处理这种情况,或者可能不需要为站点中的每个小部件或每个表单(包含媒体)枚举特定媒体?
您可以通过使用一些带有 __getattr__
方法的自定义 class 来简化您的设置。这样,如果您将该对象传递到模板并尝试调用
{{ obj.my_widget1_js }}
__getattr__
中的逻辑可以在您的项目中搜索指定的小部件,return 它是媒体。
将自动发现每个小部件的 class 示例:
class WidgetImporter(object):
def __getattr__(self, name):
path = name.split('__') # this will split path on double underscore, so you can define submodule here
path = [(node, "".join(part.capitalize() for part in node.split('_'))) for node in path] # this will convert underscore_names to CamelCase names.
# determining for each module if it's name is CamelCased or underscored
real_path = []
for underscored, cameled in path:
try:
__import__(".".join(real_path + [underscored])) # first trying to import umderscored module
except ImportError:
try:
__import__(".".join(real_path + [cameled])) # now try with cameled
except ImportError:
return "" # or raise some exception here if you like
else:
real_path.append(cameled)
else:
real_path.append(underscored)
last_node = real_path[len(real_path) - 1]
return getattr(__import__(".".join(real_path), fromlist=[last_node]), last_node)() # returning actual class instance
这样你就可以在你的 settings.py 文件中使用它了:
COMPRESS_OFFLINE_CONTEXT = {
'widgets': WidgetImporter(),
}
并在您的模板中使用:
{{ widgets.my_app1__widgets__widget1.media.css }}
Class 将尝试将 my_app1__widgets__widget1
解析为您的 class 的实际路径并尝试将其导入。如果成功,将打印 media['css']
。它不是 100% 最佳代码和 100% 安全(如果有人可以访问您的模板,可能会有一些漏洞),但它可以完成工作。