1. Django的命令:
1. 下载安装
pip install django==1.11.16 -i 源
2. 创建项目
django-admin startproject 项目名称
3. 启动项目
cd 项目根目录下
python manage.py runserver # 127.0.0.1:8000
python manage.py runserver 80 # 127.0.0.1:80
python manage.py runserver 0.0.0.0:80 # 0.0.0.0:80
4. 创建APP
python manage.py startapp app名称
注册
5. 数据库相关
python manage.py makemigrations # 记录model的变更情况
python manage.py migrate # 将变更记录同步到数据库中
Django 的路由系统
URL配置(URLconf)就像Django所支撑网站的目录。它的本质是URL与要为该URL调用的视图函 数之间的映射表。
我们就是以这种方式告诉Django,遇到哪个URL的时候,要对应执行哪个函数。
基本格式
from django.conf.urls import url
from app1 import views as aap1vw
from app2 import views as aap2vw
urlpatterns = [ url(正则表达式, app1vw,参数,别名),
url(正则表达式, app2vw, 参数, 别名), ]
示例:
from django.conf.urls import url from . import views urlpatterns = [ url(r'^articles/2003/$', views.special_case_2003), url(r'^articles/([0-9]{4})/$', views.year_archive), url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive), url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail), ]
Django 2.0版本中的路由系统是下面的写法(官方文档):
from django.urls import path,re_path urlpatterns = [ path('articles/2003/', views.special_case_2003), #与1的区别一个是url 一个是path path('articles/<int:year>/', views.year_archive), path('articles/<int:year>/<int:month>/', views.month_archive), path('articles/<int:year>/<int:month>/<slug:slug>/', views.article_detail), ]
正则匹配url
from django.conf.urls import url from . import views urlpatterns = [ url(r'^articles/2003/$', views.special_case_2003), url(r'^articles/([0-9]{4})/$', views.year_archive), url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive), url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail), ]
注意事项
- urlpatterns中的元素按照书写顺序从上往下逐一匹配正则表达式,一旦匹配成功则不再继续。
- 若要从URL中捕获一个值,只需要在它周围放置一对圆括号(分组匹配)。
- 不需要添加一个前导的反斜杠,因为每个URL 都有。例如,应该是^articles 而不是 ^/articles。
- 每个正则表达式前面的'r' 是可选的但是建议加上。
# 是否开启URL访问地址后面不为/跳转至带有/的路径的配置项 APPEND_SLASH=True
Django settings.py配置文件中默认没有 APPEND_SLASH 这个参数,但 Django 默认这个参数为 APPEND_SLASH = True。 其作用就是自动在网址结尾加'/'。
其效果就是:
我们定义了urls.py:
from django.conf.urls import url from app01 import views urlpatterns = [ url(r'^blog/$', views.blog), ]
访问 http://www.example.com/blog 时,默认将网址自动转换为 http://www.example/com/blog/ 。
如果在settings.py中设置了 APPEND_SLASH=False,此时我们再请求 http://www.example.com/blog 时就会提示找不到页面。
分组命名匹配
上面的示例使用简单的正则表达式分组匹配(通过圆括号)来捕获URL中的值并以位置参数形式传递给视图。
在更高级的用法中,可以使用分组命名匹配的正则表达式组来捕获URL中的值并以关键字参数形式传递给视图。
在Python的正则表达式中,分组命名正则表达式组的语法是(?P<name>pattern)
,其中name
是组的名称,pattern
是要匹配的模式。
下面是以上URLconf 使用命名组的重写:
from django.conf.urls import url from . import views #将匹配到括号里的url内容传给views,views必须的写形参接收 urlpatterns = [ url(r'^articles/2003/$', views.special_case_2003), url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive), url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive), url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.article_detail), ]
这个实现与前面的示例完全相同,只有一个细微的差别:捕获的值作为关键字参数而不是位置参数传递给视图函数。
例如,针对URL /articles/2017/12/相当于按以下方式调用视图函数:
views.month_archive(request, year="2017", month="12") #viewa必须的带接收的参数
在实际应用中,使用分组命名匹配的方式可以让你的URLconf 更加明晰且不容易产生参数顺序问题的错误,但是有些开发人员则认为分组命名组语法太丑陋、繁琐。
至于究竟应该使用哪一种,你可以根据自己的喜好来决定。
视图函数中指定默认值
# urls.py中 from django.conf.urls import url from . import views urlpatterns = [ #不同的url找同一个视图函数 url(r'^blog/$', views.page), url(r'^blog/page(?P<num>[0-9]+)/$', views.page), ] # views.py中,可以为num指定默认值 def page(request, num="1"): pass
在上面的例子中,两个URL模式指向相同的view - views.page - 但是第一个模式并没有从URL中捕获任何东西。
如果第一个模式匹配上了,page()函数将使用其默认参数num=“1”,如果第二个模式匹配,page()将使用正则表达式捕获到的num值。
include使urls.py 文件放在自己app目录里
将django项目里的urls.py 文件复制到app1 和app2里
项目里的urls.py文件
from django.conf.urls import url, include #引用include urlpatterns = [ url(r'app1/', include('app1.urls')), #r''可以为空,也可以匹配1级url,include 找到app1里的urls文件 url(r'app1/', include('app1.urls')), url(r'app2/', include('app2.urls')), url(r'app2/', include('app2.urls')), url(r'app2/', include('app2.urls')), ]
app1里的urls.py文件
from django.conf.urls import url from app1 import views as aap1vw urlpatterns = [ #此时的url前边要加上/app1/ 即127.0.0.1/app1/zzzz/ url(r'zzzz/',aap1vw.zzzz),
app2里的urls.py文件
from django.conf.urls import url from app2 import views as aap2vw urlpatterns = [ url(r'xxxx/',aap2vw.xxxx), url(r'dddd/',aap2vw.dddd), url(r'cccc/',aap2vw.cccc), ]
使用上述方法会导致 html页面里的和views里写死的url不能使用,解决办法:给url命名和反向解析:
url命名和反向解析
给url起一个别名 通过反向解析,通过别名动态拿到url地址
在app1里的urls.py文件里给url地址起别名
from django.conf.urls import url from app1 import views as aap1vw urlpatterns = [ url(r'zhanshi/',aap1vw.Zhan_shi,name='zhanshi'), #增加别名 url(r'add/', aap1vw.Add,name='add'), url(r'update/', aap1vw.Update,name='update'), url(r'del/', aap1vw.Del,name='del'), ]
修改html展示页里的url,使用url别名 模板: {% url '别名' %}
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/css/bootstrap.min.css"> <link rel="stylesheet" href="/static/css/das.css"> </head> <body> <nav class="navbar navbar-inverse navbar-fixed-top"> <div class="container-fluid"> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">出版社管理</a> </div> <div id="navbar" class="navbar-collapse collapse"> <ul class="nav navbar-nav navbar-right"> <li><a href="#">Dashboard</a></li> <li><a href="#">Settings</a></li> <li><a href="#">Profile</a></li> <li><a href="#">Help</a></li> </ul> <form class="navbar-form navbar-right"> <input type="text" class="form-control" placeholder="Search..."> </form> </div> </div> </nav> <div class="container-fluid"> <div class="row"> <div class="col-sm-3 col-md-2 sidebar"> <ul class="nav nav-sidebar"> <li class="active"><a href="{% url 'zhanshi' %}">出版社列表 <span class="sr-only">(current)</span></a></li> {#将 之前的/展示/ 改为 {% url 'zhanshi' %}#} 动态或其url别名对应的url路径 </ul> </div> <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main"> <h2 class="sub-header">出版社信息</h2> <div class="table-responsive"> <a href="{% url 'add' %}" class="btn btn-primary">添加</a> <table class="table table-striped table-hover"> <thead> <tr> <th>序号<th> <th>ID</th> <th>出版社名称</th> <th>操作</th> </tr> </thead> <tbody> {% for foo in all %} <tr> <td>{{ forloop.counter }}</td> <td>{{ foo.pk }}</td> <td>{{ foo.mane }}</td> <td> <a href="{% url 'del' %}?sh={{ foo.pk }}" class="btn btn-danger btn-sm">删除</a> <a href="{% url 'update' %}?zj={{ foo.pk }}" class="btn btn-primary btn-sm">编辑</a> </td> </tr> {% endfor %} </tbody> </table> </div> </div> </div> </div> </body> </html>
修改views.py 里写死的redirect 跳转的url .(需要在引用reverse方法) 模板 reverse('别名')
from django.shortcuts import render,redirect,reverse #引用reverse方法 from app1 import models def Zhan_shi(request): all_tusu = models.tusu.objects.all() return render(request,'zhanshi.html',{'all': all_tusu}) def Add(request): if request.method == 'POST': newname = request.POST.get('newname') models.tusu.objects.create(mane=newname) return redirect(reverse('zhanshi')) #将之前写的跳转写死的redirect('/zhanshi/') 修改为获取别名的redirect(reverse('zhanshi')) return render(request,'add.html') def Update(request): pk = request.GET.get('zj') obj = models.tusu.objects.filter(pk=pk).first() if request.method == 'POST': newname = request.POST.get('newname') obj.mane = newname obj.save() return redirect(reverse('zhanshi')) return render(request,'update.html', {'obj' : obj} ) def Del(request): pk = request.GET.get('sh') obj = models.tusu.objects.filter(pk=pk).first() if request.method == 'POST': dname = request.POST.get('dname') models.tusu.objects.get(mane = dname).delete() return redirect(reverse('zhanshi')) return render(request,'del.html',{'obj':obj})
命名空间模式
用来解决 不同app中,url名相同路由找错的问题
即使不同的APP使用相同的URL名称,URL的命名空间模式也可以让你唯一反转命名的URL。
设置项目里的urls.py
from django.conf.urls import url, include urlpatterns = [ url(r'app1/', include('app1.urls',namespace='app1')), #namespace 相当于增加一个别名,用来让模板和views来识别出唯一的url路径 url(r'app2/', include('app2.urls',namespace='app2')) ]
app1中的urls.py
from django.conf.urls import url from app01 import views app_name = 'app01' urlpatterns = [ url(r'^zhanshi', views.yemian1, name='zhanshi'), url(r'^add', views.yemian2, name='add'), ]
app2中的urls.py
from django.conf.urls import url from app01 import views app_name = 'app01' urlpatterns = [ url(r'^zhanshi', views.yemian1, name='zhanshi'), url(r'^add', views.yemian2, name='add'), ]
此时app1和app2里的别名相同,要想确认唯一页面,就要把namespace对应的值加入到前后端代码里
前端使用方法
{% url 'app1:zhanshi' %} #在app里的url的别名前加上 views里的url别名
后端使用方法
return redirect(reverse('app1:zhanshi') #和前端一样也是前面加views里的url别名
MVC框架和MTV框架
详细介绍:http://www.ruanyifeng.com/blog/2007/11/mvc.html
MVC,全名是Model View Controller,是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller),具有耦合性低、重用性高、生命周期成本低等优点。
Django框架的设计模式借鉴了MVC框架的思想,也是分成三部分,来降低各个部分之间的耦合性。
Django框架的不同之处在于它拆分的三部分为:Model(模型)、Template(模板)和View(视图),也就是MTV框架。
Django的MTV模式
Model(模型):负责业务对象与数据库的对象(ORM)
Template(模版):负责如何把页面展示给用户
View(视图):负责业务逻辑,并在适当的时候调用Model和Template
此外,Django还有一个urls分发器,它的作用是将一个个URL的页面请求分发给不同的view处理,view再调用相应的Model和Template
Django框架图示
Django的View(视图)
一个视图函数(类),简称视图,是一个简单的Python 函数(类),它接受Web请求并且返回Web响应。
响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片。
无论视图本身包含什么逻辑,都要返回响应。代码写在哪里也无所谓,只要它在你当前项目目录下面。除此之外没有更多的要求了——可以说“没有什么神奇的地方”。为了将代码放在某处,大家约定成俗将视图放置在项目(project)或应用程序(app)目录中的名为views.py的文件中。
CBV和FBV
我们之前写过的都是基于函数的view,就叫FBV。还可以把view写成基于类CBV的。
FBV (之前写的添加出版舍)
def Add(request): #此处定义的是函数 是FBV F是function if request.method == 'POST': newname = request.POST.get('newname') models.tusu.objects.create(mane=newname) return redirect(reverse('app1:zhanshi')) return render(request,'add.html')
CBV(C为类,把FBV改为CBV)
先修改 对应的url
from django.conf.urls import url from app1 import views as aap1vw urlpatterns = [ url(r'zhanshi/',aap1vw.Zhan_shi,name='zhanshi'), url(r'add1/', aap1vw.Add,name='add'), #这是FBV的url url(r'add2/', aap1vw.AddC.as_view(),name='add'), #这是CBV的url 区别在于后边多了.as_view() url(r'update/', aap1vw.Update, name='update'), url(r'del/', aap1vw.Del,name='del'),
写CBF方法
from django.views import View #在VBV的基础上增加引用View方法 class AddC(View): #定义类名并继承View def get(self,request): #与FBV的区别在于直接把get和post请求处理的逻辑区分开了 逻辑更清晰 return render(request, 'add.html') def post(self,request): newname = request.POST.get('newname') models.tusu.objects.create(mane=newname) return redirect(reverse('app1:zhanshi'))
给视图加装饰器
装饰器计算程序执行时间
import time def Time(func): def inner(*args, **kwargs): cc = time.time() ret = func(*args,**kwargs) print(cc - time.time()) return ret return inner
装饰FBV
FBV本身就是一个函数,所以和给普通的函数加装饰器无差
@Time def Add(request): if request.method == 'POST': newname = request.POST.get('newname') models.tusu.objects.create(mane=newname) return redirect(reverse('app1:zhanshi')) return render(request,'add.html')
装饰CBV
类中的方法与独立函数不完全相同,因此不能直接将函数装饰器应用于类中的方法 ,我们需要先将其转换为方法装饰器。
Django中提供了method_decorator装饰器用于将函数装饰器转换为方法装饰器。
第一种 low版直接在函数上加
class AddC(View): @Time def get(self,request): return render(request, 'add.html') @Time def post(self,request): newname = request.POST.get('newname') models.tusu.objects.create(mane=newname) return redirect(reverse('app1:zhanshi'))
第二种引用method_decorator
from django.utils.decorators import method_decorator #引用method_decorator方法, class AddC(View): @method_decorator(Time) #在每个函数上加装饰器 def get(self,request): return render(request, 'add.html') @method_decorator(Time) def post(self,request): newname = request.POST.get('newname') models.tusu.objects.create(mane=newname) return redirect(reverse('app1:zhanshi'))
第三种 引用上级的dispatch
from django.utils.decorators import method_decorator #不管走post还是gat都走dispatch函数,装饰dispatch函数就是装饰了他俩 class AddC(View): @method_decorator(Time) #装饰当前的dispatch函数 def dispatch(self, request, *args, **kwargs): #定义函数dispatch ret = super().dispatch(request, *args, **kwargs) 引用上级的dispatch return ret #把上级的调用结果返回给当前的dispatch函数 def get(self,request): return render(request, 'add.html') def post(self,request): newname = request.POST.get('newname') models.tusu.objects.create(mane=newname) return redirect(reverse('app1:zhanshi'))
第四种在类上加并指定使用装饰器的函数
from django.utils.decorators import method_decorator @ method_decorator(Time,name='get') @ method_decorator(Time,name='post') class AddC(View): def get(self,request): return render(request, 'add.html') def post(self,request): newname = request.POST.get('newname') models.tusu.objects.create(mane=newname) return redirect(reverse('app1:zhanshi'))
第五种 最终版直接装饰上级的dispatch 一句搞定
from django.utils.decorators import method_decorator @ method_decorator(Time,name='dispatch') class AddC(View): def get(self,request): return render(request, 'add.html') def post(self,request): newname = request.POST.get('newname') models.tusu.objects.create(mane=newname) return redirect(reverse('app1:zhanshi'))
拓展 直接在函数上加装饰器和引用method_decorator家装饰器的区别
区别在于在装饰器里接受的args
def Time(func): def inner(*args, **kwargs): cc = time.time() ret = func(*args,**kwargs) print(cc - time.time()) return ret return inner
如果直接加 args接受俩参数,第一个是func对象,第二个是request请求对象
如果用method_decorator args 只接受一个参数就是request对象
from django.utils.decorators import method_decorator # @ method_decorator(Time,name='dispatch') class AddC(View): @Time #get方式直接装饰 def get(self,request): return render(request, 'add.html') @method_decorator(Time) #post方式method_decorator方式 def post(self,request): newname = request.POST.get('newname') models.tusu.objects.create(mane=newname) return redirect(reverse('app1:zhanshi'))
结果
(<app1.views.AddC object at 0x00000241089604A8>, <WSGIRequest: GET '/app1/add2/'>) #get方式 args俩参数 -0.0019369125366210938 [22/Feb/2019 22:44:11] "GET /app1/add2/ HTTP/1.1" 200 2628 (<WSGIRequest: POST '/app1/add2/'>,) #post方式 args一个参数 -0.01776266098022461 [22/Feb/2019 22:44:15] "POST /app1/add2/ HTTP/1.1" 302 0
请求和响应request和response
请求新手必备三件套
HttpResponse('字符串') #给请求返回一个字符串
redirect(request,'html文件') #给请求返回一个html文件
rende(重定向地址) #重定向
requset对象
request属性
request.method 请求方法 GET POST PUT
request.GET URL上携带的参数
request.POST POST请求提交的数据
request.path 返回用户访问url路径,不包括域名和参数信息 (和request.path_info一样)
request.body 请求体,byte类型,即 POST请求传过来的参数
class AddC(View): @Time def get(self,request): print(request.GET) return render(request, 'add.html') @method_decorator(Time) def post(self,request): print(request.method) print(request.path_info) print(request.body) print(request.POST) newname = request.POST.get('newname') models.tusu.objects.create(mane=newname) return redirect(reverse('app1:zhanshi'))
# 打印出的参数 (<WSGIRequest: POST '/app1/add2/'>,) #url上带的参数 request.GET 由于没有在get上传参 所以这个例子没有参数 POST #请求方式 request.method /app1/add2/ #url路径 request.path_info b'newname=%E4%BA%BA%E6%B0%91%E5%87%BA%E7%89%88%E7%A4%BE' #请求主体 byte请求体 request.body <QueryDict: {'newname': ['人民出版社']}> #post请求出来的键值, request.POST
request. scheme 表示请求方案的字符串 (通常为http或https)
def Zhan_shi(request): all_tusu = models.tusu.objects.all() print(request.scheme) return render(request,'zhanshi.html',{'all': all_tusu}) http
request.FILES
一个类似于字典的对象,包含所有的上传文件信息。 FILES 中的每个键为<input type="file" name="" /> 中的name,值则为对应的数据。 注意,FILES 只有在请求的方法为POST 且提交的<form> 带有enctype="multipart/form-data" 的情况下才会 包含数据。否则,FILES 将为一个空的类似于字典的对象。
request.META
一个标准的Python 字典,包含所有的HTTP 首部。具体的头部信息取决于客户端和服务器
def Zhan_shi(request): all_tusu = models.tusu.objects.all() print(request.scheme) print(request.META) return render(request,'zhanshi.html',{'all': all_tusu}) {'ALLUSERSPROFILE': 'C:\\ProgramData', 'APPDATA': 'C:\\Users\\22490\\AppData\\Roaming', 'COMMONPROGRAMFILES': 'C:\\Program Files\\Common Files', 'COMMONPROGRAMFILES(X86)': 'C:\\Program Files (x86)\\Common Files', 'COMMONPROGRAMW6432': 'C:\\Program Files\\Common Files', 'COMPUTERNAME': 'DESKTOP-G8I5SER', 'COMSPEC': 'C:\\WINDOWS\\system32\\cmd.exe', 'CONFIGSETROOT': 'C:\\WINDOWS\\ConfigSetRoot', 'DJANGO_SETTINGS_MODULE': 'tushu.settings', 'DRIVERDATA': 'C:\\Windows\\System32\\Drivers\\DriverData', 'FPS_BROWSER_APP_PROFILE_STRING': 'Internet Explorer', 'FPS_BROWSER_USER_PROFILE_STRING': 'Default', 'HOMEDRIVE': 'C:', 'HOMEPATH': '\\Users\\22490', 'LOCALAPPDATA': 'C:\\Users\\22490\\AppData\\Local', 'LOGONSERVER': '\\\\DESKTOP-G8I5SER', 'MOZ_PLUGIN_PATH': 'D:\\福喜\\Foxit Reader\\plugins\\', 'NUMBER_OF_PROCESSORS': '4', 'ONEDRIVE': 'C:\\Users\\22490\\OneDrive', 'OS': 'Windows_NT', 'PATH': 'C:\\Python27\\;C:\\Python27\\Scripts;C:\\Python36\\Scripts\\;C:\\Python36\\;C:\\Program Files (x86)\\Intel\\iCLS Client\\;C:\\Program Files\\Intel\\iCLS Client\\;C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\Program Files (x86)\\Intel\\Intel(R) Management Engine Components\\DAL;C:\\Program Files\\Intel\\Intel(R) Management Engine Components\\DAL;C:\\Program Files (x86)\\Intel\\Intel(R) Management Engine Components\\IPT;C:\\Program Files\\Intel\\Intel(R) Management Engine Components\\IPT;C:\\Program Files (x86)\\NVIDIA Corporation\\PhysX\\Common;C:\\WINDOWS\\system32;C:\\WINDOWS;C:\\WINDOWS\\System32\\Wbem;C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\;C:\\WINDOWS\\System32\\OpenSSH\\;D:\\shexiangt\\3rdparty\\lib\\Win32\\;D:\\shexiangt\\Redist\\Win32\\;C:\\Users\\22490\\AppData\\Local\\Microsoft\\WindowsApps;;D:\\pycharm\\PyCharm 2018.3.2\\bin;;C:\\Python36\\lib\\site-packages\\numpy\\.libs;C:\\Python36\\lib\\site-packages\\numpy\\.libs', 'PATHEXT': '.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.PY;.PYW', 'PROCESSOR_ARCHITECTURE': 'AMD64', 'PROCESSOR_IDENTIFIER': 'Intel64 Family 6 Model 142 Stepping 9, GenuineIntel', 'PROCESSOR_LEVEL': '6', 'PROCESSOR_REVISION': '8e09', 'PROGRAMDATA': 'C:\\ProgramData', 'PROGRAMFILES': 'C:\\Program Files', 'PROGRAMFILES(X86)': 'C:\\Program Files (x86)', 'PROGRAMW6432': 'C:\\Program Files', 'PSMODULEPATH': 'C:\\Program Files\\WindowsPowerShell\\Modules;C:\\WINDOWS\\system32\\WindowsPowerShell\\v1.0\\Modules', 'PT5HOME': 'D:\\思科交换机模拟器\\Cisco Packet Tracer 6.0', 'PT6HOME': 'D:\\思科交换机模拟器\\Cisco Packet Tracer 6.0', 'PUBLIC': 'C:\\Users\\Public', 'PYCHARM': 'D:\\pycharm\\PyCharm 2018.3.2\\bin;', 'PYCHARM_HOSTED': '1', 'PYCHARM_MATPLOTLIB_PORT': '55621', 'PYTHON36': 'C:\\Python36', 'PYTHONIOENCODING': 'UTF-8', 'PYTHONPATH': 'D:\\django\\tushu;D:\\pycharm\\PyCharm 2018.3.2\\helpers\\pycharm_matplotlib_backend', 'PYTHONUNBUFFERED': '1', 'SESSIONNAME': 'Console', 'SYSTEMDRIVE': 'C:', 'SYSTEMROOT': 'C:\\WINDOWS', 'TEMP': 'C:\\Users\\22490\\AppData\\Local\\Temp', 'TMP': 'C:\\Users\\22490\\AppData\\Local\\Temp', 'USERDOMAIN': 'DESKTOP-G8I5SER', 'USERDOMAIN_ROAMINGPROFILE': 'DESKTOP-G8I5SER', 'USERNAME': '22490', 'USERPROFILE': 'C:\\Users\\22490', 'WINDIR': 'C:\\WINDOWS', 'RUN_MAIN': 'true', 'SERVER_NAME': 'DESKTOP-G8I5SER', 'GATEWAY_INTERFACE': 'CGI/1.1', 'SERVER_PORT': '8000', 'REMOTE_HOST': '', 'CONTENT_LENGTH': '', 'SCRIPT_NAME': '', 'SERVER_PROTOCOL': 'HTTP/1.1', 'SERVER_SOFTWARE': 'WSGIServer/0.2', 'REQUEST_METHOD': 'GET', 'PATH_INFO': '/app1/zhanshi/', 'QUERY_STRING': '', 'REMOTE_ADDR': '127.0.0.1', 'CONTENT_TYPE': 'text/plain', 'HTTP_HOST': '127.0.0.1:8000', 'HTTP_CONNECTION': 'keep-alive', 'HTTP_CACHE_CONTROL': 'max-age=0', 'HTTP_UPGRADE_INSECURE_REQUESTS': '1', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36', 'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', 'HTTP_ACCEPT_ENCODING': 'gzip, deflate, br', 'HTTP_ACCEPT_LANGUAGE': 'zh-CN,zh;q=0.9', 'wsgi.input': <_io.BufferedReader name=464>, 'wsgi.errors': <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>, 'wsgi.version': (1, 0), 'wsgi.run_once': False, 'wsgi.url_scheme': 'http', 'wsgi.multithread': True, 'wsgi.multiprocess': False, 'wsgi.file_wrapper': <class 'wsgiref.util.FileWrapper'>}
简单的上传服务
def upload(request): """ 保存上传文件前,数据需要存放在某个位置。默认当上传文件小于2.5M时,django会将上传文件的全部内容读进内存。从内存读取一次,写磁盘一次。 但当上传文件很大时,django会把上传文件写到临时文件中,然后存放到系统临时文件夹中。 :param request: :return: """ if request.method == "POST": # 从请求的FILES中获取上传文件的文件名,file为页面上type=files类型input的name属性值 filename = request.FILES["file"].name # 在项目目录下新建一个文件 with open(filename, "wb") as f: # 从上传的文件对象中一点一点读 for chunk in request.FILES["file"].chunks(): # 写入本地文件 f.write(chunk) return HttpResponse("上传OK")
request方法
request.get_full_path() 获取全路径 即路径和参数
print(request.get_full_path()) /app1/update/?zj=16
request.is_secure() 如果请求时是https,则返回True;反之False
response对象
JsonResponse是HttpResponse的子类,专门用来生成JSON编码的响应。
from django.http import JsonResponse #引用JsonResponse def json_text(request): data = {'name':'wk','age':18} return JsonResponse(data) #这样前端就能接收到json格式的数据 默认只能传递字典类型,如果要传递非字典类型需要设置一下safe关键字参数。 JsonResponse([1, 2, 3], safe=False)
Django模板系统
Django模板中只需要记两种特殊符号:
{{ }}和 {% %}
{{ }}表示变量,在模板渲染的时候替换成值,{% %}表示逻辑相关的操作。
变量
{{ 变量名 }}
变量名由字母数字和下划线组成。
点(.)在模板语言中有特殊的含义,用来获取对象的相应属性值。
def test(request): l = [1, 2, 3] d = {'name':'wk'} class dd_a(): def __init__(self, name, age): self.name = name self.age = age def dream(self): return "%s 1111" %(self.name) Wk = dd_a('wk',15) wc = dd_a('wc',13) ww = dd_a('ww',18) pp = [Wk,wc,ww] return render(request, "test.html",{'l':l,'d':d, 'pp':pp} )
在html 取变量
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {{ l.0 }} #根据索引取列表的值 <br> {{ l.1 }} #取列表的第一个值 <br> {{ d.name }} #根据key取字典的值 <br> {{ d.keys }} #取字典的所有key values 取字典所有的值 items取字典的键值 <br> {{ pp.0.name }} #取列表pp第一个对象的 name属性 <br> {{ pp.0.age }} <br> {{ pp.0.dream }} #使用pp列表第一个对象的dream方法 </body> </html>
注:当模板系统遇到一个 .(点)时,会按照如下的顺序去查询:
- 在字典中查询
- 属性或者方法
- 数字索引
Filters 过滤器
翻译为过滤器,用来修改变量的显示结果。
语法: {{ value|filter_name:参数 }}
'|'左右没有空格没有空格没有空格
default
{{ value|default:"nothing"}}
如果value值没传的话就显示nothing
注:TEMPLATES的OPTIONS可以增加一个选项:string_if_invalid:'找不到',可以替代default的的作用。
filesizeformat
将值格式化为一个 “人类可读的” 文件尺寸 (例如 '13 KB', '4.1 MB', '102 bytes', 等等)。例如:
{{ value|filesizeformat }}
如果 value 是 123456789,输出将会是 117.7 MB。
add
给变量加参数
{{ value|add:"2" }}
value是数字4,则输出结果为6。
{{ first|add:second }}
如果first是 [1,.2,3] ,second是 [4,5,6] ,那输出结果是 [1,2,3,4,5,6] 。
lower
小写
{{ value|lower }}
upper
大写
{{ value|upper}}
title
标题
{{ value|title }}
ljust
左对齐
"{{ value|ljust:"10" }}"
rjust
右对齐
"{{ value|rjust:"10" }}"
center
居中
"{{ value|center:"15" }}"
length
{{ value|length }}
返回value的长度,如 value=['a', 'b', 'c', 'd']的话,就显示4.
slice
切片
{{value|slice:"2:-1"}}
first
取第一个元素
{{ value|first }}
last
取最后一个元素
{{ value|last }}
join
使用字符串拼接列表。同python的str.join(list)。
{{ value|join:" // " }}
truncatechars
如果字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾。
参数:截断的字符数
{{ value|truncatechars:9}}
date
views视图添加时间
import datetime def test(request): Date = datetime.datetime.now() return render(request, "test.html",{'date':Date})
日期格式化
{{ value|date:"Y-m-d H:i:s"}}
可格式化输出的字符:点击查看。
日期也可以改配置文件显示
修改settings.py文件
USE_L10N = False DATETIME_FORMAT = 'Y-m-d H:i:s'
此时html只需要写 {{ date }}
就显示格式化的日期
safe (转译,把传进来的字符串转译为html可识别的语言)
Django的模板中会对HTML标签和JS等语法标签进行自动转义,原因显而易见,这样是为了安全。但是有的时候我们可能不希望这些HTML元素被转义,比如我们做一个内容管理系统,后台添加的文章中是经过修饰的,这些修饰可能是通过一个类似于FCKeditor编辑加注了HTML修饰符的文本,如果自动转义的话显示的就是保护HTML标签的源文件。为了在Django中关闭HTML的自动转义有两种方式,如果是一个单独的变量我们可以通过过滤器“|safe”的方式告诉Django这段代码是安全的不必转义。
比如:
value = "<a href='#'>点我</a>"
{{ value|safe}}
Tags
for循环
<ul> {% for user in user_list %} <li>{{ user.name }}</li> {% endfor %} </ul>
for循环可用的一些参数:
Variable | Description |
---|---|
forloop.counter |
当前循环的索引值(从1开始) |
forloop.counter0 |
当前循环的索引值(从0开始) |
forloop.revcounter |
当前循环的倒序索引值(从1开始) |
forloop.revcounter0 |
当前循环的倒序索引值(从0开始) |
forloop.first |
当前循环是不是第一次循环(布尔值)如果是第一次显示True 后边的每次循环显示Flase |
forloop.last |
当前循环是不是最后一次循环(布尔值) |
forloop.parentloop |
本层循环的外层循环 |
def test(request): class dd_a(): def __init__(self, name, age): self.name = name self.age = age def dream(self): return "%s 1111" %(self.name) Wk = dd_a('wk',15) wc = dd_a('wc',13) ww = dd_a('ww',18) pp = [Wk,wc,ww] return render(request, "test.html",{'pp':pp} ) <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <ul> {% for ps in pp %} <li>{{ forloop.counter }} = {{ ps.name }}</li> {% endfor %} </ul> </body> </html>
for ... empty 当循环为空时走empty
{% for foo in all %} <tr> <td>{{ forloop.counter }}</td> <td>{{ foo.pk }}</td> <td>{{ foo.mane }}</td> <td> <a href="{% url 'app1:del' %}?sh={{ foo.pk }}" class="btn btn-danger btn-sm">删除</a> <a href="{% url 'app1:update' %}?zj={{ foo.pk }}" class="btn btn-primary btn-sm">编辑</a> </td> <td> {% empty %} {# 当for循环为空时,显示empty的内容 #} <td>空的</td> </td> </tr> {% endfor %}
if,elif和else
{% for foo in all %} <tr {% if forloop.first %} style="color: red" {% endif %}> #如果是第一次循环得出的结果则显示红色 <td>{{ forloop.counter }}</td> <td>{{ foo.pk }}</td> <td>{{ foo.mane }}</td> <td> <a href="{% url 'app1:del' %}?sh={{ foo.pk }}" class="btn btn-danger btn-sm">删除</a> <a href="{% url 'app1:update' %}?zj={{ foo.pk }}" class="btn btn-primary btn-sm">编辑</a> </td> <td> {% empty %} <td>空的</td> </td> </tr> {% endfor %}
{% if user_list %} 用户人数:{{ user_list|length }} {% elif black_list %} 黑名单数:{{ black_list|length }} {% else %} 没有用户 {% endif %}
当然也可以只有if和else
{% if user_list|length > 5 %} 七座豪华SUV {% else %} 黄包车 {% endif %}
if语句支持 and 、or、==、>、<、!=、<=、>=、in、not in、is、is not判断。
with 给变量起别名
{% with oldname as newname %} {{ newname }} {% endwith %}
csrf_token
这个标签用于跨站请求伪造保护。
在页面的form表单里面写上{% csrf_token %} ,这样不用注释中间件 也可以提交post请求
<form class="form-horizontal" action="" method="post"> {% csrf_token %} {# 加在form表单下 #}
母板
母版就是一个普通的页面,存放所有页面公共的代码,定义好block块,让子页面进行重写
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/css/bootstrap.min.css"> <link rel="stylesheet" href="/static/css/das.css"> </head> <body> <nav class="navbar navbar-inverse navbar-fixed-top"> <div class="container-fluid"> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">出版社管理</a> </div> <div id="navbar" class="navbar-collapse collapse"> <ul class="nav navbar-nav navbar-right"> <li><a href="#">Dashboard</a></li> <li><a href="#">Settings</a></li> <li><a href="#">Profile</a></li> <li><a href="#">Help</a></li> </ul> <form class="navbar-form navbar-right"> <input type="text" class="form-control" placeholder="Search..."> </form> </div> </div> </nav> <div class="container-fluid"> <div class="row"> <div class="col-sm-3 col-md-2 sidebar"> <ul class="nav nav-sidebar"> <li class="active"><a href="{% url 'app1:zhanshi' %}">出版社列表 <span class="sr-only">(current)</span></a></li> </ul> </div> <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main"> {% block main %} {% endblock %} {# 母版只保留公共部分,并且定义block来定位不同的部分#} </div> </div> </div>
继承{% extends 'layouts.html' %} 子页面继承母版,并且在block块里加上不同的东西
{% extends 'muban.html' %} {# 继承母版 #} {% block main %} {# 在blick 定位的地方加上不通的部分#} <h2 class="sub-header">出版社信息</h2> <div class="table-responsive"> <a href="{% url 'app1:add2' %}" class="btn btn-primary">添加</a> <table class="table table-striped table-hover"> <thead> <tr> <th>序号<th> <th>ID</th> <th>出版社名称</th> <th>操作</th> </tr> </thead> <tbody> {% for foo in all %} <tr {% if forloop.first %} style="color: red" {% endif %}> <td>{{ forloop.counter }}</td> <td>{{ foo.pk }}</td> <td>{{ foo.mane }}</td> <td> <a href="{% url 'app1:del' %}?sh={{ foo.pk }}" class="btn btn-danger btn-sm">删除</a> <a href="{% url 'app1:update' %}?zj={{ foo.pk }}" class="btn btn-primary btn-sm">编辑</a> </td> <td> {% empty %} <td>空的</td> </td> </tr> {% endfor %} </tbody> </table> </div> {% endblock %}
注意点:
1. {% extends 'muban.html' %} 写在第一行
2. 不同的内容写在这中间{% block main %}
3.子页面和主页面不同的静态css js也可以通过block 添加
组件
一小段公用的html代码 很多页面要用的 把这段代码保存到html文件里
可以将常用的页面内容如导航条,页尾信息等组件保存在单独的文件中,然后在需要使用的地方按如下语法导入即可。
{% include 'toubuxinxi.html' %}
公共头部文件代码 toubuxinxi.html
<nav class="navbar navbar-inverse navbar-fixed-top"> <div class="container-fluid"> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">出版社管理</a> </div> <div id="navbar" class="navbar-collapse collapse"> <ul class="nav navbar-nav navbar-right"> <li><a href="#">Dashboard</a></li> <li><a href="#">Settings</a></li> <li><a href="#">Profile</a></li> <li><a href="#">Help</a></li> </ul> <form class="navbar-form navbar-right"> <input type="text" class="form-control" placeholder="Search..."> </form> </div> </div> </nav>
在修改页面添加头部
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/css/bootstrap.min.css"> <link rel="stylesheet" href="/static/css/das.css"> </head> <body> {% include 'toubuxinxi.html' %} <br> <form class="form-horizontal" action="" method="post"> {% csrf_token %} <div class="form-group"> <label for="inputEmail3" class="col-sm-2 control-label">出版社名称</label> <div class="col-sm-10"> <input type="text" class="form-control" id="inputEmail3" placeholder="出版社名称" name="newname" value="{{ obj.mane }}"> </div> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <button type="submit" class="btn btn-default">修改</button> </div> </body> </html>
静态文件相关
{% load static %}
head里得引用静态文件的static是写死了
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/css/bootstrap.min.css"> <link rel="stylesheet" href="/static/css/das.css"> </head>
如果settings.py文件里的static修改了则每个引用都要改
STATIC_URL = '/static/' STATICFILES_DIRS = [ os.path.join(BASE_DIR,'static')
因此使用{% load static %}来解决
<head> <meta charset="UTF-8"> <title>Title</title> {% load static %} {# <link rel="stylesheet" href="/static/css/bootstrap.min.css">#} <link rel="stylesheet" href={% static "css/bootstrap.min.css"%}> {#变量名拼接相对路径#} {# <link rel="stylesheet" href="/static/css/das.css">#} <link rel="stylesheet" href={% static "css/das.css"%}> </head>
此时改变STATIC_URL = 的值页面也照常访问
STATIC_URL = '/11111/' STATICFILES_DIRS = [ os.path.join(BASE_DIR,'static')
{% load static %}的使用
{% load static %} <img src="{% static "images/hi.jpg" %}" alt="Hi!" />
引用JS文件时使用:
{% load static %} <script src="{% static "mytest.js" %}"></script>
某个文件多处被用到可以存为一个变量
{% load static %} {% static "images/hi.jpg" as myphoto %} <img src="{{ myphoto }}"></img>
使用get_static_prefix 取的是settings.py里STATIC_URL 的值
这种用法相当于使用绝对路径
{% load static %}
<img src="{% get_static_prefix %}images/hi.jpg" alt="Hi!" />
或者
{% load static %}
{% get_static_prefix as STATIC_PREFIX %}
<img src="{{ STATIC_PREFIX }}images/hi.jpg" alt="Hi!" />
<img src="{{ STATIC_PREFIX }}images/hi2.jpg" alt="Hello!" />
外键的操作
在数据库增加关联表
from django.db import models # Create your models here. class tusu(models.Model): pid = models.AutoField(primary_key=True) mane = models.CharField(max_length=32)
class Book(models.Model): #新增表Book title = models.CharField(max_length=32) #定义书名 tusu = models.ForeignKey('tusu',on_delete=models.CASCADE) #定义外键连接 连接的的表是tusu, on_delete=models.CASCADE 即如果出版社删除了对应的书也删除
变更数据库
python3 manage.py makemigrations
python3 manage.py migrate
新建的book表
加入数据后
做展示页
url略
views.py
def newzs(request): books = models.Book.objects.all() #查出book表的信息 return render(request,'newzs.html',{'books':books}) #传到前端
前端 用母版 拿外键的值直接点外键名(不要加_id)得到外键表的对象再点对应的key拿值
{% extends 'muban.html' %} {% block main %} <h2 class="sub-header">书籍信息</h2> <div class="table-responsive"> {# <a href="{% url 'app1:add2' %}" class="btn btn-primary">添加</a>#} <table class="table table-striped table-hover"> <thead> <tr> <th>序号<th> <th>ID</th> <th>出版社名称</th> <th>书名 </th> <th>操作</th> </tr> </thead> <tbody> {% for book in books %} <tr {% if forloop.first %} style="color: red" {% endif %}> <td>{{ forloop.counter }}</td> <td>{{ book.pk }}</td> <td>{{ book.tusu.mane }}</td> {# book.tusu点出来的不是外键的值而是对应的tusu表的对象,通过对象.key拿对应的值 #} <td>{{ book.title }}</td> <td> <a href="{% url 'app1:del' %}?sh={{ foo.pk }}" class="btn btn-danger btn-sm">删除</a> <a href="{% url 'app1:update' %}?zj={{ foo.pk }}" class="btn btn-primary btn-sm">编辑</a> </td> <td> {% empty %} <td>空的</td> </td> </tr> {% endfor %} </tbody> </table> </div> {% endblock %}
删除出版社 ,对应的书也没了
添加功能
url略
views.py
class newadd(View): def get(self,request): tusu = models.tusu.objects.all() #获取所有出版社的信息返回给前端 return render(request,'newadd.html',{'tusu':tusu}) def post(self,request): #post请求添加出版舍 newname = request.POST.get('newname') #获取要添加的书的名字 put = request.POST.get('put') #获取要添加的书名对应的外键的值 models.Book.objects.create(title=newname,tusu_id=put) #创建新数据 return redirect(reverse('app1:newzs'))
前端展示页功能 直接跳转
<a href="{% url 'app1:newadd' %}" class="btn btn-primary">添加</a>
添加页功能
<div class="table-responsive"> <form class="form-horizontal" action="" method="post"> <div class="form-group"> <label for="inputEmail3" class="col-sm-2 control-label">书名</label> <div class="col-sm-10"> <input type="text" class="form-control" id="inputEmail3" placeholder="书名" name="newname"> </div> </div> <div class="form-group"> <label for="inputEmail3" class="col-sm-2 control-label">出版社</label> <div class="col-sm-10"> <select name="put" id="inputEmail3" class="form-control"> {% for i in tusu %} <option value="{{ i.pk }}">{{ i.mane }}</option> {% endfor %} </select> </div> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <button type="submit" class="btn btn-default">添加</button> </div> </div> </form> </div>
修改功能
url
url(r'upda/(\d+)$', aap1vw.upda.as_view(),name='upda'), #使用分组的方法
views.py
class upda(View): def get(self,request,zh): #分组要在这里接收前端传来的分组的位置参数 Book表的主键 # zh = request.GET.get('zh') #这是之前的接受前端传来的?号的方法 现在用分组的方法 book_obg = models.Book.objects.get(pk=zh) tusu = models.tusu.objects.all() return render(request,'upda.html',{'book_obg':book_obg,'tusu':tusu}) def post(self,request,zh): book_obg = models.Book.objects.get(pk=zh) #拿到原有的书的对象 upname = request.POST.get('upname') #拿到修改后的书名 put = request.POST.get('put') #拿到修改后的书的对应的外键的值 book_obg.title = upname book_obg.tusu_id = put book_obg.save() return redirect(reverse('app1:newzs'))
展示页提交信息 #分组url得到的参数是Book表的主键
<a href="{% url 'app1:upda' book.pk %}" class="btn btn-primary btn-sm">编辑</a>
修改页功能
<div class="col-sm-10"> <input type="text" class="form-control" id="inputEmail3" placeholder="书名" name="upname" value="{{ book_obg.title }}"> </div> <div class="form-group"> <label for="inputEmail3" class="col-sm-2 control-label">出版社</label> <div class="col-sm-10"> <select name="put" id="inputEmail3" class="form-control"> {% for i in tusu %} {% if book_obg.tusu == i %} {# book_obg是前端提交的对象,加点外键对应的键即得到对应链接的对象 然后对比前端发来的对象是不是当次循环的对象 #} <option selected value="{{ i.pk }}">{{ i.mane }}</option> {# 如果循环列表该次循环的对象是前端get提交来的对象 是就 selected 选中 #} {% else %} <option value="{{ i.pk }}">{{ i.mane }}</option> {# 如果不是就不选中 #} {% endif %} {% endfor %} </select> </div> </div>
删除功能将出版社和图书的删除合并
url 定义两个分组分别传数据库对面的名字和对应的主键
url(r'del_(tusu|book)/(\d+)/$', aap1vw.Del,name='Del'),
前端传出的值
1.出版社传出的值为 对应的数据库名 和对应 的主键值
<a href="{% url 'app1:Del' 'tusu' foo.pk %}" class="btn btn-danger btn-sm">删除</a>
2. 图书传出的值为 对应的数据库名 和对应的 主键值
<a href="{% url 'app1:Del' 'book' book.pk %}" class="btn btn-danger btn-sm">删除</a>
views.py 文件逻辑
def Del(request,zz,hh): #分别接收前端传来的两个参数 if zz == 'tusu': #图书的返回图书的展示页,出版社的返回出版社的 dd = 'app1:zhanshi' elif zz == 'Book': #此处可以优化 把展示页的别名改为对应数据库对象的名字 dd = 'app1:newzs' #可以省略以上4行代码 Obj = getattr(models,zz) #反射得到models 里的对应的图书的或出版社的数据库对象 Obj.objects.get(pk=hh).delete() #删除对应的数据库对象的选中的主键 return redirect(reverse(dd)) #返回对应的展示页