【问题标题】:Mobile templates based on user-agent in django ensuring thread safetydjango中基于用户代理的移动模板确保线程安全
【发布时间】:2016-06-22 04:29:48
【问题描述】:

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

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

所以我开始考虑其他选择。一种解决方案是覆盖 django 的渲染方法以包含基于请求标头的“dirs”参数。但后来我发现“dirs”参数已被弃用。以下是参考链接https://docs.djangoproject.com/en/1.9/_modules/django/shortcuts/#render 所以即使这样也行不通。

另一种解决方案是为移动和网络设置不同的模板名称并相应地加载它们。但是我不想这样做,并且希望保持模板目录结构对于 Web 和移动设备完全相同。

必须有一种方法来覆盖模板目录。如果移动模板目录中缺少模板,这将给我一个回退到网络版本模板的优势。

任何关于如何实现这一点的建议都会有所帮助。

这就是我的模板的组织方式。

App1
   templates
       App1
           index.html
           catalog.html
App2
   templates
       App2
           about.html

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

mobile-templates
    App1
        index.html
    App2
        about.html

谢谢 阿努拉格

【问题讨论】:

  • 修改request对象添加目录名怎么样?然后render 可以通过将路径附加到这个request.template_dir 变量来呈现文件。
  • @SudhanshuShekhar - 即使我修改了请求对象,渲染函数如何自动获取目录名称?我也检查了源代码,并没有发现 request.template_dir 被渲染函数使用。不知道我是否遗漏了什么,请澄清。如果您的意思是在我的渲染函数中预先附加目录名称,那么回退到网络版本将不起作用。
  • 我在想类似的东西:render(request, ["app/template/t_mobile.html", "app/template/t.html"], context)。您可以使用request.template_dir 来决定是否添加t_mobile.html 作为选项。
  • 我已经尝试过了,但是直到我在设置文件中定义了移动模板目录之后它才起作用。一旦我这样做了,我在渲染函数中指定的模板顺序就不再重要了。

标签: django django-templates thread-safety mobile-website django-middleware


【解决方案1】:

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

  1. templates dir 中创建两个目录 - 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)
    

这将从mobiledesktop 目录呈现模板,具体取决于template_prefix 属性的值。


更新(根据问题编辑):

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

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

【讨论】:

  • 这将在后备案例中引发错误,因为与桌面模板相比,移动模板非常少。如果没有找到移动模板,它应该回退到桌面模板。
  • @anurag 在这种情况下,您只能在要呈现不同模板的那些视图中使用request.template_prefix。如果该视图没有移动模板,请手动使用 desktop 前缀。更新了答案。
  • 是的,但问题是我不知道模板是否存在。因此,无论何时创建移动模板,我都必须检查文件是否存在或修改我的渲染函数。我可以通过使用参数“dirs”在不修改渲染函数的情况下实现这一点,但是因为它已被弃用,我正在寻找一种正确的方法。不明白为什么它被弃用了。
  • 感谢@xyres,这应该可以。我需要对其进行测试,因为我当前的模板结构有点不同。验证后将恢复。我还用我当前的模板结构更新了问题。
  • @anurag Since Django 1.8, select_template works differently。因此,它不会像我在回答中描述的那样工作。我会尝试更新。
【解决方案2】:

目前看来,开箱即用是不可能的。如果你真的想遵循这个架构,你将不得不编写自己的自定义加载器,并找出一种方法来传递请求/指示符,让它知道它是一个移动请求。

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

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

【讨论】:

  • 这不是关于遵循特定的架构,这更像是一个问题陈述,我正在寻找一个合适的解决方案。我可以创建加载器,但是正如您所说,传递动态参数很痛苦,因为加载器方法无法访问请求对象。 docs.djangoproject.com/en/1.9/ref/templates/api/#custom-loaders 并且在加载模板之前修改模板名称然后读取它并撤消之前所做的修改似乎更加混乱。
  • 似乎是一个普遍的问题,不知道人们如何解决这个问题
猜你喜欢
  • 1970-01-01
  • 2012-04-06
  • 1970-01-01
  • 2015-08-10
  • 1970-01-01
  • 2014-03-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多