来源引用网络知识与某站曹老师视频相互结合学习记录,仅供参考!
Django 表单定义和使用
表单是搜集用户数据信息的各种表单元素的集合,其作用是实现网页上的数据交互,一般就是在开发过程中新建一些实体,然后修改一些实体,或业务上是"登录、添加、修改"等操作;例如"用户在网站输入数据信息,然后提交到网站服务器端进行处理(如数据录入和用户登录注册等)";网页表单是 Web 开发的一项基本功能,Django5 的表单功能由 Form类实现,主要分为两种(这两种都需要掌握);
- diango.forms.Form:是一个基础的表单功能;
- diango.forms.ModelForm:是在 diango.forms.Form 的基础上结合模型所生成的数据表单,是基于模型的,功能上更加的强大;
Django Form 表单定义与使用
实例演示
1、修改"djangoProject"项目"helloWorld"应用下的"forms.py"配置文件内容,添加定义一个 BookInfoForm 类,添加功能使用 Form 来实现;
- class BookInfoForm(Form)--定义BookInfoForm类,继承Form(需要导包引入from django.forms import Form模块);
- bookName = forms.CharField(max_length=20, label='图书名称')--定义字段bookName(CharField 对应的是一个文本字段,CharField 到页面上生成的是一个 input 框),因为这里是自己定义字段(如果是基于模型的话就不用自己来定义字段了),首先是定义图书的名称,设置 max_length 最大的限制,label 它会自动生成添加表单,其实就是前来一串的表单字段名称label提示,也是生成一个标签的,最后返回给bookName;
- price = forms.FloatField(label='图书价格')--定义字段price,简单写入一个label 图书价格,然后返回给price;
- publishDate = forms.DateField(label='出版日期')--定义字段publishDate,简单写入一个label 发布日期,然后返回给publishDate;
- forms.CharField(choices=)--下拉框对应的是forms.CharField(),里面要跟上一个choices 参数;
- bookTypeList = BookTypeInfo.objects.values()--这个choices 怎么得到呢?choices 是一个元组类型的,是 key-value 形式对应下拉框的 id 和 text 选项,所以需要(导入 from helloWorld.models import BookTypeInfo)使用 BookTypeInfo.objects.values() 获取所有的图书类别列表,返回给bookTypeList;
- choices = (v\['id', v'bookTypeName') for v, v in enumerate(bookTypeList)]--图书类别以下拉框形式显示,下拉框选项id是图书类别Id,下拉框选项文本是图书类别名称,先是 choices = \[\] 元组,使用choices = enumerate() 这个简便的方法直接把choices = enumerate(bookTypeList) 这里面的图书类别遍历一遍,然后获取一个只有 key-value 形式的图书类型和名称的一个元组,需要遍历两个choices = for v, v in enumerate(bookTypeList) 要遍历获取 id 和 bookTypeName 这两个,就可以得到以这种形式的元组;
- bookType_id = forms.ChoiceField(choices=choices, label='图书类别')--补全信息并返回定义bookType_id 字段,定义完成。
# 导包
from django import forms
from django.forms import ModelForm, Form
from helloWorld.models import StudentInfo, BookTypeInfo
# 定义学生form表单StudentForm类,需要继承ModelForm父类
class StudentForm(ModelForm):
# 根据模板文件models.py需要嵌套一层Meta(Meta是一个配置中心)
class Meta:
# 需要获取数据,所有得定义导入model(数据是从StudentInfo里面获取到的)--就是导数据
model = StudentInfo
# 表单要显示的字段信息,__all__代表所有字段
# fields = '__all__'
# 指定字段
fields = ['name', 'age']
# 定义控件
widgets = {
'name': forms.TextInput(attrs={'id': 'name', 'class': 'inputClass'}),
'age': forms.NumberInput(attrs={'id': 'age', 'class': 'inputClass'}),
}
# 指定标签
labels = {
'name': '姓名',
'age': '年龄'
}
# 定义BookInfoForm类,需要继承Form(导包引入from django.forms import Form模块)
class BookInfoForm(Form):
"""
图书表单
"""
bookName = forms.CharField(max_length=20, label='图书名称')
price = forms.FloatField(label='图书价格')
publishDate = forms.DateField(label='出版日期')
# 获取图书类别列表(导包引入from helloWorld.models import BookTypeInfo)
bookTypeList = BookTypeInfo.objects.values()
# 图书类别以下拉框形式显示,下拉框选项id是图书类别Id,下拉框选项文本是图书类别名称
choices = [(v['id'], v['bookTypeName']) for v, v in enumerate(bookTypeList)]
# 定义bookType_id字段
bookType_id = forms.ChoiceField(choices=choices, label='图书类别')

2、在项目"templates"目录"book"目录文件路径下新建一个名称为"add2"的 HTML 文件或者复制一份"add.html"文件并改名为"add2.html";因为这是重新以 Form 表单来实现的,修改"add2.html"文件内容;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{ title }}</title>
</head>
<body>
<h3>{{ title }}</h3>
<form action="/book/add" method="post">
{% csrf_token %}
<table>
{# 以table形式 #}
{{ form.as_table }}
<tr>
<td colspan="2">
<input type="submit" value="add2提交">
</td>
</tr>
</table>
</form>
</body>
</html>

3、修改"djangoProject"项目"helloWorld"应用下的"views.py"配置文件内容,新增定义一个 preAdd2 方法;再修改bookList方法,注释掉所有条件查询,使 BookInfo.objects.all() 显示所有查询数据生效;
# 导包
import datetime
import os
from django.core.paginator import Paginator
from django.db import connection, transaction
from django.db.backends.utils import CursorDebugWrapper
from django.db.models import Q, Sum, Avg, F
from django.http import HttpResponse, HttpResponseNotFound, JsonResponse, StreamingHttpResponse, FileResponse
from django.shortcuts import render, redirect
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from helloWorld.forms import StudentForm, BookInfoForm
from helloWorld.models import StudentInfo, BookInfo, BookTypeInfo, AccountInfo
# Create your views here.
# 定义类
class Person:
# 属性 姓名
name = None
# 属性 年龄
age = None
def __init__(self, name, age):
self.name = name
self.age = age
# 自定义一个index方法,里面要加上request请求对象
def index(request):
# 传递字符串类型数据,定义字符串类型变量str
str = 'hello world!'
# 日期对象date,导包引入import datetime
date = datetime.datetime.now()
# 定义字典类型数据
myDict = {"Jack":"11", "Queen":"12", "King":"13"}
# 创建一个对象,例如:zhangsan
zhangsan = Person("张三", 18)
# 定义列表myList、元组myTuple--列表和元组里面的元素类型没有限制,列表里面可以嵌套列表,元组里面可以嵌套元组
myList = [1, 'apple', 3.1415926, True, [5, 6], (7, 8), {9, 10}]
myTuple = (1, 'apple', 3.1415926, True, [5, 6], (7, 8), {9, 10})
# 变量content_value是字典格式
content_value = {"content_msg": str, "msg2": myDict, "msg3": zhangsan, "msg4": myList, "msg5": myTuple, "date": date}
# context= 传递的本身要求格式是一个字典类型
return render(request, 'index.html', context=content_value)
# 定义index_id方法,里面第一个参数request请求对象,第二个参数 id
def index_id(request, id):
# 判断 id是否为0
if id == 0:
# 导包引入redirect模块,如果id等于0的话,重定向到一个目标静态地址
return redirect("/static/error.html")
else:
# 使用HttpResponse直接返回内容--因为id是int类型,里面得使用str()进行转换
return HttpResponse("id是" + str(id) + "的某某平台系统页面")
# 定义index_id方法,第一个参数request请求对象,里面继续跟多个路由变量"year, month, day, id"
def index_id02(request, year, month, day, id):
# 使用HttpResponse直接返回内容--因为id是int类型,里面得使用str()进行转换
return HttpResponse(str(year) + "/" + str(month) + "/" + str(day) + "/" +"id是" + str(id) + "的某某平台系统页面")
# 定义index_id方法,第一个参数request请求对象,后面跟多个路由变量"year, month, day"--注意,使用正则后不能与"id"再混合使用了
def index_id03(request, year, month, day):
# 使用HttpResponse直接返回内容--因为id是int类型,里面得使用str()进行转换
return HttpResponse(str(year) + '/' + str(month) + '/' + str(day) + "使用正则路由后的某某平台系统页面")
# 定义目标文件路径
file_path = "D:\\works\\djangoProject\\datas\\IDM.rar"
# 定义download_file1下载方法,请求需要带上request(所有的请求都要带上request)
def download_file1(request):
# 定义文件--使用open函数方法以"rb"二进制读取模式打开文件--然后返回一个file文件对象
file = open(file_path, 'rb')
# 创建构造好了HttpResponse对象之后返回给response
response = HttpResponse(file)
# 指定文件类型
response['Content-Type'] = 'application/rar-compressed'
# 扩展--指定附件及文件名字(可以动态拼接filename,并不建议使用原始名称)
response['Content-Disposition'] = 'attachment; filename=file1.rar'
return response
def download_file2(request):
file = open(file_path, 'rb')
# 创建构造好了StreamingHttpResponse对象之后返回给response
response = StreamingHttpResponse(file)
response['Content-Type'] = 'application/rar-compressed'
response['Content-Disposition'] = 'attachment; filename=file2.rar'
return response
def download_file3(request):
file = open(file_path, 'rb')
# 创建构造好了FileResponse对象之后返回给response
response = FileResponse(file)
response['Content-Type'] = 'application/rar-compressed'
response['Content-Disposition'] = 'attachment; filename=file3.rar'
return response
def get_test(request):
"""
get 请求测试
:param request:
:return:
"""
print(f'打印请求方式request.method:{request.method}')
print(f'打印常用属性request.content_type:{request.content_type}')
print(f'打印常用属性request.content_params:{request.content_params}')
print(f'打印常用属性request.COOKIES:{request.COOKIES}')
print(f'打印常用属性request.scheme:{request.scheme}')
print(f'打印常用方法request.is_secure:{request.is_secure()}')
print(f'打印常用方法request.get_host:{request.get_host()}')
print(f'打印常用方法request.get_full_path:{request.get_full_path()}')
print(f'使用request.GET.get("name")方法获取对应key值参数:{request.GET.get("name")}' )
print(f'使用request.GET.get("pwd")方法获取对应key值参数:{request.GET.get("pwd")}')
print(f'使用request.GET.get("dev")方法获取不存在的参数,返回值是'None':{request.GET.get("dev")}')
print(f'request.GET.get("prod", "555")获取的prod参数是不存在的,设置返回值'555':{request.GET.get("prod", "555")}')
return HttpResponse("hello world!http get ok!")
def post_test(request):
"""
post 请求测试方法
:param request:
:return:
"""
print(f'打印请求方式request.method:{request.method}')
print(f'使用request.POST.get("name")方法获取对应key值参数:{request.POST.get("name")}')
print(f'使用request.POST.get("pwd")方法获取对应key值参数:{request.POST.get("pwd")}')
print(f'使用request.POST.get("dev")方法获取不存在的参数,返回值是'None':{request.POST.get("dev")}')
print(f'request.POST.get("prod", "556")获取的prod参数是不存在的,设置返回值'556':{request.POST.get("prod", "556")}')
return HttpResponse("hello world!http post ok!")
def to_login(request):
"""
跳转到登录页面
:param request:
:return:
"""
# 跳转到登录页面可直接使用渲染render()方法
return render(request, 'login.html')
def login(request):
"""
登录到主页面
:param request:
:return:
"""
# 首先获取用户名和密码
user_name = request.POST.get('user_name')
pwd = request.POST.get('pwd')
# 模拟用户登录(暂不连接数据库)
if user_name == "python3" and pwd == "123456":
# session中存一个用户名
request.session['currentUserName'] = user_name
print(f'代码脚本中session获取:{request.session['currentUserName']}')
# 获取HttpResponse
response = render(request, 'main.html')
# 通过response.set_Cookie()方法来设置cookie
response.set_cookie('remember_me', True)
return response
else:
# 定义变量content_value为报错提示信息,里面是键值对格式的对象
content_value = {"error_info":"用户名或者密码错误!"}
return render(request, 'login.html', context=content_value)
def to_upload(request):
"""
跳转到上传文件页面
:param request:
:return:
"""
return render(request, 'upload.html')
def upload(request):
"""
上传文件处理
:param request:
:return:
"""
# 获取上传的文件,如果没有文件,就默认为None(如果不写指定的None,也是默认None)
myFile = request.FILES.get("myfile", None)
# 判断如果是有myFile的情况,就是说明有文件
if myFile:
# 打开特定的文件进行二进制的写操作--上传文件路径烦请自行创建(例如:在项目datas路径下新建"myFile"目录文件)
f = open(os.path.join("D:\\works\\djangoProject\\datas\\myFile", myFile.name), "wb+")
# 分块写入文件
for chunk in myFile.chunks():
f.write(chunk)
f.close()
return HttpResponse("文件上传成功!")
else:
return HttpResponse("没有发现可以上传的文件!")
# 定义List类,继续继承ListView(导包引入 from django.views.generic import ListView)
class List(ListView):
# 设置模版文件
template_name = 'student/list.html'
# 设置模型外的数据--例如:指定标题
extra_context = {'title': '学生信息列表'}
# 定义数据集(查询结果集)--StudentInfo(导入 from helloWorld.models import StudentInfo)
queryset = StudentInfo.objects.all()
# 每页展示数据的条数(自行设置)
paginate_by = 5
# 设置上下文对象名称(其实就是一个列表的对象),建议使用自定义名称,不然就默认为 objectlist
context_object_name = 'student_list'
# 定义Detail类,继续继承DetailView(导包引入 from django.views.generic import DetailView)
class Detail(DetailView):
# 设置模版文件
template_name = 'student/detail.html'
# 设置模型外的数据--例如:指定标题
extra_context = {'title': '学生信息详情'}
# 设置查询模型--指定model(不然无法查询数据)
model = StudentInfo
# 设置上下文对象名称
context_object_name = 'student'
# 指定URL中用于获取对象的唯一标识符的参数名称,默认为'pk';
# 如若在urls.py文件中自定义了名称,则需要添加对应pk_url_kwarg参数为自定义名称
# pk_url_kwarg = 'id'
# 定义Create类,继续继承CreateView(导包引入 from django.views.generic import CreateView)
class Create(CreateView):
# 设置模版文件,名称修改为create(有的名称叫"add"也行)
template_name = 'student/create.html'
# 设置模型外的数据--例如:指定标题"添加"或者"新增"都行
extra_context = {'title': '学生信息新增'}
# 因为是表单,所以需要使用form_class参数来指定form表单
form_class = StudentForm
# 执行成功后跳转地址(跳转地址可以是一个新的页面,也可以进入一个列表页面)
success_url = '/student/list'
# 定义Update类,继续继承UpdateView(导包引入 from django.views.generic import UpdateView)
class Update(UpdateView):
# 设置模版文件,名称修改为update(自定义)
template_name = 'student/update.html'
# 设置模型外的数据--例如:指定标题"编辑"或者"修改"都行
extra_context = {'title': '学生信息编辑'}
# 设置查询模型--指定model(不然无法查询数据)--传来一个组件"pk"会自动的去查数据、封装对象、显示到页面上
model = StudentInfo
# 因为是表单,所以需要使用form_class参数来指定form表单
form_class = StudentForm
# 执行成功后跳转地址(跳转地址可以是一个新的页面,也可以进入一个列表页面)
success_url = '/student/list'
# 定义Delete类,继续继承DeleteView(导包引入 from django.views.generic import DeleteView)
class Delete(DeleteView):
# 设置模版文件,名称修改为delete(自定义)
template_name = 'student/delete.html'
# 设置模型外的数据--例如:指定标题"删除"或者"修改"都行
extra_context = {'title': '学生信息删除'}
# 设置上下文对象名称
context_object_name = 'student'
# 设置查询模型--指定model(不然无法查询数据)--传来一个组件"pk"会自动的去查数据、封装对象、显示到页面上
model = StudentInfo
# 执行成功后跳转地址(跳转地址可以是一个新的页面,也可以进入一个列表页面)
success_url = '/student/list'
# 自定义跳转子类模板页面方法
def to_course(request):
"""
跳转到子类模板页面
:param request:
:return:
"""
return render(request, 'course.html')
# 定义bookList方法
def bookList(request):
"""
图书列表查询
"""
# 查询显示所有数据
bookList = BookInfo.objects.all()
# 使用BookInfo.objects.extra()方法,里面使用where加参数(元组格式),在params加上具体的参数,也是一个元组
# 如果是模型外的参数(models.py里面没有定义的参数),可以使用BookInfo.objects.select()方法
# bookList = BookInfo.objects.extra(where=["price>%s"], params=[90])
# # 使用BookInfo.objects.raw()方法,直接写一个SQL语句
# bookList = BookInfo.objects.raw('SELECT * FROM t_book where price>%s', params=[90])
# 使用connection方法需要导包引入from django.db import connection模块,返回一个cursor游标
# 指定cursor游标为CursorDebugWrapper类型,需导包引入from django.db.backends.utils import CursorDebugWrapper模块
# cursor: CursorDebugWrapper = connection.cursor()
# 然后就可以执行使用execute方法,查询图书价格大于90的记录数
# cursor.execute('select count(*) from t_book where price>90')
# print('预计提取的一个结果是(2,):', cursor.fetchone())
content_value = {"title": "图书列表", "bookList": bookList}
return render(request, 'book/list.html', context=content_value)
def bookList2(request):
"""
多表查询,有正向查询和反向查询
:param request:
:return:
"""
# 正向查询(经常使用)--多的一端BookInfo条件id=2且只查询一条数据,返回图书book并指定类型BookInfo
book: BookInfo = BookInfo.objects.filter(id=2).first()
# 能够输出book的所有信息,也能输出所关联的bookType.bookTypeName数据信息
print(book.bookName, book.bookType.bookTypeName)
# 反向查询(很少使用)--(导包引入from helloWorld.models import BookTypeInfo)
# 一的一端BookTypeInfo条件id=1且只查询一条数据,返回图书类别bookType并指定类型BookTypeInfo
bookType: BookTypeInfo = BookTypeInfo.objects.filter(id=1).first()
# 输出读取关联的这个图书类别下的所有图书的数据集中第一条数据,固定语法bookinfo_set
print(bookType.bookinfo_set.first().bookName)
# 显示这个类别下的所有数据,返回的结果是一个QuerySet数据集,这个类型下有两条数据,
print(bookType.bookinfo_set.all())
# 把QuerySet这个数据集转发到页面上就可以进行遍历了
content_value = {"title": "图书列表", "bookList": bookType.bookinfo_set.all()}
return render(request, 'book/list.html', context=content_value)
def preAdd(request):
"""
预处理,添加操作
:param request:
:return:
"""
# 预处理操作需要先找到所有数据
bookTypeList = BookTypeInfo.objects.all()
print(f'可自行打印查看bookTypeList是否查询出所有数据:{bookTypeList}')
# 预处理操作之后要返回到图书添加页面
content_value = {"title": "图书添加", "bookTypeList": bookTypeList}
return render(request, 'book/add.html', context=content_value)
def preAdd2(request):
"""
预处理,添加操作 使用form表单
:param request:
:return:
"""
# 导包引入from helloWorld.forms import BookInfoForm
form = BookInfoForm()
context_value = {"title": "图书添加2", "form": form}
return render(request, 'book/add2.html', context_value)
def add(request):
"""
图书添加
:param request:
:return:
"""
# 封装对象
book = BookInfo()
# 添加数据
book.bookName = request.POST.get("bookName")
book.publishDate = request.POST.get("publishDate")
book.bookType_id = request.POST.get("bookType_id")
book.price = request.POST.get("price")
# 因为是模板,所以直接使用save()方法
book.save()
# 数据添加完成之后,可以获取新增数据的主键id
print(f'数据添加完成之后,获取新增数据的主键id:{book.id}')
# 添加完成之后,需要返回到列表,直接return返回那个对象
return bookList(request)
# 修改预处理,定义修改操作方法;页面需要传一个id来确定是要修改哪一条记录
def preUpdate(request, id):
"""
预处理,修改操作
:param request:
:return:
"""
print("通过path里面的变量,页面需要传一个id来确定是要修改哪一条记录:", id)
# 根据传过来的id去查询数据,并返回一个book对象
book = BookInfo.objects.get(id=id)
print(f'可自行打印查看返回的book对象:{book}')
bookTypeList = BookTypeInfo.objects.all()
print(bookTypeList)
# book对象也需要传到页面上去,到时候页面上就能够取值了
content_value = {"title": "图书修改", "bookTypeList": bookTypeList, "book": book}
return render(request, 'book/edit.html', context=content_value)
def update(request):
"""
图书修改
:param request:
:return:
"""
book = BookInfo()
book.id = request.POST.get("id")
book.bookName = request.POST.get("bookName")
book.publishDate = request.POST.get("publishDate")
book.bookType_id = request.POST.get("bookType_id")
book.price = request.POST.get("price")
book.save()
return bookList(request)
def delete(request, id):
"""
图书删除
:param request:
:return:
"""
# 删除指定id数据
BookInfo.objects.get(id=id).delete()
# 根据条件删除多条数据
# BookInfo.objects.filter(price__gte=100).delete()
# 删除所有数据
# BookInfo.objects.all().delete()
return bookList(request)
# 定义转账transfer方法--导包引入from django.db import transaction模块使用事务
@transaction.atomic
def transfer(request):
"""
模拟转账
:param request:
:return:
"""
# 开启事务,
sid = transaction.savepoint()
# 添加异常处理机制
try:
# 导包引入from helloWorld.models import AccountInfo方法
# AccountInfo.objects.filter()方法查找A用户
a1 = AccountInfo.objects.filter(user='A用户_赵大')
# 导包引入from django.db.models import F 模块
a1.update(account = F('account') + 100)
# 这个"'A用户_赵大'"账户是加account余额,那么对应的"'B 用户_ 钱二'"账户就是减account余额
b2 = AccountInfo.objects.filter(user='B用户_钱二')
# 修改操作
b2.update(account = F('account') - 100 / 0)
# 提交事务,如果不设置,当程序执行完成后,会自动提交事务
transaction.savepoint_commit(sid)
except Exception as e:
print(f'异常信息为:{e}')
# 事务回滚--传一个sid表示需要事务回滚哪一个事务
transaction.savepoint_rollback(sid)
# 执行完两个操作之后,模拟转账完成,返回到界面
return HttpResponse("OK!转账完成!")


4、修改"djangoProject"应用下的"urls.py"配置文件内容,配置定义映射关系;
"""
URL configuration for djangoProject project.
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/5.0/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.conf import settings
from django.contrib import admin
from django.urls import path, re_path, include
from django.views.generic import RedirectView
from django.views.static import serve
import helloWorld.views
urlpatterns = [
path('admin/', admin.site.urls),
# 可直接复制上面"admin",把"admin"修改为"index";后面是跟的"处理函数helloWorld.views.index"
# 是helloWorld应用的views里面的index()--引入helloWorld.views,记得index后不能加小括号,因为传入的是对象;
path('index/', helloWorld.views.index, name='index'),
# 要是请求"redirectTo"这个请求的话给重定向到"index"--重定向使用RedirectView.as_view(),里面使用关键词参数url
path('redirectTo', RedirectView.as_view(url="index/")),
# 自定义名称,例如叫"index_id/",后面再跟上某网页id;<int:id> int指定类型,路由变量为id;对应对应的路由映射
path('index_id/<int:id>', helloWorld.views.index_id),
# 多个路由变量
path('index_id02/<int:year>/<int:month>/<int:day>/<int:id>', helloWorld.views.index_id02),
# 正则表达式匹配是必须使用re_path方法,正则表达式"?P"开头是固定格式
re_path('index_id03/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})', helloWorld.views.index_id03),
# 配置媒体文件的路由地址
re_path('media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}, name='media'),
# 命名空间namespace--比如说是"user/"开头的,第一个参数include(),第二个参数namespace
# 导包引入include模块,include()里面也有两个参数,第一个是指定user的urls,第二个是项目名;
path('user/', include(('user.urls', 'user'), namespace='user')),
path('order/', include(('order.urls', 'order'), namespace='order')),
# 配置 download_file
path('download1/', helloWorld.views.download_file1),
path('download2/', helloWorld.views.download_file2),
path('download3/', helloWorld.views.download_file3),
path('get', helloWorld.views.get_test),
path('post', helloWorld.views.post_test),
path('toLogin/', helloWorld.views.to_login),
path('login', helloWorld.views.login),
path('toUpload/', helloWorld.views.to_upload),
path('upload', helloWorld.views.upload),
path('student/list', helloWorld.views.List.as_view()),
# 默认名称就是"pk",建议不要更改,在开发过程中就默认为pk即可
path('student/<int:pk>', helloWorld.views.Detail.as_view()),
# 如果把默认名称"pk"更改为其他(例如:id),则需要在urls.py配置文件Detail类中添加pk_url_kwarg参数
# path('student/<int:id>', helloWorld.views.Detail.as_view())
path('student/create', helloWorld.views.Create.as_view()),
path('student/update/<int:pk>', helloWorld.views.Update.as_view()),
path('student/delete/<int:pk>', helloWorld.views.Delete.as_view()),
path('toCourse/', helloWorld.views.to_course),
path('book/list', helloWorld.views.bookList),
path('book/list2', helloWorld.views.bookList2),
path('book/preAdd', helloWorld.views.preAdd),
path('book/add', helloWorld.views.add),
path('book/preUpdate/<int:id>', helloWorld.views.preUpdate),
path('book/delete/<int:id>', helloWorld.views.delete),
path('book/update', helloWorld.views.update),
path('transfer/', helloWorld.views.transfer),
path('book/preAdd2', helloWorld.views.preAdd2)
]

5、修改项目"templates"目录"book"目录文件路径下的"list.html"的文件内容,新增一个"图书添加 2"preAdd2 链接;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{ title }}</title>
</head>
<body>
<h3>{{ title }}</h3>
<a href="/book/preAdd">图书添加</a><br/><br/>
<a href="/book/preAdd2">图书添加2(使用Form)</a><br/><br/>
<table border="1">
<tr>
<th>编号</th>
<th>图书名称</th>
<th>图书价格</th>
<th>出版日期</th>
<th>图书类别</th>
<th>更多操作</th>
</tr>
{% for book in bookList %}
<tr>
<td>{{ book.id }}</td>
<td>{{ book.bookName }}</td>
<td>{{ book.price }}</td>
<td>{{ book.publishDate | date:'Y-m-d' }}</td>
<td>{{ book.bookType.bookTypeName }}</td>
<td>
<a href="/book/preUpdate/{{ book.id }}">图书修改</a>
<a href="/book/delete/{{ book.id }}">图书删除</a>
</td>
</tr>
{% endfor %}
</table>
</body>
</html>

6、运行项目,浏览器输入"http://127.0.0.1:8000/book/list"测试验证;经测试使用 Form 表单可以把数据添加成功,Form 表单它是自动生成的;

拓展知识
表单Form的常用属性和方法
|------------------------|---|---|
| 表单Form的常用属性和方法 |||
| data | 默认值为None,以字典形式表示,字典的键为表单字段,代表将数据绑定到对应的表单字段。 ||
| auto_id | 默认值为id_%s,以字符串格式化表示,若设置HTML元素控件的id属性,比如表单字段job,则元素控件id属性为id_job,%s 代表表单字段名称。 ||
| prefix | 默认值为None,以字符串表示,设置表单的控件属性name和id的属性值,如果一个网页里使用多个相同的表单,那么设置该属性可以区分每个表单。 ||
| initial | 默认值为None,以字典形式表示,在表单的实例化过程中设置初始化值。 ||
| label_suffix | 若参数值为None,则默认为冒号,以表单字段job为例,其HTML控件含有label标签(<label for="id_job">职位:</label> ),其中 label标签里的冒号由参数label_suffix设置。field_order:默认值为None,则以表单字段定义的先后顺序进行排列,若要自定义排序,则将每个表单字段按先后顺序放置在列表里,并把列表作为该参数的值。 ||
| use_required_attribute | 默认值为None(或为True ),为表单字段所对应的 HTML控件设置required属性,该控件为必填项,数据不能为空,若设为False,则HTML控件为可填项。errors():验证表单的数据是否存在异常,若存在异常,则获取异常信息,异常信息可设为字典或JSON格式。 ||
| is_valid() | 验证表单数据是否存在异常,若存在,则返回False,否则返回True。 ||
| as_table() | 将表单字段以HTML的<table>标签生成网页表单。 ||
| as_ul() | 将表单字段以HTML的<ul>标签生成网页表单。 ||
| as _p() | 将表单字段以HTML的<p>标签生成网页表单。 ||
| has_changed() | 对比用于提交的表单数据与表单初始化数据是否发生变化。 ||
表单定义字段类型
|--------------------------|---|---|
| 表单定义字段类型 | 描述说明 ||
| CharField | 文本框,参数max_length 和min_length分别设置文本长度。 ||
| IntegerField | 数值框,参数max_value设置最大值,min_value设置最小值。 ||
| FloatField | 数值框,继承IntegerField,验证数据是否为浮点数。 ||
| DecimalField | 数值框,继承IntegerField,验证数值是否设有小数点,参数max_digits设置最大位数,参数decimal_places设置小数点最大位数。 ||
| DateField | 文本框,继承BaseTemporalField,具有验证日期格式的功能,参数input_formats设置日期格式。 ||
| TimeField | 文本框,继承BaseTemporalField,验证数据是否为datetime.time或特定时间格式的字符串。 ||
| DateTimeField | 文本框,继承 BaseTemporalField,验证数据是否为datetime.datetime ,datetime.date或特定日期时间格式的字符串。 ||
| DurationField | 文本框,验证数据是否为一个有效的时间段。 ||
| RegexField | 文本框,继承CharField,验证数据是否与某个正则表达式匹配,参数regex设置正则表达式。 ||
| EmailField | 文本框,继承CharField,验证数据是否为合法的邮箱地址。 ||
| FileField | 文件上传框,参数max_length设置上传文件名的最大长度,参数allow_empty_file设置是否允许文件内容为空。 ||
| ImageField | 文件上传控件,继承FileField,验证文件是否为Pillow库可识别的图像格式。FilePathField:文件选择控件,在特定的目录选择文件,参数 path是必需参数,参数值为目录的绝对路径;参数recursive、match、 allow_files和allow_folders为可选参数。 URLField:文本框,继承CharField,验证数据是否为有效的路由地址。 ||
| BooleanField | 复选框,设有选项True和 False,如果字段带有required=True,复选框就默认为True。 ||
| NullBooleanField | 复选框,继承BooleanField,设有3个选项,分别为None、True和 False。ChoiceField:下拉框,参数choices 以元组形式表示,用于设置下拉框的选项列表。 ||
| TypedChoiceField | 下拉框,继承 ChoiceField,参数coerce 代表强制转换数据类型,参数empty_value表示空值,默认为空字符串。 ||
| MultipleChoiceField | 下拉框,继承ChoiceField,验证数据是否在下拉框的选项列表。 ||
| TypedMultipleChoiceField | 下拉框,继承MultipleChoiceField,验证数据是否在下拉框的选项列表,并且可强制转换数据类型,参数coerce代表强制转换数据类型,参数 empty_value表示空值,默认为空字符串。 ||
| ComboField | 文本框,为表单字段设置验证功能,比如字段类型为CharField,为该字段添加EmailField,使字段具有邮箱验证功能。 ||
| MultiValueField | 文本框,将多个表单字段合并成一个新的字段。 ||
| SplitDateTimeField | 文本框,继承MultiValueField,验证数据是否为datetime.datetime或特定日期时间格式的字符串。 ||
| GenericIPAddressField | 文本框,继承CharField,验证数据是否为有效的IP地址。 ||
| SlugField | 文本框,继承CharField,验证数据是否只包括字母、数字、下画线及连字符。 ||
| UUIDField | 文本框,继承CharField,验证数据是否为UUID格式。 ||
每个表单有一些常用属性
|---------------------|---|---|
| 每个表单有一些常用属性 | 描述说明 ||
| required | 输入的数据是否为空,默认值为True。 ||
| widget | 设置HTML控件的样式。 ||
| label | 用于生成label标签的网页内容。initial:设置表单字段的初始值。 ||
| help_text | 设置帮助提示信息。 ||
| error_messages | 设置错误信息,以字典形式表示,比如{'required": '不能为空', 'invalid': '格式错误}。 ||
| show_hidden_initial | 参数值为True/False,是否在当前控件后面再加一个隐藏的且具有默认值的控件( 可用于检验两次输入的值是否一致)。 ||
| validators | 自定义数据验证规则。以列表格式表示,列表元素为函数名。localize:参数值为True/False,设置本地化,不同时区自动显示当地时间。disabled:参数值为True/False,HTML控件是否可以编辑。 ||
| label_suffix | 设置label 的后缀内容。 ||
参数widget
参数widget是一个forms.widgets对象,有4大类小部件:文本框类型,下拉框(复选框)类型,文件上传类型和复合框类型;
文本框类型
|---------------|---|---|
| 文本框类型 | 描述说明 ||
| TextInput | 对应CharField字段,文本框内容设置为文本格式。 ||
| NumberInput | 对应IntegerField字段,文本框内容只允许输入数值。 ||
| Emaillnput | 对应 EmailField字段,验证输入值是否为邮箱地址格式。 ||
| URLInput | 对应URLField字段,验证输入值是否为路由地址格式。 ||
| PasswordInput | 对应CharField字段,输入值以"*"显示。 ||
| HiddenInput | 对应CharField字段,隐藏文本框,不显示在网页上。 ||
| DateInput | 对应DateField字段,验证输入值是否为日期格式。 ||
| DateTimeInput | 对应DateTimeField字段,验证输入值是否为日期时间格式。 ||
| TimeInput | 对应TimeField字段,验证输入值是否为时间格式。 ||
| Textarea | 对应CharField字段,将文本框设为Textarea格式。 ||
下拉框(复选框)类型
|------------------------|---|---|
| 下拉框(复选框)类型 | 描述说明 ||
| CheckboxInput | 对应 BooleanField字段,设置复选框,选项为True和 False。 ||
| Select | 对应 ChoiceField字段,设置下拉框。 ||
| NullBooleanSelect | 对应NullBooleanField,设置复选框,选项为None、True和 False。 ||
| SelectMultiple | 对应ChoiceField字段,与Select类似,允许选择多个值。 ||
| RadioSelect | 对应ChoiceField字段,将数据列表设置为单选按钮。 ||
| CheckboxSelectMultiple | 对应ChoiceField字段,与SelectMultiple类似,设置为复选框列表。 ||
文件上传类型
|--------------------|---|---|
| 文件上传类型 | 描述说明 ||
| FileInput | 对应 FileField 或 ImageField字段。 ||
| ClearableFileInput | 对应 FileField 或ImageField字段,但多了复选框,允许清除上传的文件和图像。 ||
复合框类型
|---------------------------|---|---|
| 复合框类型 | 描述说明 ||
| MultipleHiddenInput | 隐藏一个或多个HTML的控件。 ||
| SplitDateTimeWidget | 组合使用Datelnput和 Timelnput。 ||
| SplitHiddenDateTimeWidget | 与SplitDateTimeWidget类似,但将控件隐藏,不显示在网页上。 ||
| SelectDateWidget | 组合使用3个Select,分别生成年、月、日的下拉框。 ||
优化表单实例
添加一个字段验证功能以及前端页面样式;
7、修改"helloWorld"应用下的"forms.py"配置文件中的 BookInfoForm 类内容,添加一个required=True 字段验证为必填项、添加placeholder提示、样式等;
# 导包
from django import forms
from django.forms import ModelForm, Form, widgets
from helloWorld.models import StudentInfo, BookTypeInfo
# 定义学生form表单StudentForm类,需要继承ModelForm父类
class StudentForm(ModelForm):
# 根据模板文件models.py需要嵌套一层Meta(Meta是一个配置中心)
class Meta:
# 需要获取数据,所有得定义导入model(数据是从StudentInfo里面获取到的)--就是导数据
model = StudentInfo
# 表单要显示的字段信息,__all__代表所有字段
# fields = '__all__'
# 指定字段
fields = ['name', 'age']
# 定义控件
widgets = {
'name': forms.TextInput(attrs={'id': 'name', 'class': 'inputClass'}),
'age': forms.NumberInput(attrs={'id': 'age', 'class': 'inputClass'}),
}
# 指定标签
labels = {
'name': '姓名',
'age': '年龄'
}
# 定义BookInfoForm类,需要继承Form(导包引入from django.forms import Form模块)
class BookInfoForm(Form):
"""
图书表单
"""
bookName = forms.CharField(
max_length=20,
label='图书名称',
required=True,
widget=widgets.TextInput(attrs={"placeholder": "请输入图书名称", "class": "inputCls"})
)
price = forms.FloatField(
label='图书价格',
required=True,
widget=forms.TextInput(attrs={'placeholder': '请输入图书价格', 'class': 'inputCls'})
)
publishDate = forms.DateField(label='出版日期')
# 获取图书类别列表(导包引入from helloWorld.models import BookTypeInfo)
bookTypeList = BookTypeInfo.objects.values()
# 图书类别以下拉框形式显示,下拉框选项id是图书类别Id,下拉框选项文本是图书类别名称
choices = [(v['id'], v['bookTypeName']) for v, v in enumerate(bookTypeList)]
# 定义bookType_id字段
bookType_id = forms.ChoiceField(choices=choices, label='图书类别')

8、修改项目"templates"目录"book"目录文件路径下"add2.html"文件内容,添加页面样式;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{ title }}</title>
<style>
.inputCls {
width: 200px;height:20px;
}
</style>
</head>
<body>
<h3>{{ title }}</h3>
<form action="/book/add" method="post">
{% csrf_token %}
<table>
{# 以table形式 #}
{{ form.as_table }}
<tr>
<td colspan="2">
<input type="submit" value="add2提交">
</td>
</tr>
</table>
</form>
</body>
</html>

9、运行项目,浏览器输入"http://127.0.0.1:8000/book/list"测试验证;

Django ModelForm 表单定义与使用
Django表单django.forms.ModelForm是在django.forms.Form的基础上结合模型所生成的模型表单,所有更加方便一些,但是也有一些注意点;模型表单是将模型字段转换成表单字段,由表单字段生成HTML控件,从而生成网页表单;ModelForm有9个属性,说明如下所示;
|------------------|---|---|
| ModelForm属性 | 描述说明 ||
| model | 必填项,必需属性,用于绑定Model对象。 ||
| fields | 可选属性,设置模型内哪些字段转换成表单字段,默认值为None,代表所有的模型字段,也可以将属性值设为'all',同样表示所有的模型字段;若只需部分模型字段,则将模型字段写入一个列表或元组里,再把该列表或元组作为属性值;一般情况下使用。 ||
| exclude | 可选属性,与fields 相反,禁止模型字段转换成表单字段。属性值以列表或元组表示,若设置了该属性,则属性fields 无须设置;特殊情况使用,有很多个字段其中某几个不用时可使用。 ||
| labels | 可选属性,设置表单字段的参数label,属性值以字典表示,字典的键为模型字段。 ||
| widgets | 可选属性,设置表单字段的参数 widget,属性值以字典表示,字典的键为模型字段。 ||
| localized_fields | 可选参数,将模型字段设为本地化的表单字段,常用于日期类型的模型字段。 ||
| field_classes | 可选属性,将模型字段重新定义,默认情况下,模型字段与表单字段遵从Django内置的转换规则。 ||
| help_texts | 可选属性,设置表单字段的参数help_text。提示性的帮助字段,显示不好看,一般不建议使用; ||
| error_messages | 可选属性,设置表单字段的参数error_messages。报错提示。 |||---------------------------|---|---|
| 模型字段转换表单字段遵从Django内置的规则进行转换,两者的转换规则如下所示: |||
| 模型字段类型 | 表单字段类型 ||
| AutoField | 不能转换表单字段。 ||
| BigAutoField | 不能转换表单字段。 ||
| BigIntegerField | IntegerField ||
| BinaryField | CharField ||
| BooleanField | BooleanField或者NullBooleanField ||
| CharField | CharField ||
| DateField | DateField ||
| DateTimeField | DateTimeField ||
| DecimalField | DecimalField ||
| EmailField | EmailField ||
| FileField | FileField ||
| FilePathField | FilePathField ||
| ForeignKey | ModelChoiceField ||
| ImageField | ImageField ||
| IntegerField | IntegerField ||
| IPAddressField | IPAddressField ||
| GenericlPAddressField | GenericIPAddressField ||
| ManyToManyField | ModelMultipleChoiceField ||
| NullBooleanField | NullBooleanField ||
| PositiveIntegerField | IntegerField ||
| PositiveSmallIntegerField | IntegerField ||
| SlugField | SlugField ||
| SmalllntegerField | IntegerField ||
| TextField | CharField ||
| TimeField | TimeField ||
| URLField | URLField ||
使用ModelForm来实现图书添加实例
10、修改"djangoProject"项目"helloWorld"应用下的"forms.py"配置文件内容,添加定义一个 BookInfoModelForm 类,需要继承 ModelForm,添加功能使用 ModelForm 来实现;
# 导包
from django import forms
from django.forms import ModelForm, Form, widgets
from helloWorld.models import StudentInfo, BookTypeInfo, BookInfo
# 定义学生form表单StudentForm类,需要继承ModelForm父类
class StudentForm(ModelForm):
# 根据模板文件models.py需要嵌套一层Meta(Meta是一个配置中心)
class Meta:
# 需要获取数据,所有得定义导入model(数据是从StudentInfo里面获取到的)--就是导数据
model = StudentInfo
# 表单要显示的字段信息,__all__代表所有字段
# fields = '__all__'
# 指定字段
fields = ['name', 'age']
# 定义控件
widgets = {
'name': forms.TextInput(attrs={'id': 'name', 'class': 'inputClass'}),
'age': forms.NumberInput(attrs={'id': 'age', 'class': 'inputClass'}),
}
# 指定标签
labels = {
'name': '姓名',
'age': '年龄'
}
# 定义BookInfoForm类,需要继承Form(导包引入from django.forms import Form模块)
class BookInfoForm(Form):
"""
图书表单
"""
bookName = forms.CharField(
max_length=20,
label='图书名称',
required=True,
widget=widgets.TextInput(attrs={"placeholder": "请输入图书名称", "class": "inputCls"})
)
price = forms.FloatField(
label='图书价格',
required=True,
widget=forms.TextInput(attrs={'placeholder': '请输入图书价格', 'class': 'inputCls'})
)
publishDate = forms.DateField(label='出版日期')
# 获取图书类别列表(导包引入from helloWorld.models import BookTypeInfo)
bookTypeList = BookTypeInfo.objects.values()
# 图书类别以下拉框形式显示,下拉框选项id是图书类别Id,下拉框选项文本是图书类别名称
choices = [(v['id'], v['bookTypeName']) for v, v in enumerate(bookTypeList)]
# 定义bookType_id字段
bookType_id = forms.ChoiceField(choices=choices, label='图书类别')
# 定义BookInfoModelForm类,需要继承ModelForm(导包引入from django.forms import ModelForm模块)
class BookInfoModelForm(ModelForm):
# 配置中心
class Meta:
# 导包引入from helloWorld.models import BookInfo
model = BookInfo
# 代表所有字段
fields = '__all__'
widgets = { # 定义控件
'bookName': forms.TextInput(attrs={"placeholder": "请输入图书名称", 'id': 'bookName', 'class': 'inputCls'})
}
labels = { # 指定标签
'bookName': '图书名称',
'price': '图书价格',
'publishDate': '出版日期',
'bookType': '图书类别'
}
help_texts = {
'bookName': 'help_text提示性的帮助字段,一般在开发过程中不建议使用,因为显示不好看不美观!'
}

11、在项目"templates"目录"book"目录文件路径下新建一个名称为"add3"的 HTML 文件或者复制一份"add2.html"文件并改名为"add3.html";
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{ title }}</title>
<style>
.inputCls {
width: 200px;height:20px;
}
</style>
</head>
<body>
<h3>{{ title }}</h3>
<form action="/book/add" method="post">
{% csrf_token %}
<table>
{# 以table形式 #}
{{ form.as_table }}
<tr>
<td colspan="2">
<input type="submit" value="add3提交">
</td>
</tr>
</table>
</form>
</body>
</html>

12、 修改"djangoProject"项目"helloWorld"应用下的"views.py"配置文件内容,新增定义一个 preAdd3 预处理方法;
# 导包
import datetime
import os
from django.core.paginator import Paginator
from django.db import connection, transaction
from django.db.backends.utils import CursorDebugWrapper
from django.db.models import Q, Sum, Avg, F
from django.http import HttpResponse, HttpResponseNotFound, JsonResponse, StreamingHttpResponse, FileResponse
from django.shortcuts import render, redirect
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from helloWorld.forms import StudentForm, BookInfoForm, BookInfoModelForm
from helloWorld.models import StudentInfo, BookInfo, BookTypeInfo, AccountInfo
# Create your views here.
# 定义类
class Person:
# 属性 姓名
name = None
# 属性 年龄
age = None
def __init__(self, name, age):
self.name = name
self.age = age
# 自定义一个index方法,里面要加上request请求对象
def index(request):
# 传递字符串类型数据,定义字符串类型变量str
str = 'hello world!'
# 日期对象date,导包引入import datetime
date = datetime.datetime.now()
# 定义字典类型数据
myDict = {"Jack":"11", "Queen":"12", "King":"13"}
# 创建一个对象,例如:zhangsan
zhangsan = Person("张三", 18)
# 定义列表myList、元组myTuple--列表和元组里面的元素类型没有限制,列表里面可以嵌套列表,元组里面可以嵌套元组
myList = [1, 'apple', 3.1415926, True, [5, 6], (7, 8), {9, 10}]
myTuple = (1, 'apple', 3.1415926, True, [5, 6], (7, 8), {9, 10})
# 变量content_value是字典格式
content_value = {"content_msg": str, "msg2": myDict, "msg3": zhangsan, "msg4": myList, "msg5": myTuple, "date": date}
# context= 传递的本身要求格式是一个字典类型
return render(request, 'index.html', context=content_value)
# 定义index_id方法,里面第一个参数request请求对象,第二个参数 id
def index_id(request, id):
# 判断 id是否为0
if id == 0:
# 导包引入redirect模块,如果id等于0的话,重定向到一个目标静态地址
return redirect("/static/error.html")
else:
# 使用HttpResponse直接返回内容--因为id是int类型,里面得使用str()进行转换
return HttpResponse("id是" + str(id) + "的某某平台系统页面")
# 定义index_id方法,第一个参数request请求对象,里面继续跟多个路由变量"year, month, day, id"
def index_id02(request, year, month, day, id):
# 使用HttpResponse直接返回内容--因为id是int类型,里面得使用str()进行转换
return HttpResponse(str(year) + "/" + str(month) + "/" + str(day) + "/" +"id是" + str(id) + "的某某平台系统页面")
# 定义index_id方法,第一个参数request请求对象,后面跟多个路由变量"year, month, day"--注意,使用正则后不能与"id"再混合使用了
def index_id03(request, year, month, day):
# 使用HttpResponse直接返回内容--因为id是int类型,里面得使用str()进行转换
return HttpResponse(str(year) + '/' + str(month) + '/' + str(day) + "使用正则路由后的某某平台系统页面")
# 定义目标文件路径
file_path = "D:\\works\\djangoProject\\datas\\IDM.rar"
# 定义download_file1下载方法,请求需要带上request(所有的请求都要带上request)
def download_file1(request):
# 定义文件--使用open函数方法以"rb"二进制读取模式打开文件--然后返回一个file文件对象
file = open(file_path, 'rb')
# 创建构造好了HttpResponse对象之后返回给response
response = HttpResponse(file)
# 指定文件类型
response['Content-Type'] = 'application/rar-compressed'
# 扩展--指定附件及文件名字(可以动态拼接filename,并不建议使用原始名称)
response['Content-Disposition'] = 'attachment; filename=file1.rar'
return response
def download_file2(request):
file = open(file_path, 'rb')
# 创建构造好了StreamingHttpResponse对象之后返回给response
response = StreamingHttpResponse(file)
response['Content-Type'] = 'application/rar-compressed'
response['Content-Disposition'] = 'attachment; filename=file2.rar'
return response
def download_file3(request):
file = open(file_path, 'rb')
# 创建构造好了FileResponse对象之后返回给response
response = FileResponse(file)
response['Content-Type'] = 'application/rar-compressed'
response['Content-Disposition'] = 'attachment; filename=file3.rar'
return response
def get_test(request):
"""
get 请求测试
:param request:
:return:
"""
print(f'打印请求方式request.method:{request.method}')
print(f'打印常用属性request.content_type:{request.content_type}')
print(f'打印常用属性request.content_params:{request.content_params}')
print(f'打印常用属性request.COOKIES:{request.COOKIES}')
print(f'打印常用属性request.scheme:{request.scheme}')
print(f'打印常用方法request.is_secure:{request.is_secure()}')
print(f'打印常用方法request.get_host:{request.get_host()}')
print(f'打印常用方法request.get_full_path:{request.get_full_path()}')
print(f'使用request.GET.get("name")方法获取对应key值参数:{request.GET.get("name")}' )
print(f'使用request.GET.get("pwd")方法获取对应key值参数:{request.GET.get("pwd")}')
print(f'使用request.GET.get("dev")方法获取不存在的参数,返回值是'None':{request.GET.get("dev")}')
print(f'request.GET.get("prod", "555")获取的prod参数是不存在的,设置返回值'555':{request.GET.get("prod", "555")}')
return HttpResponse("hello world!http get ok!")
def post_test(request):
"""
post 请求测试方法
:param request:
:return:
"""
print(f'打印请求方式request.method:{request.method}')
print(f'使用request.POST.get("name")方法获取对应key值参数:{request.POST.get("name")}')
print(f'使用request.POST.get("pwd")方法获取对应key值参数:{request.POST.get("pwd")}')
print(f'使用request.POST.get("dev")方法获取不存在的参数,返回值是'None':{request.POST.get("dev")}')
print(f'request.POST.get("prod", "556")获取的prod参数是不存在的,设置返回值'556':{request.POST.get("prod", "556")}')
return HttpResponse("hello world!http post ok!")
def to_login(request):
"""
跳转到登录页面
:param request:
:return:
"""
# 跳转到登录页面可直接使用渲染render()方法
return render(request, 'login.html')
def login(request):
"""
登录到主页面
:param request:
:return:
"""
# 首先获取用户名和密码
user_name = request.POST.get('user_name')
pwd = request.POST.get('pwd')
# 模拟用户登录(暂不连接数据库)
if user_name == "python3" and pwd == "123456":
# session中存一个用户名
request.session['currentUserName'] = user_name
print(f'代码脚本中session获取:{request.session['currentUserName']}')
# 获取HttpResponse
response = render(request, 'main.html')
# 通过response.set_Cookie()方法来设置cookie
response.set_cookie('remember_me', True)
return response
else:
# 定义变量content_value为报错提示信息,里面是键值对格式的对象
content_value = {"error_info":"用户名或者密码错误!"}
return render(request, 'login.html', context=content_value)
def to_upload(request):
"""
跳转到上传文件页面
:param request:
:return:
"""
return render(request, 'upload.html')
def upload(request):
"""
上传文件处理
:param request:
:return:
"""
# 获取上传的文件,如果没有文件,就默认为None(如果不写指定的None,也是默认None)
myFile = request.FILES.get("myfile", None)
# 判断如果是有myFile的情况,就是说明有文件
if myFile:
# 打开特定的文件进行二进制的写操作--上传文件路径烦请自行创建(例如:在项目datas路径下新建"myFile"目录文件)
f = open(os.path.join("D:\\works\\djangoProject\\datas\\myFile", myFile.name), "wb+")
# 分块写入文件
for chunk in myFile.chunks():
f.write(chunk)
f.close()
return HttpResponse("文件上传成功!")
else:
return HttpResponse("没有发现可以上传的文件!")
# 定义List类,继续继承ListView(导包引入 from django.views.generic import ListView)
class List(ListView):
# 设置模版文件
template_name = 'student/list.html'
# 设置模型外的数据--例如:指定标题
extra_context = {'title': '学生信息列表'}
# 定义数据集(查询结果集)--StudentInfo(导入 from helloWorld.models import StudentInfo)
queryset = StudentInfo.objects.all()
# 每页展示数据的条数(自行设置)
paginate_by = 5
# 设置上下文对象名称(其实就是一个列表的对象),建议使用自定义名称,不然就默认为 objectlist
context_object_name = 'student_list'
# 定义Detail类,继续继承DetailView(导包引入 from django.views.generic import DetailView)
class Detail(DetailView):
# 设置模版文件
template_name = 'student/detail.html'
# 设置模型外的数据--例如:指定标题
extra_context = {'title': '学生信息详情'}
# 设置查询模型--指定model(不然无法查询数据)
model = StudentInfo
# 设置上下文对象名称
context_object_name = 'student'
# 指定URL中用于获取对象的唯一标识符的参数名称,默认为'pk';
# 如若在urls.py文件中自定义了名称,则需要添加对应pk_url_kwarg参数为自定义名称
# pk_url_kwarg = 'id'
# 定义Create类,继续继承CreateView(导包引入 from django.views.generic import CreateView)
class Create(CreateView):
# 设置模版文件,名称修改为create(有的名称叫"add"也行)
template_name = 'student/create.html'
# 设置模型外的数据--例如:指定标题"添加"或者"新增"都行
extra_context = {'title': '学生信息新增'}
# 因为是表单,所以需要使用form_class参数来指定form表单
form_class = StudentForm
# 执行成功后跳转地址(跳转地址可以是一个新的页面,也可以进入一个列表页面)
success_url = '/student/list'
# 定义Update类,继续继承UpdateView(导包引入 from django.views.generic import UpdateView)
class Update(UpdateView):
# 设置模版文件,名称修改为update(自定义)
template_name = 'student/update.html'
# 设置模型外的数据--例如:指定标题"编辑"或者"修改"都行
extra_context = {'title': '学生信息编辑'}
# 设置查询模型--指定model(不然无法查询数据)--传来一个组件"pk"会自动的去查数据、封装对象、显示到页面上
model = StudentInfo
# 因为是表单,所以需要使用form_class参数来指定form表单
form_class = StudentForm
# 执行成功后跳转地址(跳转地址可以是一个新的页面,也可以进入一个列表页面)
success_url = '/student/list'
# 定义Delete类,继续继承DeleteView(导包引入 from django.views.generic import DeleteView)
class Delete(DeleteView):
# 设置模版文件,名称修改为delete(自定义)
template_name = 'student/delete.html'
# 设置模型外的数据--例如:指定标题"删除"或者"修改"都行
extra_context = {'title': '学生信息删除'}
# 设置上下文对象名称
context_object_name = 'student'
# 设置查询模型--指定model(不然无法查询数据)--传来一个组件"pk"会自动的去查数据、封装对象、显示到页面上
model = StudentInfo
# 执行成功后跳转地址(跳转地址可以是一个新的页面,也可以进入一个列表页面)
success_url = '/student/list'
# 自定义跳转子类模板页面方法
def to_course(request):
"""
跳转到子类模板页面
:param request:
:return:
"""
return render(request, 'course.html')
# 定义bookList方法
def bookList(request):
"""
图书列表查询
"""
# 查询显示所有数据
bookList = BookInfo.objects.all()
# 使用BookInfo.objects.extra()方法,里面使用where加参数(元组格式),在params加上具体的参数,也是一个元组
# 如果是模型外的参数(models.py里面没有定义的参数),可以使用BookInfo.objects.select()方法
# bookList = BookInfo.objects.extra(where=["price>%s"], params=[90])
# # 使用BookInfo.objects.raw()方法,直接写一个SQL语句
# bookList = BookInfo.objects.raw('SELECT * FROM t_book where price>%s', params=[90])
# 使用connection方法需要导包引入from django.db import connection模块,返回一个cursor游标
# 指定cursor游标为CursorDebugWrapper类型,需导包引入from django.db.backends.utils import CursorDebugWrapper模块
cursor: CursorDebugWrapper = connection.cursor()
# 然后就可以执行使用execute方法,查询图书价格大于90的记录数
# cursor.execute('select count(*) from t_book where price>90')
# print('预计提取的一个结果是(2,):', cursor.fetchone())
content_value = {"title": "图书列表", "bookList": bookList}
return render(request, 'book/list.html', context=content_value)
def bookList2(request):
"""
多表查询,有正向查询和反向查询
:param request:
:return:
"""
# 正向查询(经常使用)--多的一端BookInfo条件id=2且只查询一条数据,返回图书book并指定类型BookInfo
book: BookInfo = BookInfo.objects.filter(id=2).first()
# 能够输出book的所有信息,也能输出所关联的bookType.bookTypeName数据信息
print(book.bookName, book.bookType.bookTypeName)
# 反向查询(很少使用)--(导包引入from helloWorld.models import BookTypeInfo)
# 一的一端BookTypeInfo条件id=1且只查询一条数据,返回图书类别bookType并指定类型BookTypeInfo
bookType: BookTypeInfo = BookTypeInfo.objects.filter(id=1).first()
# 输出读取关联的这个图书类别下的所有图书的数据集中第一条数据,固定语法bookinfo_set
print(bookType.bookinfo_set.first().bookName)
# 显示这个类别下的所有数据,返回的结果是一个QuerySet数据集,这个类型下有两条数据,
print(bookType.bookinfo_set.all())
# 把QuerySet这个数据集转发到页面上就可以进行遍历了
content_value = {"title": "图书列表", "bookList": bookType.bookinfo_set.all()}
return render(request, 'book/list.html', context=content_value)
def preAdd(request):
"""
预处理,添加操作
:param request:
:return:
"""
# 预处理操作需要先找到所有数据
bookTypeList = BookTypeInfo.objects.all()
print(f'可自行打印查看bookTypeList是否查询出所有数据:{bookTypeList}')
# 预处理操作之后要返回到图书添加页面
content_value = {"title": "图书添加", "bookTypeList": bookTypeList}
return render(request, 'book/add.html', context=content_value)
def preAdd2(request):
"""
预处理,添加操作 使用form表单
:param request:
:return:
"""
# 导包引入from helloWorld.forms import BookInfoForm
form = BookInfoForm()
context_value = {"title": "图书添加2", "form": form}
return render(request, 'book/add2.html', context_value)
def preAdd3(request):
"""
预处理,添加操作 使用ModelForm表单
:param request:
:return:
"""
# 导包引入from helloWorld.forms import BookInfoModelForm
form = BookInfoModelForm()
context_value = {"title": "图书添加3", "form": form}
return render(request, 'book/add3.html', context_value)
def add(request):
"""
图书添加
:param request:
:return:
"""
# 封装对象
book = BookInfo()
# 添加数据
book.bookName = request.POST.get("bookName")
book.publishDate = request.POST.get("publishDate")
book.bookType_id = request.POST.get("bookType_id")
book.price = request.POST.get("price")
# 因为是模板,所以直接使用save()方法
book.save()
# 数据添加完成之后,可以获取新增数据的主键id
print(f'数据添加完成之后,获取新增数据的主键id:{book.id}')
# 添加完成之后,需要返回到列表,直接return返回那个对象
return bookList(request)
# 修改预处理,定义修改操作方法;页面需要传一个id来确定是要修改哪一条记录
def preUpdate(request, id):
"""
预处理,修改操作
:param request:
:return:
"""
print("通过path里面的变量,页面需要传一个id来确定是要修改哪一条记录:", id)
# 根据传过来的id去查询数据,并返回一个book对象
book = BookInfo.objects.get(id=id)
print(f'可自行打印查看返回的book对象:{book}')
bookTypeList = BookTypeInfo.objects.all()
print(bookTypeList)
# book对象也需要传到页面上去,到时候页面上就能够取值了
content_value = {"title": "图书修改", "bookTypeList": bookTypeList, "book": book}
return render(request, 'book/edit.html', context=content_value)
def update(request):
"""
图书修改
:param request:
:return:
"""
book = BookInfo()
book.id = request.POST.get("id")
book.bookName = request.POST.get("bookName")
book.publishDate = request.POST.get("publishDate")
book.bookType_id = request.POST.get("bookType_id")
book.price = request.POST.get("price")
book.save()
return bookList(request)
def delete(request, id):
"""
图书删除
:param request:
:return:
"""
# 删除指定id数据
BookInfo.objects.get(id=id).delete()
# 根据条件删除多条数据
# BookInfo.objects.filter(price__gte=100).delete()
# 删除所有数据
# BookInfo.objects.all().delete()
return bookList(request)
# 定义转账transfer方法--导包引入from django.db import transaction模块使用事务
@transaction.atomic
def transfer(request):
"""
模拟转账
:param request:
:return:
"""
# 开启事务,
sid = transaction.savepoint()
# 添加异常处理机制
try:
# 导包引入from helloWorld.models import AccountInfo方法
# AccountInfo.objects.filter()方法查找A用户
a1 = AccountInfo.objects.filter(user='A用户_赵大')
# 导包引入from django.db.models import F 模块
a1.update(account = F('account') + 100)
# 这个"'A用户_赵大'"账户是加account余额,那么对应的"'B 用户_ 钱二'"账户就是减account余额
b2 = AccountInfo.objects.filter(user='B用户_钱二')
# 修改操作
b2.update(account = F('account') - 100 / 0)
# 提交事务,如果不设置,当程序执行完成后,会自动提交事务
transaction.savepoint_commit(sid)
except Exception as e:
print(f'异常信息为:{e}')
# 事务回滚--传一个sid表示需要事务回滚哪一个事务
transaction.savepoint_rollback(sid)
# 执行完两个操作之后,模拟转账完成,返回到界面
return HttpResponse("OK!转账完成!")

13、修改"djangoProject"应用下的"urls.py"配置文件内容,配置定义映射关系;
"""
URL configuration for djangoProject project.
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/5.0/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.conf import settings
from django.contrib import admin
from django.urls import path, re_path, include
from django.views.generic import RedirectView
from django.views.static import serve
import helloWorld.views
urlpatterns = [
path('admin/', admin.site.urls),
# 可直接复制上面"admin",把"admin"修改为"index";后面是跟的"处理函数helloWorld.views.index"
# 是helloWorld应用的views里面的index()--引入helloWorld.views,记得index后不能加小括号,因为传入的是对象;
path('index/', helloWorld.views.index, name='index'),
# 要是请求"redirectTo"这个请求的话给重定向到"index"--重定向使用RedirectView.as_view(),里面使用关键词参数url
path('redirectTo', RedirectView.as_view(url="index/")),
# 自定义名称,例如叫"index_id/",后面再跟上某网页id;<int:id> int指定类型,路由变量为id;对应对应的路由映射
path('index_id/<int:id>', helloWorld.views.index_id),
# 多个路由变量
path('index_id02/<int:year>/<int:month>/<int:day>/<int:id>', helloWorld.views.index_id02),
# 正则表达式匹配是必须使用re_path方法,正则表达式"?P"开头是固定格式
re_path('index_id03/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})', helloWorld.views.index_id03),
# 配置媒体文件的路由地址
re_path('media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}, name='media'),
# 命名空间namespace--比如说是"user/"开头的,第一个参数include(),第二个参数namespace
# 导包引入include模块,include()里面也有两个参数,第一个是指定user的urls,第二个是项目名;
path('user/', include(('user.urls', 'user'), namespace='user')),
path('order/', include(('order.urls', 'order'), namespace='order')),
# 配置 download_file
path('download1/', helloWorld.views.download_file1),
path('download2/', helloWorld.views.download_file2),
path('download3/', helloWorld.views.download_file3),
path('get', helloWorld.views.get_test),
path('post', helloWorld.views.post_test),
path('toLogin/', helloWorld.views.to_login),
path('login', helloWorld.views.login),
path('toUpload/', helloWorld.views.to_upload),
path('upload', helloWorld.views.upload),
path('student/list', helloWorld.views.List.as_view()),
# 默认名称就是"pk",建议不要更改,在开发过程中就默认为pk即可
path('student/<int:pk>', helloWorld.views.Detail.as_view()),
# 如果把默认名称"pk"更改为其他(例如:id),则需要在urls.py配置文件Detail类中添加pk_url_kwarg参数
# path('student/<int:id>', helloWorld.views.Detail.as_view())
path('student/create', helloWorld.views.Create.as_view()),
path('student/update/<int:pk>', helloWorld.views.Update.as_view()),
path('student/delete/<int:pk>', helloWorld.views.Delete.as_view()),
path('toCourse/', helloWorld.views.to_course),
path('book/list', helloWorld.views.bookList),
path('book/list2', helloWorld.views.bookList2),
path('book/preAdd', helloWorld.views.preAdd),
path('book/add', helloWorld.views.add),
path('book/preUpdate/<int:id>', helloWorld.views.preUpdate),
path('book/delete/<int:id>', helloWorld.views.delete),
path('book/update', helloWorld.views.update),
path('transfer/', helloWorld.views.transfer),
path('book/preAdd2', helloWorld.views.preAdd2),
path('book/preAdd3', helloWorld.views.preAdd3)
]

14、修改项目"templates"目录"book"目录文件路径下的"list.html"的文件内容,新增一个"图书添加 3"preAdd3 链接;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{ title }}</title>
</head>
<body>
<h3>{{ title }}</h3>
<a href="/book/preAdd">图书添加</a><br/><br/>
<a href="/book/preAdd2">图书添加2(使用Form)</a><br/><br/>
<a href="/book/preAdd3">图书添加3(使用ModelForm)</a><br/><br/>
<table border="1">
<tr>
<th>编号</th>
<th>图书名称</th>
<th>图书价格</th>
<th>出版日期</th>
<th>图书类别</th>
<th>更多操作</th>
</tr>
{% for book in bookList %}
<tr>
<td>{{ book.id }}</td>
<td>{{ book.bookName }}</td>
<td>{{ book.price }}</td>
<td>{{ book.publishDate | date:'Y-m-d' }}</td>
<td>{{ book.bookType.bookTypeName }}</td>
<td>
<a href="/book/preUpdate/{{ book.id }}">图书修改</a>
<a href="/book/delete/{{ book.id }}">图书删除</a>
</td>
</tr>
{% endfor %}
</table>
</body>
</html>

15、重新运行项目,浏览器输入"http://127.0.0.1:8000/book/list"测试验证,可以看到图书类别的下拉框选择文本选项显示有误;

16、 使用浏览器开发者工具(快捷键 F12),审查元素查看,可以发现 value 值是与数据库表中的一致是默认取的图书类型的主键id 是没有问题的,但是选项文本取值就不对,它默认打印的是对象,这样是不对的,需要进一步优化代码;

17、通过使用魔法方法__str__来实现,重写一下打印"bookTypeName"就行了,默认打印对象输出图书类别名称,修改"helloWorld"应用下的"models.py"配置文件内容中的 BookTypeInfo 类;
from django.db import models
# Create your models here.
class StudentInfo(models.Model):
# 新建3个字段,第一个字段是 主键id
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=20)
age = models.IntegerField()
# 默认会有个StudentInfo表,但是不好;所以务必通过Meta属性db_table来指定表名
class Meta:
db_table = 't_student'
# 先创建BookTypeInfo图书类型类,需要继承于models.Model
class BookTypeInfo(models.Model):
# 定义字段,首先定义id是自增主键
id = models.AutoField(primary_key=True)
# 再定义一个bookTypeName字段是CharField类型并指定最大长度20
bookTypeName = models.CharField(max_length=20)
# 定义Meta
class Meta:
# 指定表名
db_table = "t_bookType"
# 给模型取个直观的名字,主要是在Django5内置的admin系统里面给它娶个名字,以便管理
verbose_name = "图书类别信息"
# 使用魔法方法__str__来重写一下打印"bookTypeName",默认打印对象输出图书类别名称
def __str__(self):
return self.bookTypeName
# 定义BookInfo类,依然是继承models.Model
class BookInfo(models.Model):
id = models.AutoField(primary_key=True)
# 图书名称
bookName = models.CharField(max_length=20)
# 图书价格
price = models.FloatField()
# 出版日期
publishDate = models.DateField()
# 定义图书和图书类别的外键关联--bookType对象通过外键值取到图书类别的一个数据对象的封装
bookType = models.ForeignKey(BookTypeInfo, on_delete=models.PROTECT)
class Meta:
db_table = "t_book"
verbose_name = "图书信息"
# 定义AccountInfo类,继承models.Model
class AccountInfo(models.Model):
# id字段可定义亦可不定义
id = models.AutoField(primary_key=True)
# 定义user用户
user = models.CharField(max_length=20)
# 定义某个用户的账户余额
account = models.FloatField()
class Meta:
# 数据库表名
db_table = "t_account"
# 给模型取个直观的名字
verbose_name = "用户账户信息"

18、重新运行项目,浏览器输入"http://127.0.0.1:8000/book/list"测试验证,可以看到图书类别的下拉框选择文本选项显示已经正确了;

注意点
需要注意一个细节,在默认转换的时候,下拉选择框的name是bookType,所以要修改下views.py里的add方法,把接收时的"bookType_id"更改为"bookType";如若更改成功,则只有图书添加3(使用ModelForm)能够添加成功,而 图书添加 和 图书添加2(使用Form) 则不会添加成功,都会出现报错"( "Column 'bookType_id' cannot be null"--"列'bookType_id'不能为空")"提示;
19、修改"djangoProject"项目"helloWorld"应用下的"views.py"配置文件内容中 add 方法里面的book.bookType_id = request.POST.get("bookType_id")更改为 book.bookType_id = request.POST.get("bookType")
# 导包
import datetime
import os
from django.core.paginator import Paginator
from django.db import connection, transaction
from django.db.backends.utils import CursorDebugWrapper
from django.db.models import Q, Sum, Avg, F
from django.http import HttpResponse, HttpResponseNotFound, JsonResponse, StreamingHttpResponse, FileResponse
from django.shortcuts import render, redirect
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from helloWorld.forms import StudentForm, BookInfoForm, BookInfoModelForm
from helloWorld.models import StudentInfo, BookInfo, BookTypeInfo, AccountInfo
# Create your views here.
# 定义类
class Person:
# 属性 姓名
name = None
# 属性 年龄
age = None
def __init__(self, name, age):
self.name = name
self.age = age
# 自定义一个index方法,里面要加上request请求对象
def index(request):
# 传递字符串类型数据,定义字符串类型变量str
str = 'hello world!'
# 日期对象date,导包引入import datetime
date = datetime.datetime.now()
# 定义字典类型数据
myDict = {"Jack":"11", "Queen":"12", "King":"13"}
# 创建一个对象,例如:zhangsan
zhangsan = Person("张三", 18)
# 定义列表myList、元组myTuple--列表和元组里面的元素类型没有限制,列表里面可以嵌套列表,元组里面可以嵌套元组
myList = [1, 'apple', 3.1415926, True, [5, 6], (7, 8), {9, 10}]
myTuple = (1, 'apple', 3.1415926, True, [5, 6], (7, 8), {9, 10})
# 变量content_value是字典格式
content_value = {"content_msg": str, "msg2": myDict, "msg3": zhangsan, "msg4": myList, "msg5": myTuple, "date": date}
# context= 传递的本身要求格式是一个字典类型
return render(request, 'index.html', context=content_value)
# 定义index_id方法,里面第一个参数request请求对象,第二个参数 id
def index_id(request, id):
# 判断 id是否为0
if id == 0:
# 导包引入redirect模块,如果id等于0的话,重定向到一个目标静态地址
return redirect("/static/error.html")
else:
# 使用HttpResponse直接返回内容--因为id是int类型,里面得使用str()进行转换
return HttpResponse("id是" + str(id) + "的某某平台系统页面")
# 定义index_id方法,第一个参数request请求对象,里面继续跟多个路由变量"year, month, day, id"
def index_id02(request, year, month, day, id):
# 使用HttpResponse直接返回内容--因为id是int类型,里面得使用str()进行转换
return HttpResponse(str(year) + "/" + str(month) + "/" + str(day) + "/" +"id是" + str(id) + "的某某平台系统页面")
# 定义index_id方法,第一个参数request请求对象,后面跟多个路由变量"year, month, day"--注意,使用正则后不能与"id"再混合使用了
def index_id03(request, year, month, day):
# 使用HttpResponse直接返回内容--因为id是int类型,里面得使用str()进行转换
return HttpResponse(str(year) + '/' + str(month) + '/' + str(day) + "使用正则路由后的某某平台系统页面")
# 定义目标文件路径
file_path = "D:\\works\\djangoProject\\datas\\IDM.rar"
# 定义download_file1下载方法,请求需要带上request(所有的请求都要带上request)
def download_file1(request):
# 定义文件--使用open函数方法以"rb"二进制读取模式打开文件--然后返回一个file文件对象
file = open(file_path, 'rb')
# 创建构造好了HttpResponse对象之后返回给response
response = HttpResponse(file)
# 指定文件类型
response['Content-Type'] = 'application/rar-compressed'
# 扩展--指定附件及文件名字(可以动态拼接filename,并不建议使用原始名称)
response['Content-Disposition'] = 'attachment; filename=file1.rar'
return response
def download_file2(request):
file = open(file_path, 'rb')
# 创建构造好了StreamingHttpResponse对象之后返回给response
response = StreamingHttpResponse(file)
response['Content-Type'] = 'application/rar-compressed'
response['Content-Disposition'] = 'attachment; filename=file2.rar'
return response
def download_file3(request):
file = open(file_path, 'rb')
# 创建构造好了FileResponse对象之后返回给response
response = FileResponse(file)
response['Content-Type'] = 'application/rar-compressed'
response['Content-Disposition'] = 'attachment; filename=file3.rar'
return response
def get_test(request):
"""
get 请求测试
:param request:
:return:
"""
print(f'打印请求方式request.method:{request.method}')
print(f'打印常用属性request.content_type:{request.content_type}')
print(f'打印常用属性request.content_params:{request.content_params}')
print(f'打印常用属性request.COOKIES:{request.COOKIES}')
print(f'打印常用属性request.scheme:{request.scheme}')
print(f'打印常用方法request.is_secure:{request.is_secure()}')
print(f'打印常用方法request.get_host:{request.get_host()}')
print(f'打印常用方法request.get_full_path:{request.get_full_path()}')
print(f'使用request.GET.get("name")方法获取对应key值参数:{request.GET.get("name")}' )
print(f'使用request.GET.get("pwd")方法获取对应key值参数:{request.GET.get("pwd")}')
print(f'使用request.GET.get("dev")方法获取不存在的参数,返回值是'None':{request.GET.get("dev")}')
print(f'request.GET.get("prod", "555")获取的prod参数是不存在的,设置返回值'555':{request.GET.get("prod", "555")}')
return HttpResponse("hello world!http get ok!")
def post_test(request):
"""
post 请求测试方法
:param request:
:return:
"""
print(f'打印请求方式request.method:{request.method}')
print(f'使用request.POST.get("name")方法获取对应key值参数:{request.POST.get("name")}')
print(f'使用request.POST.get("pwd")方法获取对应key值参数:{request.POST.get("pwd")}')
print(f'使用request.POST.get("dev")方法获取不存在的参数,返回值是'None':{request.POST.get("dev")}')
print(f'request.POST.get("prod", "556")获取的prod参数是不存在的,设置返回值'556':{request.POST.get("prod", "556")}')
return HttpResponse("hello world!http post ok!")
def to_login(request):
"""
跳转到登录页面
:param request:
:return:
"""
# 跳转到登录页面可直接使用渲染render()方法
return render(request, 'login.html')
def login(request):
"""
登录到主页面
:param request:
:return:
"""
# 首先获取用户名和密码
user_name = request.POST.get('user_name')
pwd = request.POST.get('pwd')
# 模拟用户登录(暂不连接数据库)
if user_name == "python3" and pwd == "123456":
# session中存一个用户名
request.session['currentUserName'] = user_name
print(f'代码脚本中session获取:{request.session['currentUserName']}')
# 获取HttpResponse
response = render(request, 'main.html')
# 通过response.set_Cookie()方法来设置cookie
response.set_cookie('remember_me', True)
return response
else:
# 定义变量content_value为报错提示信息,里面是键值对格式的对象
content_value = {"error_info":"用户名或者密码错误!"}
return render(request, 'login.html', context=content_value)
def to_upload(request):
"""
跳转到上传文件页面
:param request:
:return:
"""
return render(request, 'upload.html')
def upload(request):
"""
上传文件处理
:param request:
:return:
"""
# 获取上传的文件,如果没有文件,就默认为None(如果不写指定的None,也是默认None)
myFile = request.FILES.get("myfile", None)
# 判断如果是有myFile的情况,就是说明有文件
if myFile:
# 打开特定的文件进行二进制的写操作--上传文件路径烦请自行创建(例如:在项目datas路径下新建"myFile"目录文件)
f = open(os.path.join("D:\\works\\djangoProject\\datas\\myFile", myFile.name), "wb+")
# 分块写入文件
for chunk in myFile.chunks():
f.write(chunk)
f.close()
return HttpResponse("文件上传成功!")
else:
return HttpResponse("没有发现可以上传的文件!")
# 定义List类,继续继承ListView(导包引入 from django.views.generic import ListView)
class List(ListView):
# 设置模版文件
template_name = 'student/list.html'
# 设置模型外的数据--例如:指定标题
extra_context = {'title': '学生信息列表'}
# 定义数据集(查询结果集)--StudentInfo(导入 from helloWorld.models import StudentInfo)
queryset = StudentInfo.objects.all()
# 每页展示数据的条数(自行设置)
paginate_by = 5
# 设置上下文对象名称(其实就是一个列表的对象),建议使用自定义名称,不然就默认为 objectlist
context_object_name = 'student_list'
# 定义Detail类,继续继承DetailView(导包引入 from django.views.generic import DetailView)
class Detail(DetailView):
# 设置模版文件
template_name = 'student/detail.html'
# 设置模型外的数据--例如:指定标题
extra_context = {'title': '学生信息详情'}
# 设置查询模型--指定model(不然无法查询数据)
model = StudentInfo
# 设置上下文对象名称
context_object_name = 'student'
# 指定URL中用于获取对象的唯一标识符的参数名称,默认为'pk';
# 如若在urls.py文件中自定义了名称,则需要添加对应pk_url_kwarg参数为自定义名称
# pk_url_kwarg = 'id'
# 定义Create类,继续继承CreateView(导包引入 from django.views.generic import CreateView)
class Create(CreateView):
# 设置模版文件,名称修改为create(有的名称叫"add"也行)
template_name = 'student/create.html'
# 设置模型外的数据--例如:指定标题"添加"或者"新增"都行
extra_context = {'title': '学生信息新增'}
# 因为是表单,所以需要使用form_class参数来指定form表单
form_class = StudentForm
# 执行成功后跳转地址(跳转地址可以是一个新的页面,也可以进入一个列表页面)
success_url = '/student/list'
# 定义Update类,继续继承UpdateView(导包引入 from django.views.generic import UpdateView)
class Update(UpdateView):
# 设置模版文件,名称修改为update(自定义)
template_name = 'student/update.html'
# 设置模型外的数据--例如:指定标题"编辑"或者"修改"都行
extra_context = {'title': '学生信息编辑'}
# 设置查询模型--指定model(不然无法查询数据)--传来一个组件"pk"会自动的去查数据、封装对象、显示到页面上
model = StudentInfo
# 因为是表单,所以需要使用form_class参数来指定form表单
form_class = StudentForm
# 执行成功后跳转地址(跳转地址可以是一个新的页面,也可以进入一个列表页面)
success_url = '/student/list'
# 定义Delete类,继续继承DeleteView(导包引入 from django.views.generic import DeleteView)
class Delete(DeleteView):
# 设置模版文件,名称修改为delete(自定义)
template_name = 'student/delete.html'
# 设置模型外的数据--例如:指定标题"删除"或者"修改"都行
extra_context = {'title': '学生信息删除'}
# 设置上下文对象名称
context_object_name = 'student'
# 设置查询模型--指定model(不然无法查询数据)--传来一个组件"pk"会自动的去查数据、封装对象、显示到页面上
model = StudentInfo
# 执行成功后跳转地址(跳转地址可以是一个新的页面,也可以进入一个列表页面)
success_url = '/student/list'
# 自定义跳转子类模板页面方法
def to_course(request):
"""
跳转到子类模板页面
:param request:
:return:
"""
return render(request, 'course.html')
# 定义bookList方法
def bookList(request):
"""
图书列表查询
"""
# 查询显示所有数据
bookList = BookInfo.objects.all()
# 使用BookInfo.objects.extra()方法,里面使用where加参数(元组格式),在params加上具体的参数,也是一个元组
# 如果是模型外的参数(models.py里面没有定义的参数),可以使用BookInfo.objects.select()方法
# bookList = BookInfo.objects.extra(where=["price>%s"], params=[90])
# # 使用BookInfo.objects.raw()方法,直接写一个SQL语句
# bookList = BookInfo.objects.raw('SELECT * FROM t_book where price>%s', params=[90])
# 使用connection方法需要导包引入from django.db import connection模块,返回一个cursor游标
# 指定cursor游标为CursorDebugWrapper类型,需导包引入from django.db.backends.utils import CursorDebugWrapper模块
cursor: CursorDebugWrapper = connection.cursor()
# 然后就可以执行使用execute方法,查询图书价格大于90的记录数
# cursor.execute('select count(*) from t_book where price>90')
# print('预计提取的一个结果是(2,):', cursor.fetchone())
content_value = {"title": "图书列表", "bookList": bookList}
return render(request, 'book/list.html', context=content_value)
def bookList2(request):
"""
多表查询,有正向查询和反向查询
:param request:
:return:
"""
# 正向查询(经常使用)--多的一端BookInfo条件id=2且只查询一条数据,返回图书book并指定类型BookInfo
book: BookInfo = BookInfo.objects.filter(id=2).first()
# 能够输出book的所有信息,也能输出所关联的bookType.bookTypeName数据信息
print(book.bookName, book.bookType.bookTypeName)
# 反向查询(很少使用)--(导包引入from helloWorld.models import BookTypeInfo)
# 一的一端BookTypeInfo条件id=1且只查询一条数据,返回图书类别bookType并指定类型BookTypeInfo
bookType: BookTypeInfo = BookTypeInfo.objects.filter(id=1).first()
# 输出读取关联的这个图书类别下的所有图书的数据集中第一条数据,固定语法bookinfo_set
print(bookType.bookinfo_set.first().bookName)
# 显示这个类别下的所有数据,返回的结果是一个QuerySet数据集,这个类型下有两条数据,
print(bookType.bookinfo_set.all())
# 把QuerySet这个数据集转发到页面上就可以进行遍历了
content_value = {"title": "图书列表", "bookList": bookType.bookinfo_set.all()}
return render(request, 'book/list.html', context=content_value)
def preAdd(request):
"""
预处理,添加操作
:param request:
:return:
"""
# 预处理操作需要先找到所有数据
bookTypeList = BookTypeInfo.objects.all()
print(f'可自行打印查看bookTypeList是否查询出所有数据:{bookTypeList}')
# 预处理操作之后要返回到图书添加页面
content_value = {"title": "图书添加", "bookTypeList": bookTypeList}
return render(request, 'book/add.html', context=content_value)
def preAdd2(request):
"""
预处理,添加操作 使用form表单
:param request:
:return:
"""
# 导包引入from helloWorld.forms import BookInfoForm
form = BookInfoForm()
context_value = {"title": "图书添加2", "form": form}
return render(request, 'book/add2.html', context_value)
def preAdd3(request):
"""
预处理,添加操作 使用ModelForm表单
:param request:
:return:
"""
# 导包引入from helloWorld.forms import BookInfoModelForm
form = BookInfoModelForm()
context_value = {"title": "图书添加3", "form": form}
return render(request, 'book/add3.html', context_value)
def add(request):
"""
图书添加
:param request:
:return:
"""
# 封装对象
book = BookInfo()
# 添加数据
book.bookName = request.POST.get("bookName")
book.publishDate = request.POST.get("publishDate")
book.bookType_id = request.POST.get("bookType")
book.price = request.POST.get("price")
# 因为是模板,所以直接使用save()方法
book.save()
# 数据添加完成之后,可以获取新增数据的主键id
print(f'数据添加完成之后,获取新增数据的主键id:{book.id}')
# 添加完成之后,需要返回到列表,直接return返回那个对象
return bookList(request)
# 修改预处理,定义修改操作方法;页面需要传一个id来确定是要修改哪一条记录
def preUpdate(request, id):
"""
预处理,修改操作
:param request:
:return:
"""
print("通过path里面的变量,页面需要传一个id来确定是要修改哪一条记录:", id)
# 根据传过来的id去查询数据,并返回一个book对象
book = BookInfo.objects.get(id=id)
print(f'可自行打印查看返回的book对象:{book}')
bookTypeList = BookTypeInfo.objects.all()
print(bookTypeList)
# book对象也需要传到页面上去,到时候页面上就能够取值了
content_value = {"title": "图书修改", "bookTypeList": bookTypeList, "book": book}
return render(request, 'book/edit.html', context=content_value)
def update(request):
"""
图书修改
:param request:
:return:
"""
book = BookInfo()
book.id = request.POST.get("id")
book.bookName = request.POST.get("bookName")
book.publishDate = request.POST.get("publishDate")
book.bookType_id = request.POST.get("bookType_id")
book.price = request.POST.get("price")
book.save()
return bookList(request)
def delete(request, id):
"""
图书删除
:param request:
:return:
"""
# 删除指定id数据
BookInfo.objects.get(id=id).delete()
# 根据条件删除多条数据
# BookInfo.objects.filter(price__gte=100).delete()
# 删除所有数据
# BookInfo.objects.all().delete()
return bookList(request)
# 定义转账transfer方法--导包引入from django.db import transaction模块使用事务
@transaction.atomic
def transfer(request):
"""
模拟转账
:param request:
:return:
"""
# 开启事务,
sid = transaction.savepoint()
# 添加异常处理机制
try:
# 导包引入from helloWorld.models import AccountInfo方法
# AccountInfo.objects.filter()方法查找A用户
a1 = AccountInfo.objects.filter(user='A用户_赵大')
# 导包引入from django.db.models import F 模块
a1.update(account = F('account') + 100)
# 这个"'A用户_赵大'"账户是加account余额,那么对应的"'B 用户_ 钱二'"账户就是减account余额
b2 = AccountInfo.objects.filter(user='B用户_钱二')
# 修改操作
b2.update(account = F('account') - 100 / 0)
# 提交事务,如果不设置,当程序执行完成后,会自动提交事务
transaction.savepoint_commit(sid)
except Exception as e:
print(f'异常信息为:{e}')
# 事务回滚--传一个sid表示需要事务回滚哪一个事务
transaction.savepoint_rollback(sid)
# 执行完两个操作之后,模拟转账完成,返回到界面
return HttpResponse("OK!转账完成!")

20、运行项目,浏览器输入"http://127.0.0.1:8000/book/list"测试验证,能够正常添加成功;

再次注意此时只有图书添加3(使用ModelForm)能够添加成功,而 图书添加 和 图书添加2(使用Form) 则不会添加成功,提交后都会出现报错"( "Column 'bookType_id' cannot be null" 列'bookType_id'不能为空)"提示;
以后如果没有特殊情况的话,建议在平时开发过程中还是使用 ModelForm 比较好,简单方便,功能更强大。
未完待续......