Django根据设备类型响应的一种解决方案

本文介绍了如何在Django中利用页面模板继承和UserAgent判断实现后端响应式布局。通过创建base_mobile和base_pc模板,解耦页面框架和内容,结合context_processors自定义设备类型,减少为移动和PC端分别编写页面的工作量。虽然不是真正的自适应网页,但对于小型网站来说是一个更优的平衡点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Django根据设备类型响应的一种解决方案


只看代码请直接往下拖。

环境

Django 2.1.1
Python 3.6.6
Ubuntu

提出问题

进行网页微调(字体大小、布局微调)时,使用前端 js 或 css 自然优于后端处理;但在 移动/ PC 端间切换时,往往涉及到较大的布局更改,用 js 一个一个调显然不太现实,不如后端重新来过。市面上大多也是这样,前端加个 js 判断,再根据设备类型直接定向到不同的链接页面。

但是,对于个人小站或是调整并不大的网站,为移动与 PC 写两套不同的页面,是一件难以忍受的事情。

解决思路

利用 Django 页面模板的继承特性,将页面框架与内容解耦。

也就是说,只需要为 移动端/PC端 分别制作不同的底层模板,并为内容部分提供好接口;内容部分大家都一样,只管往底层的接口里塞就行了。

所以,问题简化成如下:

所有页面均继承自 base_mobile.html 或 base_pc.html
base_*.html 只提供页面框架模板,留下 block 供内容接入

解决过程

查阅资料发现,一个模板只能继承一个父模板,并且没有办法进行条件判断。所以如要不走死胡同,我们只能把所有的页面全部继承到 base.html,再在 base.html 里进行分类讨论。

继承到 base.html 简单,只要加一行 {% extends 'base.html' %} 就完了,但分类讨论呢?如何在模板层中完成?

仿照 js 的判断方法,我们需要对浏览器的 UserAgent 进行处理。在 Django 中,UserAgent 作为数据包头的一部分被存放在字典 request.META 中,key 为'HTTP_USER_AGENT'。之后就可以仿照 js 进行一下关键词搜索,这里列举一个非常非常潦草的解决方案.

def get_device(request):
    ''' Get client device type.''''
    agent = request.META['HTTP_USER_AGENT'].lower()
    mobile = ['iphone','android','symbianos','windows phone']

    if any([agent.find(name) + 1 for name in mobile]):
        return 'mobile'
    else:
        return 'pc'

因为只是搞个人小站,就懒得再使用正则,并区分安卓苹果平板等等了,有更仔细需求的,请自行查阅或对着各大网站 F12 ,此处仅做抛砖引玉。

但这是 Python 函数,是不能直接写到 Django 模板里的,Django 模板只认 context 参数里的内容(其实把模板引擎换成 jinjia2 就搞定这个问题了,不过换引擎貌似得不偿失…),总不能每个 render 函数前全加一串代码吧?

这个时候我想到了一个细节,就是在渲染模板时,虽然没有在 context 里添加 request 对象,但是写 {{request.***}}却能正常工作。看起来 request 对象是被 Django 自动添加进 render 的 context 参数里的,那我们能不能手动再添加一条自己的呢?查阅果然发现了这么一条——

class DjangoTemplates[source]
… …
DjangoTemplates engines accept the following OPTIONS:
‘context_processors’:
a list of dotted Python paths to callables that are used to populate the context when a template is rendered with a request. These callables take a request object as their argument and return a dict of items to be merged into the context.

It defaults to an empty list.
Django官方文档

那问题就解决了,我们可以在 mysite/myapp 下新建一个 context_processors.py,把刚才的函数稍作改动,让它把设备类型作为 device_type 塞进一个字典并返回。现在只需把这个函数注册到项目的 settings.py 里,Django 就会帮我们在 render 的 context 中加一个存有设备类型的 device_type 变量。我们在 base.html 中,只需用 {% if device_type == 'mobile' %}这样的条件标签进行编写即可。

总结

这种在后端进行判断响应、解耦框架内容的方式,发挥出了模板引擎相较于前端 DOM 操作的优势,相较于写两套页面系统也减少了很多工作量,利于维护。

缺点自然也有,它虽然只制作了框架,但本质还是为不同的设备穷举页面,仍然不能与真正意义上的‘自适应网页’相当。

只不过相较于考验布局水平的自适应,再相较于繁琐的前端 DOM,此方案为小型网站提供了一个更优的平衡点。

于我个人,在解决这次问题的过程中,对 Django 整个系统又有了一个更深的认识,对其工作流程更加清晰,还熟悉了几乎整个 settings.py(笑)

代码

项目目录 (只放出相关文件)

mysite
├── mysite
│   ├── context_processors.py
│   ├── settings.py
│   └── urls.py
├── manage.py
├── myapp
│   ├── templates
│   │   └── myapp
│   │       └── home.html
│   └── views.py
└── templates
    └── base.html

mysite/settings.py(只放出相关内容,以下文件同)

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': ['/...../mysite/templates'],        # 此处自行添加路径
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
                'mysite.context_processors.device',    # 前四个是 startproject 时自带的,这个是我们自己加的
            ],
        },
    },
]

mysite/context_processors.py

def device(request):
    '''Add client device type into template contexts.'''
    agent = request.META['HTTP_USER_AGENT'].lower()
    mobile = ['iphone','android','symbianos','windows phone']

    if any([agent.find(name) + 1 for name in mobile]):
        return {'device_type':'mobile'}
    else:
        return {'device_type':'pc'}

templates/base.html
要啥 js 啊 css 啊仿照下面自己加就行了。

<!DOCTYPE HTML>
<html>
    <head>
        <meta charset='utf8'>
        <title>
        {% block title %}
        {% endblock %}
        </title>
    </head>
    <body>
        {% if device_type == 'mobile' %}
        <p>我是手机版框架</p>
        {% else %}
        <p>我是电脑版框架</p>
        {% endif %}
        </p>我是两个框架的相同点</p>
        <div>
            {% block body %}
            {% endblock %}
        </div>
   </body>
</html>

myapp/templates/myapp/home.html
同理,要啥自己加。

{% extends 'base.html' %}
{% block title %} 测试界面 {% endblock %}
{% block body %}
<p>我是与它们无关的页面内容</p>
{% endblock %}

myapp/views.py

from django.shortcuts import render
# Create your views here.

def home(request):
    return render(request,'myapp/home.html')

mysite/urls.py

from django.contrib import admin
from django.urls import path

from myapp import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.home, name='home'),
]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值