【问题标题】:Change Django Templates Based on User-Agent基于 User-Agent 更改 Django 模板
【发布时间】:2010-09-14 22:23:06
【问题描述】:

我做了一个 Django 网站,但我喝了 Koolaid,我想制作一个 IPhone 版本。经过深思熟虑,我想出了两个选择:

  1. 创建一个完整的其他网站,例如 i.xxxx.com。使用 Django 的站点框架将其绑定到同一个数据库中。
  2. 找一些中间件读取用户代理,动态更改模板目录。

不过,我真的更喜欢选项 #2;我有一些保留,主要是因为 Django 文档discourages changing settings on the fly。我找到了一个snippet 可以做我想做的事。我的主要问题是让它尽可能无缝,我希望它对用户来说是自动的和透明的。

还有其他人遇到过同样的问题吗?有人愿意分享一下他们是如何制作 iPhone 版本的 Django 网站的吗?

更新

我结合了中间件和调整模板调用。

对于中间件,我使用了minidetector。我喜欢它,因为它检测到一个plethora 的移动用户代理。我所要做的就是在我的视图中检查 request.mobile。

对于模板调用调整:

 def check_mobile(request, template_name):
     if request.mobile:
         return 'mobile-%s'%template_name
     return template_name

我将它用于我知道我有两个版本的任何视图。

待办事项:

  • 弄清楚如何在 render_to_response 的扩展版本中访问 request.mobile,这样我就不必使用 check_mobile('template_name.html')
  • 如果不存在移动版本,则使用以前的自动回退到常规模板。

【问题讨论】:

  • 你也可以这样做/需要这个来拥有一个特殊的 IE6 版本的模板。我正在考虑使用 1 个模板,如果用户代理是 IE6,则带有一些条件。
  • 我喜欢你的做法 :) 你会说我有过多的皮纳塔吗?
  • 请注意,不幸的是,minidetector 似乎与 Python 3 不兼容。请参阅this SO question 了解替代方案。

标签: python django django-templates mobile-website django-middleware


【解决方案1】:

检测中间件中的用户代理,切换url绑定,获利!

怎么样? Django 请求对象有一个 .urlconf 属性,可以通过中间件设置。

来自 django 文档:

Django 确定根 URLconf 要使用的模块。通常情况下,这是 ROOT_URLCONF 设置的值,但是 如果传入的 HttpRequest 对象有 一个名为 urlconf 的属性(由 中间件请求处理),其 值将用于代替 ROOT_URLCONF 设置。

  1. 在 yourproj/middlware.py 中,编写一个检查 http_user_agent 字符串的类:

    import re
    MOBILE_AGENT_RE=re.compile(r".*(iphone|mobile|androidtouch)",re.IGNORECASE)
    class MobileMiddleware(object):
        def process_request(self,request):
            if MOBILE_AGENT_RE.match(request.META['HTTP_USER_AGENT']):
                request.urlconf="yourproj.mobile_urls"
    
  2. 不要忘记在 settings.py 中将其添加到 MIDDLEWARE_CLASSES 中:

    MIDDLEWARE_CLASSES= [...
        'yourproj.middleware.MobileMiddleware',
    ...]
    
  3. 创建一个移动端的urlconf,yourproj/mobile_urls.py:

    urlpatterns=patterns('',('r'/?$', 'mobile.index'), ...)
    

【讨论】:

    【解决方案2】:

    一个简单的解决方案是围绕django.shortcuts.render 创建一个包装器。我将我的放在应用程序根目录的utils 库中。包装器通过在“移动”或“桌面”文件夹中自动呈现模板来工作。

    utils.shortcuts:

    from django.shortcuts import render
    from user_agents import parse
    
    def my_render(request, *args, **kwargs):
      """
      An extension of django.shortcuts.render.
    
      Appends 'mobile/' or 'desktop/' to a given template location
      to render the appropriate template for mobile or desktop
    
      depends on user_agents python library
      https://github.com/selwin/python-user-agents
    
      """
      template_location = args[0]
      args_list = list(args)
    
      ua_string = request.META['HTTP_USER_AGENT']
      user_agent = parse(ua_string)
    
      if user_agent.is_mobile:
          args_list[0] = 'mobile/' + template_location
          args = tuple(args_list)
          return render(request, *args, **kwargs)
      else:
          args_list[0] = 'desktop/' + template_location
          args = tuple(args_list)
          return render(request, *args, **kwargs)
    

    view:

    from utils.shortcuts import my_render
    
    def home(request):    return my_render(request, 'home.html')
    

    【讨论】:

      【解决方案3】:

      最佳方案:使用 minidetector 将额外信息添加到请求中,然后使用 django 的内置请求上下文将其传递给您的模板,就像这样

      from django.shortcuts import render_to_response
      from django.template import RequestContext
      
      def my_view_on_mobile_and_desktop(request)
          .....
          render_to_response('regular_template.html', 
                             {'my vars to template':vars}, 
                             context_instance=RequestContext(request))
      

      然后在您的模板中,您可以引入以下内容:

      <html>
        <head>
        {% block head %}
          <title>blah</title>
        {% if request.mobile %}
          <link rel="stylesheet" href="{{ MEDIA_URL }}/styles/base-mobile.css">
        {% else %}
          <link rel="stylesheet" href="{{ MEDIA_URL }}/styles/base-desktop.css">
        {% endif %}
        </head>
        <body>
          <div id="navigation">
            {% include "_navigation.html" %}
          </div>
          {% if not request.mobile %}
          <div id="sidebar">
            <p> sidebar content not fit for mobile </p>
          </div>
          {% endif %>
          <div id="content">
            <article>
              {% if not request.mobile %}
              <aside>
                <p> aside content </p>
              </aside>
              {% endif %}
              <p> article content </p>
            </aricle>
          </div>
        </body>
      </html>
      

      【讨论】:

        【解决方案4】:

        有一篇很好的文章解释了如何通过不同的模板呈现相同的数据 http://www.postneo.com/2006/07/26/acknowledging-the-mobile-web-with-django

        但是,您仍然需要自动将用户重定向到移动网站,这可以使用多种方法来完成(您的 check_mobile 技巧也可以使用)

        【讨论】:

          【解决方案5】:

          我正在开发 djangobile,一个 django 移动扩展:http://code.google.com/p/djangobile/

          【讨论】:

            【解决方案6】:

            另一种方法是创建您自己的模板加载器来加载特定于用户代理的模板。这是一种非常通用的技术,可用于根据其他因素动态确定必须加载的模板,例如请求的语言(现有 Django i18n 机器的良好伴侣)。

            Django Book 有一个section on this subject

            【讨论】:

              【解决方案7】:

              你应该看看django-mobileadmin源代码,它正好解决了这个问题。

              【讨论】:

                【解决方案8】:

                在某些中间件中解析用户的 UA 后将用户重定向到 i.xxx.com 怎么样?我非常怀疑移动用户是否关心 url 的外观,他们仍然可以使用主 url 访问您的网站。

                【讨论】:

                • 这有什么帮助?您仍然拥有该站点的模板名称和布局。要么创建一套全新的逻辑,要么问题仍然存在。
                【解决方案9】:

                您可以修改请求并添加一个值,让您的视图知道用户是否在 iphone 上,而不是动态更改模板目录。然后包装 render_to_response (或您用于创建 HttpResponse 对象的任何内容)以获取模板的 iphone 版本而不是标准的 html 版本(如果他们使用的是 iphone)。

                【讨论】:

                • 可能是最好的方法...我只是不想在我的视图中添加额外的代码。谢谢你的回答!
                • 我所做的是编写自己的 render_to_response 包装器并将其放入项目范围的实用程序库中。然后只需导入它而不是 render_to_response(如 my_render_response)。您的视图中有相同数量的代码,只需更改视图中的 import 和 func 调用。
                • @imjoevasquez 这是事后的事,但你总是可以做类似from my_site_wide_stuff.utils import my_render_to_response as render_to_response 的事情——因为render_to_response 无论如何只是一个捷径。虽然查找/替换可能是要走的路;)
                • 如果我尝试根据移动设备或桌面设备在 DOM 的不同位置呈现模板,您会推荐相同的方法吗?
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2016-08-05
                • 2020-07-09
                • 2017-07-04
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多