Django学习
一、介绍&&初识
django是一个开源的web框架,由python编写。django的mtv模式:
Model(模型):负责业务对象与数据库的对象(ORM)
Template(模版):负责如何把页面展示给用户
View(视图):负责业务逻辑,并在适当的时候调用Model和Template
django还有一个url分发器,它的作用是将一个个URL的页面请求分发给不同的view处理,view再调用相应的Model和Template
二、Django流程以及常用命令
创建project django-admin startproject mysite 创建APP python manage.py startapp appname 启动项目 python manage.py runserver 127.0.0.1:8090 生成同步数据库的脚本 python manage.py makemigrations 同步数据库 python manage.py migrate 创建超级管理员 python manage.py createsuperuser 启动交互界面 python manage.py shell
简单项目实例
1、创建项目
django-admin.py startproject mysite
当前目录下会生成mysite的工程,目录结构如下:
manage.py ----- Django项目里面的工具,通过它可以调用django shell和数据库等。 settings.py ---- 包含了项目的默认设置,包括数据库信息,调试标志以及其他一些工作的变量。 urls.py ----- 负责把URL模式映射到应用程序。
wsgi.py:wsgi是Python语言定义的web服务器和web服务程序或者框架之间的一种简单而通用的接口。
2、创建应用
python manage.py startapp blog
目录结构
3、启动项目
在settings.py文件中加入blog app
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'blog', //自己新建的项目 ]
python manage.py runserver 8080
访问
http://127.0.0.1:8080/
4、配置url
from django.conf.urls import include, url from blog.views import * // 导入app blog views.py中所有函数 urlpatterns = [ path('admin/', admin.site.urls), url(r'^userinfo/', userinfo), ]
5、写逻辑处理函数
info_list=[] def userinfo(request): if request.method=="POST": username=request.POST.get("username",None) sex=request.POST.get("sex",None) email=request.POST.get("email",None) info={"username":username,"sex":sex,"email":email} info_list.append(info) return render(request,"info.html",{"info_list":info_list}) //返回info.html 把info_list传到html使模板进行渲染格式:{"info_list":info_list}
6、写html模板
准备
settings.py文件里设置DIRS 'DIRS': [os.path.join(BASE_DIR, 'templates')],
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, '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',
],
},
},
]
在blog同级目录下创建templates目录
编写info.html
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>创建个人信息</h1> <form action="/userinfo/" method="post"> {% csrf_token %} <p>姓名<input type="text" name="username"></p> <p>性别<input type="text" name="sex"></p> <p>邮箱<input type="text" name="email"></p> <p><input type="submit" value="提交"></p> </form> <hr> <h1>信息展示</h1> <table border="1"> <tr> <td>姓名</td> <td>性别</td> <td>邮箱</td> </tr> {% for i in info_list %} <tr> <!--获取字典i中键为username的值--> <td>{{ i.username }}</td> <td>{{ i.sex }}</td> <td>{{ i.email }}</td> </tr> {% endfor %} </table> </body> </html>
7、测试
8、数据信息存储在数据库
上面的数据在django重启后就没了,要想使数据永久保存,需把数据放在数据库中
vim models.py
from django.db import models
# Create your models here.
class UserInfor(models.Model):
username=models.CharField(max_length=64)
sex=models.CharField(max_length=64)
email=models.CharField(max_length=64)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
$ python manage.py makemigrations Migrations for 'blog': blog\migrations\0001_initial.py - Create model UserInfor $python manage.py migrate Operations to perform: Apply all migrations: admin, auth, blog, contenttypes, sessions Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying admin.0003_logentry_add_action_flag_choices... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying auth.0009_alter_user_last_name_max_length... OK Applying blog.0001_initial... OK Applying sessions.0001_initial... OK
vim views.py
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from django.shortcuts import render from blog.models import * # Create your views here. # info_list=[] # # def userinfo(request): # # if request.method=="POST": # username=request.POST.get("username",None) # sex=request.POST.get("sex",None) # email=request.POST.get("email",None) # # info={"username":username,"sex":sex,"email":email} # info_list.append(info) # # # return render(request,"info.html",{"info_list":info_list}) def userinfo(request): if request.method=="POST": u=request.POST.get("username",None) s=request.POST.get("sex",None) e=request.POST.get("email",None) #把获取到数据写入数据库 #info = {"username": u, "sex": e, "email": e} #UserInfor.objects.create(**info) #把获取到数据写入数据库 UserInfor.objects.create( username=u, sex=s, email=e ) info_list = UserInfor.objects.all() return render(request, "info.html", {"info_list": info_list}) #是post请求时,把info_list传给info.html 渲染后返回info.html给用户 '信息展示显示数据库中已存的所有数据' return render(request,"info.html") #第一次是get请求返回info.html '信息展示那里不显示任何信息'
三、Django各部分介绍
3.1 URL介绍
告诉django用户请求的url应该对应哪个视图函数
3.1.1 url配置格式:
urlpatterns = [ url(正则表达式, views视图函数,参数,别名), ] 参数说明: 一个正则表达式字符串 一个可调用对象,通常为一个视图函数或一个指定视图函数路径的字符串 可选的要传递给视图函数的默认参数(字典形式) 一个可选的name参数 例子:url(r'^userinfo/', userinfo), url(r'^articles/[0-9]{4}/$', views.year_archive),
3.1.2 通过url传参数到视图函数
1、无命名参数
大括号括()起来的为参数
url配置
url(r'^index/([0-9]{4})/$', index), #no_named group
url(r'^index/([0-9]{4})/([0-9]{2})/$', index),
views代码 def index(request,arg): return HttpResponse('hello'+arg)
def index(request,arg1,arg2):
return HttpResponse('hello'+arg1+arg2)
注意:无论前端是数字还是字符串,作为参数传到后端都是以字符串处理
无命名参数,参数传入是有顺序之分的
在浏览器请求http://127.0.0.1/index/2019
可以看大浏览器返回:hello2019
在浏览器请求http://127.0.0.1/index/2019/08
可以看到浏览器返回:hello201908
2、有名字参数
格式:
(?P<name>[0-9]{4}) 固定格式:(?P<>) name:参数名字 [0-9]{4}正则表达式
url 配置 url(r'^index/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', index), year和month分别是参数的名字
def index(request,year,month): return HttpResponse('hello'+year+month) 注意:year和month必须和url中名字保持一致 year和month没有固定顺序,按照对应的名字获取参数的值
3.1.3 url别名使用
格式:
url(r'^login/', login,name='loginname'), name是固定的,loginname是login别名
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def login(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') if username == 'root' and password == '123': return HttpResponse("登陆成功") return render(request, 'login.html')
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {# <form action="/index/" method="post">#} <form action="{% url 'loginname' %}" method="post"> <!--渲染的时候django会去urls.py中找name='loginname'的url,然后替换--> {% csrf_token %} 用户名:<input type="text" name="username"> 密码:<input type="password" name="password"> <input type="submit" value="submit"> </form> </body> </html>
3.1.4 url映射分发
Django中有很多不同的app,要想实现其具体的功能,就要用到路径分发。在不同的app中定义自己需要的url
全局urls.py配置 url(r'^blog/', include('blog.urls')), #一旦浏览器中输入blog,就找到blog中urls.py app blog 中urls.py配置 from django.urls import path from . import views #app_name = 'blog' urlpatterns = [ path("login/",views.userinfo), ]
在浏览器中输入:http://127.0.0.1:8080/blog/login 即可看到返回的页面
3.2 视图函数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
path: 请求页面的全路径,不包括域名 method:请求中使用的HTTP方法的字符串表示。全大写表示 GET/POST: 包含所有HTTP GET/POST参数的类字典对象 可以通过request.POST.get('username')获取对应的值 COOKIES: 包含所有cookies的标准Python字典对象;keys和values都是字符串 FILES:包含所有上传文件的类字典对象;FILES中的每一个Key都是<input type="file" name="" />标签中 name属性的值,FILES中的每一个value同时也是一个标准的python字典对象,包含下面三个Keys: filename: 上传文件名,用字符串表示 content_type: 上传文件的Content Type content: 上传文件的原始内容 request.POST.getlist("hobby"):键值对的值是多个的时候,比如checkbox类型的input标签,select标签,需要用
HttpResponse对象
对于HttpRequest对象来说,是由django自动创建的,但是,HttpResponse对象就必须我们自己创建。每个view请求处理方法必须返回一个HttpResponse对象
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1.render 函数 将指定页面渲染后返回给浏览器 格式:render(request, template_name[, context]) 参数说明: request: 用于生成响应的请求对象 template_name:要使用的模板的完整名称,可选的参数 context:添加到模板上下文的一个字典。默认是一个空字典。如果字典中的某个值是可调用的,视图将在渲染模板之前调用它 content_type:生成的文档要使用的MIME类型。默认为DEFAULT_CONTENT_TYPE 设置的值。 render方法主要是将从服务器提取的数据,填充到模板中,然后将渲染后的html静态文件返回给浏览器。这里一定要注意:render渲染的是模板 例子: return render(request, "info.html") return render(request, "info.html", {"info": info_list}) #info_list可以是字符串、列表、字典 herolist = book.heroinfo_set.all() context = {'list': herolist} # 将数据保存在list return render(request, 'booktest/show.html', context) #通过render进行模板渲染
redirect :页面跳转,一般会生成一个302状态码 return rediect('/login.html/') return rediect('/http://www.baidu.com/') return render(request,'login.html')只是跳到login.html这个页面上去,没有别的任何操作 return rediect('/login.html/') 执行的的是url里面的函数,相当于又一次调用新的函数,/login.html/函数里面的所有代码都会执行,
注意:
如果是redirect,那么不仅内容返回的是里面的模板内容,地址栏中的地址也会改变为模板内容的路径。
如果是return,那么内容会返回的是模板内容,地址栏中的地址不会改变,还是请求时候的。
locals()返回一个包含当前作用域里面的所有变量和它们的值的字典。 def index(request): username='root' return render(request, 'login.html',locals()) 在模板中可以直接使用name获取对应值 <p>姓名:{{ username }}</p>
3.3模板 Template
模板由"HTML代码+逻辑控制代码"组成
Django模板中只需要记两种特殊符号:{{ }}和{% %}, 其中{{ }}表示变量,在模板渲染的时候替换成值,{% %}表示逻辑相关的操作
3、1 变量(使用双大括号来引用变量)
语法格式: {{var_name}}
变量名由字母数字和下划线组成,点(.
)在模板语言中有特殊的含义,用来获取对象的相应属性值。
模板中支持的写法:
{# 访问列表索引,取列表l中的索引为0是数据 #} {{ l.0 }} {# 取字典中key的值 #} {{ d.name }} {# 取对象的name属性 可以是python内置的对象或者是自定义类中对象的属性#} {{ person_list.0.name }} {# 点语法也可以用来引用对象的方法 但是.操作只能调用不带参数的方法 #} {{ person_list.0.dream }}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h3>变量练习</h3> <p> 字符串:{{ s }}</p> <p> 列表:{{ l }}</p> <p> 列表索引:{{ l.0 }}</p> <p> 字典:{{ dic.name }} </p> <p> 自定义类:姓名:{{ person.name }}年龄:{{ person.age }}</p> </body> </html>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def index(request): s='aaaaaaaaaaa' l=['a','b','c'] dic={'name':'yqh'} class People: def __init__(self,name,age): self.name=name self.age=age person=People('yqh',18) return render(request,"index.html",{'s':s,'l':l,'dic':dic,'person':person})
3、2 标签tag
标签语法是:{% tag %},它比变量更加复杂:一些在输出中创建文本,一些通过循环或者逻辑来控制流程,一些加载其后的变量将使用道德额外信息到模板中。一些标签需要开始和结束标签(例如:{% tag %}... 标签内容...{% endtag %})。
1、for标签语法如下:
{%for item in 列表%} 循环逻辑 {{forloop.counter}}表示当前是第几次循环,从1开始 {%empty%} 列表为空或不存在时执行此逻辑 {%endfor%}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Variable Description
forloop.counter 当前循环的索引值(从1开始)
forloop.counter0 当前循环的索引值(从0开始)
forloop.revcounter 当前循环的倒序索引值(到1结束)
forloop.revcounter0 当前循环的倒序索引值(到0结束)
forloop.first 当前循环是不是第一次循环(布尔值)
forloop.last 当前循环是不是最后一次循环(布尔值)
forloop.parentloop 本层循环的外层循环
2、if标签语法如下:
{%if ...%} 逻辑1 {%elif ...%} 逻辑2 {%else%} 逻辑3 {%endif%}
{% if %}标签计算一个变量值,如果是“true”,即它存在、不为空并且不是false的boolean值,系统则会显示{% if %}和{% endif %}间的所有内容 支持的操作符有 not,and,or,==, !=, <, >, <=, >=, in, not in, is, is not 注意:运算符左右两侧不能紧挨变量或常量,必须有空格。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--<h3>变量练习</h3>--> <!--<p> 字符串:{{ s }}</p>--> <!--<p> 列表:{{ l }}</p>--> <!--<p> 列表索引:{{ l.0 }}</p>--> <!--<p> 字典:{{ dic.name }} </p>--> <!--<p> 自定义类:姓名:{{ person.name }}年龄:{{ person.age }}</p>--> <ul> {% for book in dic %} {% if book.id == 1 %} <li style="background-color: red;">{{book.btitle}}</li> {% elif book.id == 2 %} <li style="background-color: blue;">{{book.btitle}}</li> {% else %} <li style="background-color: green;">{{book.btitle}}</li> {%endif%} {%empty%} <li>对不起没数据</li> {% endfor %} </ul> </body> </html>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def index(request): l=[] class Book: def __init__(self,id,btitle): self.id=id self.btitle=btitle book1 = Book(1,'python') book2 = Book(2, 'java') book3 = Book(3, 'shell') l.append(book1) l.append(book2) l.append(book3) return render(request,"index.html",{'dic':l})
访问:http://127.0.0.1:8080/index
当传入html数据为空时
3、with标签
使用一个简单的名字还出一个复杂的变量,当我们需要一个"昂贵的"方法(比如:访问数据库)很多次的时候是非常有用的
{% with total=fhjsaldfhjsdfhlasdfhljsdal %}
{{ total }}
{% endwith %}
4、csrf_token标签
提交数据的时候会做安全机制,当用户点击提交的时候会出现一个forbidden错误,就是用settings配置里面的csrf做的安全机制,如果我们不需要使用,可以将其注释。或者在form表单下面添加{% csrf_token %}来解决该问题(这才是真正的解决办法,注释不是解决办法)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<form action="/tag/" method="post"> {% csrf_token %} <p><input type="text" name="test"></p> <input type="submit"> </form>
5、{% url %}: 引用路由配置的地址
6、{% verbatim %}: 禁止render
{% verbatim %} {{ hello }} {% endverbatim %} 不把hello当做变量,原样显示 {{ hello }}
3、3 过滤器
过滤器会更改变量或标签参数的值,可以通过使用 过滤器来改变变量的显示;使用管道符号 (|)来应用过滤器;过滤器可以“链接”,即一个过滤器的输出应用于下一个过滤器
语法: 变量|过滤器:参数 1、使用管道符号|来应用过滤器,用于进行计算、转换操作,可以使用在变量、标签中。 2、如果过滤器需要参数,则使用冒号:传递参数
常见过滤器 1.default:{{ variable| default: "nothing"}},如果变量是false或者为空,使用默认值。否则,使用变量的值 <p>default过滤器:{{ li|default:"如果显示为空,设置解释性的内容。" }}</p> 2、length:返回值的长度,它对字符串和列表都起作用 {{ value | length }} # 如果value是["a", "b", "c", "d"],那么输出是4 3、date:格式化日期时间格式 {{ value | date:"Y-m-d" }} # 如果value=datetime.datetime.now(),返回时间的年-月-日格式 4、safe django的模板中会对HTML标签和JS语法标签进行自动转义,这样是为了安全。但是有的时候我们不想这些HTML元素被转义,比如我们做一个内容管理系统,后台添加的文章是经过修饰的,这些修饰可能是通过类似于FCKeditor编辑加注了HTML修饰符的文本,如果自动转义的话显示的就是保护HTML标签的源文件。为了在django中关闭HTML自动转义有两种方式,如果是一个单独的变量我们可以通过过滤器"|safe"的方式告诉django这段代码是安全的不必转义,如下: value="<a href="">点击</a>"" {{ value | safe }}
<p>{{ label }}</p> # 为了安全,系统会把标签变成字符串 <p>{{ label | safe }}</p> # 加上safe,确定数据时安全的才能被当成是标签
3、4 自定义标签(tag)和过滤器(filter)
实现方式
1、在app目录中创建templatetags包(包名只能是templatetags)
total 17 -rw-r--r-- 1 migu 197121 0 八月 23 14:11 __init__.py drwxr-xr-x 1 migu 197121 0 八月 29 17:05 __pycache__/ -rw-r--r-- 1 migu 197121 66 八月 23 14:11 admin.py -rw-r--r-- 1 migu 197121 88 八月 23 14:11 apps.py drwxr-xr-x 1 migu 197121 0 八月 23 15:25 migrations/ -rw-r--r-- 1 migu 197121 224 八月 23 15:20 models.py drwxr-xr-x 1 migu 197121 0 八月 29 17:29 templates/ drwxr-xr-x 1 migu 197121 0 八月 30 14:13 templatetags/ -rw-r--r-- 1 migu 197121 63 八月 23 14:11 tests.py -rw-r--r-- 1 migu 197121 129 八月 26 20:40 urls.py -rw-r--r-- 1 migu 197121 2028 八月 29 17:05 views.py
2、在templatetags目录下创建任意的.py文件 如:my_tags.py
$ ll templatetags/ total 0 -rw-r--r-- 1 migu 197121 0 八月 30 14:13 __init__.py -rw-r--r-- 1 migu 197121 0 八月 30 14:16 my_tags.py
3、在my_tags.py文件中添加自定义的标签和过滤器
from django import template from django.utils.safestring import mark_safe register = template.Library() #register的名字是固定的,不可改变 #自己定义filter @register.filter def myfilter(v1):#无参数 return v1+10 @register.filter def myfilter1(v1,v2):#有参数 return v1 + v2 #自己定义tag @register.simple_tag def mytag(v1,v2,v3): return v1 + v2+v3
4、在使用自定义simple_tag和filter的html文件中导入之前创建的 my_tags.py :{% load my_tags %}
{# 先导入我们自定义filter那个文件 必须在首行 #} {% load my_tags%} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {# 使用我们自定义的filter #} <p> <!--过滤器myfilter将传递变量num--> 数值加10:{{ num|myfilter }} </p> <p> <!--过滤器myfilter将传递变量num和参数1--> 数值加传入的值:{{ num|myfilter1:1 }} </p> {# 使用我们自定义的tag #} <p> {% mytag 'a' 'b' 'c' %} </p> </body> </html>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
url(r'^tag/', mytag), def mytag(request): num=1 return render(request,'mytag.html',{'num':num})
访问:http://127.0.0.1:8080/tag 输出结果: 数值加10:11 数值加传入的值:2 abc
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
自定义标签:参数不限,但不能放在if for语句中 自定义过滤器:可以放在if for 语句中,最多2个参数
3、5模板继承
模版继承可以让您创建一个基本的“骨架”模版,它包含您站点中的全部元素,并且可以定义能够被子模版覆盖的 blocks 。
父模板:如果发现在多个模板中某些内容相同,那就应该把这段内容定义到父模板中。 标签block:用于在父模板中预留区域,留给子模板填充差异性的内容,名字不能相同。 为了更好的可读性,建议给endblock标签写上名字,这个名字与对应的block名字相同。父模板中也可以使用上下文中传递过来的数据。 {%block 名称%} 预留区域,可以编写默认内容,也可以没有默认内容 {%endblock 名称%}
子模板 标签extends:继承,写在子模板文件的第一行。 {% extends "父模板"%} 子模版不用填充父模版中的所有预留区域,如果子模版没有填充,则使用父模版定义的默认值。 {%block 名称%} 实际填充内容 {{block.super}}用于获取父模板中block的内容 {%endblock 名称%}
模板继承例子部分代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> * { margin:0; } .page-header{ height:30px; background-color:rebeccapurple; } .page-body .menu{ height:700px; background-color:antiquewhite; float:left; width:20% } .page-body .content{ height:700px; background-color:cornflowerblue; float:left; width:80% } .page-footer{ height:30px; background-color:darkcyan; clear:both; } a {display: block; } </style> </head> <body> <div> <div class="page-header"></div> <div class="page-body"> <div class="menu"> <a href="/shouye">首页</a> <a href="/dingdan">订单</a> </div> <div class="content"> {% block page-content %} 默认信息 {% endblock %} </div> </div> <div class="page-footer"></div> </div> </body> </html>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
{% extends 'base.html' %} {% block page-content %} <p> 个人信息 </p> {% endblock %}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
{% extends 'base.html' %} </style> {% block page-content %} <p> 手机</p> <p> 电脑</p> <p> 平板</p> {% endblock %}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
url(r'^index/', index),
url(r'^shouye/', shouye),
url(r'^dingdan/', dingdan),
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def index(request):
return render(request,"base.html")
def shouye(request):
return render(request,"shouye.html")
def dingdan(request):
return render(request,"dingdan.html")
分别访问:
http://127.0.0.1:8080/index
http://127.0.0.1:8080/shouye
http://127.0.0.1:8080/dingdan