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 并为其添加适当的后缀,以便包含移动设备目录也)。

然而,我认为最大的挑战是能够将动态参数传递给它(表明这是一个移动请求)。您可以将此参数存储在会话中或修改模板名称,然后再将其传递给自定义渲染器(渲染器将删除此指示器部分并获取模板)。

以下是我组织模板的方式:

  1. templates 目录中创建两个目录 - mobiledesktop.
  2. mobile 目录中保留移动模板,在 desktop 中保留桌面模板。

这样您就不必重命名模板。


下面是我将如何渲染它们:

  1. 在中间件中读取 User-Agent。

  2. request 上设置一个名为 template_prefix 的属性,其值将为 mobiledesktop,具体取决于用户代理。例如:

    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'
    
  3. 在您看来,在模板名称前使用request.template_prefix。例如:

    def home(request):
        ...
        template = request.template_prefix + '/home.html'
        return render(request, template)
    

这将根据值 template_prefix 属性从 mobiledesktop 目录呈现模板。


UPDATE(根据问题编辑):

看看你的模板是如何组织的,我会这样做:

  1. 中间件:

    仅为移动请求设置 template_prefix

    def process_request(self, request):
        if mobile_user_agent:
            request.template_prefix = 'mobile-templates'
        else:
            request.template_prefix = '' # set an empty string
    
  2. 浏览量:

    使用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 块似乎是多余的。如果我有更好的解决方案,我会更新。