视图也就是 Django 结构中的 views.py 文件。

HttpRequest对象:常用属性

Django 会建立一个包含请求源数据的 HttpRequest 对象,当 Django 加载对应的视图时,HttpRequest 对象将作为函数视图的第一个参数(request),每个视图负责返回一个 HttpResponse 对象。

例如:

def index(request):
  return HttpResponse("首页")
属性 描述
request.scheme 表示请求协议的字符串(http或https)
request.body 原始HTTP请求正文
request.path 一个字符串,请求页面的完整路径,不包含域名
request.method 一个字符串,请求的HTTP方法,如:GET、POST等
request.GET GET请求的所有参数,返回QueryDict类型,类似于字典
request.POST POST请求的所有参数,返回QueryDict类型
request.COOKIES 以字典格式返回cookie
request.session 可读写的类似于字典的对象,表示当前会话
request.FILES 所有上传的文件
request.META 返回字典,包含所有HTTP请求头。如:客户端ip、Referer等。

编写myapp视图测试以上属性

# devops/myapp/views.py
def request_detail(request):
    print(f"scheme: {request.scheme}")
    print(f"body: {request.body}")
    print(f"path: {request.path}")
    print(f"method: {request.method}")
    print(f"GET: {request.GET}")
    print(f"POST: {request.POST}")
    print(f"COOKIES: {request.COOKIES}")
    print(f"session: {request.session}")
    print(f"FILES: {request.FILES}")
    print(f"META: {request.META}")
    return HttpResponse("请求对象测试")

编写myapp url调用视图

# devops/myapp/urls.py
from django.urls import path
from myapp import views
urlpatterns = [
    path('blog/', views.myapphtml),
    path('request_detail/', views.request_detail, name='request_detail'),
]

访问请求 http://localhost:8000/myapp/request_detail

# print 信息会以日志的类型输出
scheme: http
body: b''
path: /myapp/request_detail/
method: GET
GET: <QueryDict: {}>
POST: <QueryDict: {}>
COOKIES: {'csrftoken': 'MQzpzEHgLJ9Mk4iDy8RkwvdpWSwbIUSL7hLzEwqxImBYO36F2WbGG30QmmvLM5vp'}
session: <django.contrib.sessions.backends.db.SessionStore object at 0x000001EB636B3CD0>
FILES: <MultiValueDict: {}>
META: 信息庞大,省略

可以以字典的形式获取META中的具体信息,META中包含了请求者的ip地址、浏览器信息、如:

print(f"META: {request.META['REMOTE_ADDR']}")

HttpRequest对象:常用方法

方法 描述
request.get_host() 服务器主机地址和端口
request.get_port() 服务器端口
request.get_full_path() 请求页面完整路径和查询参数
request.get_raw_uri() 请求页面URL所有信息,包括主机名、路径和参数

继续在视图中添加

def request_detail(request):
    print(f"get_host: {request.get_host()}")
    print(f"get_port: {request.get_port()}")
    print(f"get_full_path: {request.get_full_path()}")
    print(f"get_raw_uri: {request.get_raw_uri()}")
    return HttpResponse("请求对象测试")

访问请求 http://localhost:8000/myapp/request_detail

# 方法返回的信息
get_host: localhost:8000
get_port: 8000
get_full_path: /myapp/request_detail/
get_raw_uri: http://localhost:8000/myapp/request_detail/

HttpRequest对象:接受URL参数

传参一般用于sql查询,后端根据传参条件去执行sql,URL参数格式:

# 单个传参
http://xxx.com/yyy/zzz/?id=1
# 多个传参
http://xxx.com/yyy/zzz/?id=1&count=2

URL 从 ? 开始后面就是传参了,可以通过 request.GET['id'] 来获取

def request_detail(request):
    print(f"URL传参id: {request.GET['id']}")
    print(f"URL传参count: {request.GET['count']}")
    return HttpResponse("请求对象测试")

访问请求 http://localhost:8000/myapp/request_detail/?id=1&count=2

URL传参id: 1
URL传参count: 2

如果在获取2个参数的时候,URL中只传了一个,请求会报错,可以使用 request.GET.get('count') ,如果没有传参将会返回 None

URL传参id: 1
URL传参count: None

HttpRequest对象:QueryDict对象

request.GET 和 request.POST 返回的值都是QueryDict,类似于字典

对于字典的处理有以下常用方法

方法(request.GET或request.POST的基础上) 描述
get(key, default) 返回key的值,如果不存在,则返回 default
items() 返回迭代器,键值
values() 返回返回迭代器,所有键的值
keys() 返回所有键
getlist(key, default) 返回key的值作为列表,如果不存在,则返回default
lists() 返回返回迭代器,所有键的值作为列表
dict() 返回字典

测试,其中的迭代器转换为正常可识别的字符串

def request_detail(request):
    print(dict(req.items()))
    print(list(req.values()))
    print(req.keys())
    print(req.getlist('id'))
    print(dict(req.lists()))
    print(req.dict())

访问请求 http://localhost:8000/myapp/request_detail/?id=1&count=2

{'id': '1', 'count': '2'}
['1', '2']
dict_keys(['id', 'count'])
['1']
{'id': ['1'], 'count': ['2']}
{'id': '1', 'count': '2'}

示例1: GET搜索结果

编写一个执行sql的搜索页面

# devops/myapp/views.py
def search(request):
    req = request.GET
    id = req.get('id')
    count = req.get('count')
    sql = f"select * from table where id={id},count={count}"
    context = "这是关键字%s和%s的搜索结果,执行的sql为%s" % (id, count, sql)
    return render(request, 'search.html', {"result": context})

# devops/myapp/urls.py
from django.urls import path
from myapp import views
urlpatterns = [
    path('blog/', views.myapphtml),
    path('request_detail/', views.request_detail, name='request_detail'),
    path('search/', views.search, name='search'),
]

结合前面再编写一个html页面

# templates/search.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>搜索结果</title>
</head>
<body>
<h1>搜索结果</h1>
<h3>{{ result }}</h3>
</body>
</html>

访问请求 http://localhost:8000/myapp/search/?id=1&count=2

py_req_search

示例2:POST登录页面

正常的登录流程为,先访问登录页面,再使用用户名密码进行登录并进入登录后的首页

访问登录页面为 GET 请求,所以先编写 GET 请求要访问的登录页面

编写登录页面

# templates/login.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>
<h1>欢迎访问DevOps系统</h1>
<form action="">
    用户名:<input type="text" name="username"><br>
    密码:<input type="password" name="password"><br>
    <button type="submit">登录</button>
</form>
</body>
</html>

编写视图

# devops/views.py
from django.shortcuts import render

def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')

编写url

from django.contrib import admin
from django.urls import path, include, re_path
from myapp import views
from devops import views as devops_views
urlpatterns = [
    path('admin/', admin.site.urls),
    re_path('^$', views.index),
    path('myapp/', include('myapp.urls')),
    re_path('myapp/([0-9]{4})/([0-9]{2})', views.year_mouth),
    path('login/', devops_views.login, name='login')
]

访问 http://localhost:8000/login/

py_req_login

现在点击登录是没有响应的,登录为 POST 请求,继续编写视图

# devops/views.py
from django.shortcuts import render, redirect   # 新增redirect

def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    elif request.method == 'POST':
        reqP = request.POST
        username = reqP.get('username')
        password = reqP.get('password')
        if username == 'admin' and password == '123456':
            return redirect('/myapp/blog')        # 验证成功后,跳转到blog页面
        else:
            msg = '用户名或密码错误!'
            return render(request, 'login.html', {'msg': msg})   # 错误后,将错误信息返回到login页面

修改 html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>
<h1>欢迎访问DevOps系统</h1>
<form action="{% url 'login' %}" method="post">         # 点击登录后使用post请求再次跳转到路径 login 中验证
    用户名:<input type="text" name="username"><br>
    密码:<input type="password" name="password"><br>
    <span style="color: red">{{ msg }}</span><br>       # 返回错误信息
    <button type="submit">登录</button>
</form>
</body>
</html>

在setting.py里面禁用csrf

#'django.middleware.csrf.CsrfViewMiddleware',

访问 http://localhost:8000/login/,并尝试正确和错误的密码

示例3:上传文件,如:修改头像

和登录逻辑类似,也要先访问到上传的页面,再进行文件上传

编写上传页面

# templates/uploads.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>上传文件</title>
</head>
<body>
<h1>上传文件</h1>
<form method="post" action="uploads.html">
    <input type="file" name="file"><br>
    <button type="submit">上传</button>
</form>
</body>
</html>

编写视图

# devops/views.py
def user_upload(request):
    if request.method == 'GET':
        return render(request, 'uploads.html')

编写url

# devops/urls.py
...
urlpatterns = [
    path('admin/', admin.site.urls),
    re_path('^$', views.index),
    path('myapp/', include('myapp.urls')),
    re_path('myapp/([0-9]{4})/([0-9]{2})', views.year_mouth),
    path('login/', devops_views.login, name='login'),
    path('uploads/', devops_views.user_upload, name='uploads')
]

访问 http://localhost:8000/uploads

py_req_uploads

接下来就是点击上传后如何处理这个文件,应该是保存到服务器

# devops/views.py
def user_upload(request):
    if request.method == 'GET':
        return render(request, 'uploads.html')
    elif request.method == 'POST':
        print(request.FILES)      # 可以使用request.FILES来进行debug
        obj = request.FILES.get("file")   # 获取上传后的文件对象,这里的file就是html文件中指定的<input type="file" name="file">
        file_path = os.path.join('uploads', obj.name)
        if not os.path.exists('uploads'):
            os.makedirs('uploads')
        with open(file_path, 'wb+') as f:
            for chunk in obj.chunks():    # obj.chunks()使用分块的方法读取大文件,可以减少内存的使用
                f.write(chunk)
        msg = f"{obj.name} 上传成功"
        return render(request, 'uploads.html', {'msg': msg})
# debug上传后可以看到<MultiValueDict: {'file': [<InMemoryUploadedFile: 开启上班模式 极简主义 简约 黑色背景 打工人 原创 4K电脑壁纸_彼岸图网.jpg (image/jpeg)>]}>

更改html

# templates/uploads.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>上传文件</title>
</head>
<body>
<h1>上传文件</h1>
<form method="post" action={% url 'uploads' %} enctype="multipart/form-data">  # 处理上传二进制转换需要enctype
    <input type="file" name="file"><br>
    <span style="color: green">{{ msg }}</span><br>
    <button type="submit">上传</button><button type="reset">重置</button>
</form>
</body>
</html>

访问 http://localhost:8000/uploads,并上传一个文件,该文件会上传到项目目录的 uploads/[file]

评论




正在载入...
PoweredHexo
HostedAliyun
DNSAliyun
ThemeVolantis
UV
PV
BY-NC-SA 4.0