基于 django 中用户代理的移动模板,确保线程安全



我正在开发我的网站的移动版本,所以考虑使用用户代理作为为移动版和网络版提供不同模板的标准。我成功地从nginx读取了用户代理信息,并将其作为标头传递给了gunicorn服务器。

然后我创建了一个中间件,它读取此标头并更改设置文件中的模板目录。这最初似乎有效,但后来我意识到发生了竞争条件,因为此方法不是线程安全的。(我应该事先想到的)。

所以我开始考虑其他选择。一种解决方案是覆盖 django 的渲染方法,以包含基于请求标头的"dirs"参数。但后来我发现"dirs"参数已被弃用。以下是参考链接 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

在项目目录(不是应用程序文件夹的一部分)中,有一个移动模板文件夹,其结构如下

mobile-templates
    App1
        index.html
    App2
        about.html

谢谢阿努拉格

以下是我如何组织我的模板:

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

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

<小时 />

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

  1. 读取中间件中的用户代理。

  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目录呈现模板。

<小时 />

更新(根据问题编辑):

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

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

现在似乎不可能开箱即用。如果你真的想遵循这个架构,你必须编写自己的自定义加载器,并找出一种方法来传递请求/指标,让它知道这是一个移动请求。

编写加载器并不难(只需查看 Django 文件系统加载器,如果请求来自移动设备,则遍历所有templates_dirs并为其添加适当的后缀,以便您也包含移动目录)。

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

最新更新