视图也就是 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
示例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/
现在点击登录是没有响应的,登录为 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
接下来就是点击上传后如何处理这个文件,应该是保存到服务器
# 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]