django中基于user-agent的移动模板确保线程安全
Mobile templates based on user-agent in django ensuring thread safety
我正在开发我的网站的移动版本,因此想到使用 user-agent 作为为移动和网络版本提供不同模板的标准。
我成功地从 nginx 读取了 user-agent 信息,并将其作为 header 传递给了 gunicorn 服务器。
然后我创建了一个中间件来读取这个 header 并更改设置文件中的模板目录。这似乎最初有效,但后来我意识到由于此方法不是线程安全的,因此发生了竞争条件。 (早该想到的before-hand)。
所以我开始考虑其他选择。一种解决方案是根据请求 header 覆盖 django 的渲染方法以包含 "dirs" 参数。但后来我发现 "dirs" 参数已被弃用。以下是参考 link https://docs.djangoproject.com/en/1.9/_modules/django/shortcuts/#render
所以即使这样也行不通。
另一种解决方案是为移动设备和 Web 使用不同的模板名称并相应地加载它们。但是,我不想这样做,并希望使 Web 和移动设备的模板目录结构完全相同。
必须有一种方法可以覆盖模板目录。如果它在移动模板目录中丢失,这将使我回退到网络版模板的优势。
关于如何实现这一点的任何建议都会有所帮助。
我的模板是这样组织的。
App1
templates
App1
index.html
catalog.html
App2
templates
App2
about.html
并且在项目目录中(不是app文件夹的一部分),有一个mobile templates文件夹,其结构如下
mobile-templates
App1
index.html
App2
about.html
谢谢
阿努拉格
现在似乎无法开箱即用。如果你真的想遵循这种架构,你将不得不编写自己的自定义加载程序,并想出一种方法来传递 request/indicator 以让它知道它是一个移动请求。
编写加载程序并不太难(只需查看 Django 文件系统加载程序,如果请求来自移动设备,循环遍历所有 templates_dirs 并为其添加适当的后缀,以便包含移动设备目录也)。
然而,我认为最大的挑战是能够将动态参数传递给它(表明这是一个移动请求)。您可以将此参数存储在会话中或修改模板名称,然后再将其传递给自定义渲染器(渲染器将删除此指示器部分并获取模板)。
以下是我组织模板的方式:
- 在
templates
目录中创建两个目录 - mobile
和 desktop
.
- 在
mobile
目录中保留移动模板,在 desktop
中保留桌面模板。
这样您就不必重命名模板。
下面是我将如何渲染它们:
在中间件中读取 User-Agent。
在 request
上设置一个名为 template_prefix
的属性,其值将为 mobile
或 desktop
,具体取决于用户代理。例如:
def process_request(self, request):
# read user agent and determine if
# request is from mobile or desktop
# ...
if mobile_user_agent:
request.template_prefix = 'mobile'
else:
request.template_prefix = 'desktop'
在您看来,在模板名称前使用request.template_prefix
。例如:
def home(request):
...
template = request.template_prefix + '/home.html'
return render(request, template)
这将根据值 template_prefix
属性从 mobile
或 desktop
目录呈现模板。
UPDATE(根据问题编辑):
看看你的模板是如何组织的,我会这样做:
中间件:
仅为移动请求设置 template_prefix
。
def process_request(self, request):
if mobile_user_agent:
request.template_prefix = 'mobile-templates'
else:
request.template_prefix = '' # set an empty string
浏览量:
使用os.path.join
;捕获 TemplateDoesNotExist
异常。
import os.path
from django.template.loader import get_template
from django.template.base import TemplateDoesNotExist
def index(request):
try:
template = os.path.join(request.template_prefix, 'App1/index.html')
get_template(template)
except TemplateDoesNotExist:
template = 'App1/index.html'
return render(request, template)
我已经对此进行了测试并且有效。但是在每个视图中写一个 try...except
块似乎是多余的。如果我有更好的解决方案,我会更新。
我正在开发我的网站的移动版本,因此想到使用 user-agent 作为为移动和网络版本提供不同模板的标准。 我成功地从 nginx 读取了 user-agent 信息,并将其作为 header 传递给了 gunicorn 服务器。
然后我创建了一个中间件来读取这个 header 并更改设置文件中的模板目录。这似乎最初有效,但后来我意识到由于此方法不是线程安全的,因此发生了竞争条件。 (早该想到的before-hand)。
所以我开始考虑其他选择。一种解决方案是根据请求 header 覆盖 django 的渲染方法以包含 "dirs" 参数。但后来我发现 "dirs" 参数已被弃用。以下是参考 link https://docs.djangoproject.com/en/1.9/_modules/django/shortcuts/#render 所以即使这样也行不通。
另一种解决方案是为移动设备和 Web 使用不同的模板名称并相应地加载它们。但是,我不想这样做,并希望使 Web 和移动设备的模板目录结构完全相同。
必须有一种方法可以覆盖模板目录。如果它在移动模板目录中丢失,这将使我回退到网络版模板的优势。
关于如何实现这一点的任何建议都会有所帮助。
我的模板是这样组织的。
App1
templates
App1
index.html
catalog.html
App2
templates
App2
about.html
并且在项目目录中(不是app文件夹的一部分),有一个mobile templates文件夹,其结构如下
mobile-templates
App1
index.html
App2
about.html
谢谢 阿努拉格
现在似乎无法开箱即用。如果你真的想遵循这种架构,你将不得不编写自己的自定义加载程序,并想出一种方法来传递 request/indicator 以让它知道它是一个移动请求。
编写加载程序并不太难(只需查看 Django 文件系统加载程序,如果请求来自移动设备,循环遍历所有 templates_dirs 并为其添加适当的后缀,以便包含移动设备目录也)。
然而,我认为最大的挑战是能够将动态参数传递给它(表明这是一个移动请求)。您可以将此参数存储在会话中或修改模板名称,然后再将其传递给自定义渲染器(渲染器将删除此指示器部分并获取模板)。
以下是我组织模板的方式:
- 在
templates
目录中创建两个目录 -mobile
和desktop
. - 在
mobile
目录中保留移动模板,在desktop
中保留桌面模板。
这样您就不必重命名模板。
下面是我将如何渲染它们:
在中间件中读取 User-Agent。
在
request
上设置一个名为template_prefix
的属性,其值将为mobile
或desktop
,具体取决于用户代理。例如:def process_request(self, request): # read user agent and determine if # request is from mobile or desktop # ... if mobile_user_agent: request.template_prefix = 'mobile' else: request.template_prefix = 'desktop'
在您看来,在模板名称前使用
request.template_prefix
。例如:def home(request): ... template = request.template_prefix + '/home.html' return render(request, template)
这将根据值 template_prefix
属性从 mobile
或 desktop
目录呈现模板。
UPDATE(根据问题编辑):
看看你的模板是如何组织的,我会这样做:
中间件:
仅为移动请求设置
template_prefix
。def process_request(self, request): if mobile_user_agent: request.template_prefix = 'mobile-templates' else: request.template_prefix = '' # set an empty string
浏览量:
使用
os.path.join
;捕获TemplateDoesNotExist
异常。import os.path from django.template.loader import get_template from django.template.base import TemplateDoesNotExist def index(request): try: template = os.path.join(request.template_prefix, 'App1/index.html') get_template(template) except TemplateDoesNotExist: template = 'App1/index.html' return render(request, template)
我已经对此进行了测试并且有效。但是在每个视图中写一个 try...except
块似乎是多余的。如果我有更好的解决方案,我会更新。