* <pre>━━━━━━神兽出没━━━━━━
* ┏┓ ┏┓
* ┏┛┻━━━┛┻┓
* ┃ 王 ┃
* ┃ ┃
* ┃ ┳┛ ┗┳ ┃
* ┃ ┃
* ┃ ┻ ┃
* ┃ ┃
* ┗━┓ ┏━┛
* ┃ ┃ 神兽保佑,代码无bug
* ┃ ┃
* ┃ ┗━━━┓
* ┃ ┣┓
* ┃ ┏┛
* ┗┓┓┏━┳┓┏┛
* ┃┫┫ ┃┫┫
* ┗┻┛ ┗┻┛
*</pre>
本练习作者源于虫师
1、环境配置
- 安装django
pip install django
- 配置环境变量
将下面路径添加到系统环境变量的path中
C:\Users\12978\AppData\Local\Programs\Python\Python36-32\Scripts
在cmd.exe中运行:django-admin help
出现以下图表示配置成功!
3、创建django项目
1.创建项目
方法一:点击:file-->new project,输入项目名称,创建项目。
方法二:win+r弹窗中输入命令cmd命令,输入:django-admin,罗列出 Django 所提供给我们的命令,其中使用“startproject”命令来创建项目,把项目创建在了D盘。
切换文件创建位置命令:cd /d d:
输入创建项目名称:django-admin startproject guest #项目名为guest
项目结构如下:
与项目同名的目录中是配置文件,templates目录是html文件存放也就是MTV中的T。manage.py是django项目管理文件。
guest/__init__.py:一个空的文件,用它标识一个目录为 Python 的标准包。
guest/settings.py:Django 项目的配置文件,包括 Django 模块应用配置,数据库配置,模板配置等。
guest/urls.py:Django 项目的 URL 声明。
guest/wsgi.py:为 WSGI 兼容的 Web 服务器服务项目的切入点。
manage.py:一个命令行工具,可以让你在使用 Django 项目时以不同的方式进行交互。
查看 manage 所提供的命令:python manage.py
2.创建APP
每个django项目中可以包含多个APP,相当于一个大型项目中的分系统、子模块、功能部件等等,相互之间比较独立,但也有联系。所有的APP共享项目资源。
在pycharm下方的terminal终端中输入命令:python manage.py startapp cmdb
目录如下:
这样就创建了一个叫做cmdb的APP,django自动生成“cmdb”文件夹。
migrations/:用于记录 models 中数据的变更。
admin.py:映射 models 中的数据到 Django 自带的 admin 后台。
apps.py:在新的 Django 版本中新增,用于应用程序的配置。
models.py:创建应用程序数据表模型(对应数据库的相关操作)。
tests.py:创建 Django 测试。
views.py:控制向前端显示哪些数据。
4.编写路由
路由都在urls文件里,它将浏览器输入的url映射到相应的业务处理逻辑。
编写路由需要用的正则表达式,
r 字符串前面加“ r ”是为了防止字符串中出现类似“\t”字符时被转义。
^ 匹配字符串开头;在多行模式中匹配每一行的开头。 ^abc abc
$ 匹配字符串末尾;在多行模式中匹配每一行末尾
通过^index/$ 匹配到/index/目录。并且将处理指向 sign 应用的视图文件 views.py 的 index 函数
在guest文件夹得urls.py编写
Django2.0中path无法匹配正则表达式,用下面的方法
re_path(r'^index/$',views.index),
1.项目地址配置:
方法一:启动项目命令:>python manage.py runserver
方法二:直接使用pycharm运行,打开链接地址;
URL 地栏输入:http://127.0.0.1:8000/或者http://localhost:8080/
修改Url.py文件,添加index路径
from django.contrib import admin
from django.urls import path,re_path
from cmdb import views #导入模块
urlpatterns = [
#re_path('admin/', admin.site.urls),
re_path(r'^index/$',views.index), 添加路径配置
5. views视图编写业务处理逻辑
业务处理逻辑都在views.py文件里。
from django.http import HttpResponse def index(request): return HttpResponse("")
运行后http://localhost:8080/index返回内容为102
6.返回HTML文件
1.创建 templates/index.html 文件
<html> <head> <title>Django Page</title> </head> <body> <h1>Hello Django!</h1> </body> </html>
2.修改views.py文件
导入的 render 函数。该函数的第一个参数是请求对象的,第二个参 数返回一个 index.html 页面。
from django.shortcuts import render
from django.http import HttpResponse def index(request): return render(request,"index.html") # return HttpResponse("102")'''
4、views视图get/post请求案例
GET- 从指定的资源请求数据。
POST- 向指定的资源提交要被处理的数据
1.登录使用get请求,修改index.html
<html lang="en"> <head> <meta charset="UTF-8"> <title>Django page</title> </head> <body> <h1>发布管理</h1> <form method="POST" > <input name="username" type="text" placeholder="username"><br> <input name="password" type="password" placeholder="password"><br> <button id="btn" type="submit">登录</button> </form> </body> </html>
请求地址:运行后http://localhost:8080/index
输入用户名和密码,查看用户提交的数据添加到url地址中:
http://localhost:8080/index/?username=admin&password=admin123
2.使用post请求
将 form 表单的中的属性改为 method="post",会报错,报错信息如下:
“CSRFverificationfailed.Requestaborted.”
( Cross-SiteRequestForgery, CSRF)Django 针对 CSRF 的保护措施是在生成的每个表单中放置一个自动生成的令牌,通过这个令牌判断 POST 请求是否来自同一个网站。
解决办法1:需要在 from 表单中添加{% csrf_token %}。
解决办法2:在setting.py文件中注释掉csrf.
3.处理登录请求
1.修改index.html文件
#通过 form 表单的 action 属性来指定提交的路径,,添加{{error}},它对应 render 返回字典中的 key,并且在登录失败 的页面中显示 value,
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Django page</title>
</head>
<body>
<h1>发布管理</h1>
<form method="POST" action="/login_action/">
<input name="username" type="text" placeholder="username"><br>
<input name="password" type="password" placeholder="password"><br>
{{ error }}<br>
<button id="btn" type="submit">登录</button>
</form>
</body>
</html>
2.修改urls.py文件
from django.urls import path,re_path
from cmdb import views urlpatterns = [
#re_path('admin/', admin.site.urls),
re_path(r'^index/$',views.index),
re_path(r'^login_action/$',views.login_action),
3.修改views.py
from django.shortcuts import render
from django.http import HttpResponse def index(request): return render(request,"index.html") #登录 def login_action(request): if request.method =='POST': username = request.POST.get('username','') password = request.POST.get('password','') if username == 'admin' and password=='': return HttpResponse(‘login success!’) else: return render(request,'index.html', {'error': 'username or passworderror!'})
运行运行程序:
4.添加登录成功页面
1.创建/templates/event_manage.html 页面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Event Manage Page</title> </head> <body> <h1>Login Success!</h1> </body> </html>
2.修改views.py
from django.shortcuts import render from django.http import HttpResponse,HttpResponsePermanentRedirect def index(request): return render(request,"index.html") #登录 def login_action(request): if request.method =='POST': username = request.POST.get('username','') password = request.POST.get('password','') user = auth.authenticate(username=username, password=password) if username == 'admin' and password=='': return HttpResponsePermanentRedirect('/event_manage/') else: return render(request,'index.html', {'error': 'username or passworderror!'}) else: return render(request,'index.html', {'error': 'username or passworderror!'}) #发布会管理 def event_manage(request): return render(request,"event_manage.html",{"user":username})
又用到的一个新的类 HttpResponseRedirect,它可以对路径进行重定向,从而将登录成功之后的请求 指向/event_manage/目录。
创建 event_manage 函数,用于返回发布会管理 event_manage.html 面页
3.修改urls.py文件
from django.contrib import admin
from django.urls import path,re_path
from cmdb import views urlpatterns = [
#re_path('admin/', admin.site.urls),
re_path(r'^index/$',views.index),
re_path(r'^login_action/$',views.login_action),
re_path(r'^event_manage/$',views.event_manage), #添加路由
重新运行!
5. Cookie 和 Session
session和cookie的作用都是为了存储用户相关的信息。不同的是,cookie是存储在本地浏览器,而session存储在服务器。存储在服务器的数据会更加的安全,不容易被窃取。但存储在服务器也有一定的弊端,就是会占用服务器的资源,但现在服务器已经发展至今,一些session信息还是绰绰有余的。
1.修改views.py文件
from django.shortcuts import render
from django.http import HttpResponse,HttpResponsePermanentRedirect def index(request): return render(request,"index.html") # return HttpResponse("102")''' #登录 def login_action(request): if request.method =='POST': username = request.POST.get('username','') password = request.POST.get('password','') if username == 'admin' and password=='': response= HttpResponsePermanentRedirect('/event_manage/') #response.set_cookie('user',username,3600) #添加浏览器cookie request.session['user']=username #把cookie替换为 session 信息记录到浏览器 else: return render(request,'index.html', {'error': 'username or passworderror!'}) #发布会管理 def event_manage(request): #username = request.COOKIES.get('user','') ##读取浏览器cookie username=request.session.get('user','') # 读取浏览器 session return render(request,"event_manage.html",{"user":username})
set_cookie()方法传了三个参数,第一个参数“user”是用于表示写入浏览器的 Cookie 名,第二个 参数 username 是由用户在登录页上输入的用户名,第三个参数 3600 用于表示该 cookie 信息在浏览器中的停 留时间,默认以秒为单位。
而在 event_manage 视图函数中,通过 request.COOKIES 来读取 Cookie 名为“user”的值。并且通过 render 将和 event_manage.html 页面一起返回给客户端浏览器。
2.修改/event_manage.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Event Manage Page</title>
</head>
<body>
<h1>Login Success!</h1>
<div style="float:right;"><a>嘿!{{ user }} 欢迎</a><hr/> </div>
</body>
</html>
Cookie的运行页面:
3.读取session时的问题
将cookie替换后再次登录会报错:“nosuchtable:django_session”
这个错误跟 Session 的机制有关,要服务器端记录用户的数据,那么一定要有地方来存放用户 Sessionid 对应的信息才对。所以,我们需要创建 django_session 表。
命令:python3 manage.py migrate
如下:
在 guest 项目的根目录下会生成一个 db.sqlite3 文件.
6、django模型
1.设计系统表
Django 提供完善的模型(model)层主要用来创建和存取数据,不需要我们直接对数据库操作。每个模型是一个 Python 类,继承 django.db.models.model 类。该模型的每个属性表示一个数据库表字段。每个字段都指定为一个类属性,每个属性都映射到一个数据库列。自动生成的数据库访问的 API。
1.打开.../sign/models.py 文件,完成表的创建。
from django.db import models from django.utils import timezone # Create your models here. #发布会 class Event(models.Model): name=models.CharField(max_length=100) #发布会标题 limit=models.IntegerField(blank=True, null=True) #参加人数 status=models.BooleanField(blank=True, null=True) #状态 address=models.CharField(max_length=200) #地址 start_time=models.DateTimeField(default=timezone.now) #发布会时间 create_time=models.DateTimeField(default=timezone.now) #创建时间(自动获取时间) def __str__ (self): return self.name #嘉宾表 class Guest(models.Model): #event=models.ForeignKey(Event) 关联外键会报错,外键值的后面加上 on_delete=models.CASCADE event=models.ForeignKey('Event',on_delete=models.CASCADE) #关联发布会 realname=models.CharField(max_length=64) #姓名 phone=models.CharField(max_length=16) #手机号 email=models.EmailField(blank=True, null=True) #邮箱 sign=models.BooleanField(blank=True, null=True) #签到状态 create_time=models.DateTimeField(default=timezone.now) #创建时间(自动获取时间) class Meta: unique_together=("event","phone") def __str__(self): return self.realname
对于产品发布会来说,显然它是一个事件。那么时间、地点、人物等要素必不可少。数据库表的设计需
要围绕着这些要素进行。
关于发布会表(Event 类)和嘉宾表(Guest 类)的每一个字段,在代码中已经做了注解。有些字段的设 计需要做一下简单的说明。
首先,发布会表和嘉宾表中默认都会生成自增 id,而我们在创建模型时不需要声明该字段。
其次,发布会表中增加了 status 字段用于表示发布会的状态是否开启,用于控制该发布会是否可用。
再次,嘉宾表中通过 event_id 关联发布会表,一条嘉宾信息一定所属于某一场发布会。
最后,对于一场发布会来说,一般会选择手机号作为一位嘉宾的验证信息,所以,对于一场发布会来说, 手机号必须是唯一。除了嘉宾 id 外,这里通过发布会 id+手机号来做为联合主键。
__str__()方法告诉 Python 如何将对象以 str 的方式显示出来。所以,为每个模型类添加了__str__()方法。
Django 数据类型:
2.数据迁移
1)命令:python manage.py makemigrations cmdb
使用命令生成数据表时的问题:
event=models.ForeignKey(Event) 关联外键会报错,外键值的后面加上 on_delete=models.CASCADE
报错信息:
原因:在django2.0后,定义外键和一对一关系的时候需要加on_delete选项,此参数为了避免两个表里的数据不一致问题,不然会报错:
TypeError: __init__() missing 1 required positional argument: 'on_delete'
举例说明:
user=models.OneToOneField(User)
owner=models.ForeignKey(UserProfile)
需要改成:
user=models.OneToOneField(User,on_delete=models.CASCADE) --在老版本这个参数(models.CASCADE)是默认值
owner=models.ForeignKey(UserProfile,on_delete=models.CASCADE) --在老版本这个参数(models.CASCADE)是默认值
参数说明:
on_delete有CASCADE、PROTECT、SET_NULL、SET_DEFAULT、SET()五个可选择的值
CASCADE:此值设置,是级联删除。
PROTECT:此值设置,是会报完整性错误。
SET_NULL:此值设置,会把外键设置为null,前提是允许为null。
SET_DEFAULT:此值设置,会把设置为外键的默认值。
SET():此值设置,会调用外面的值,可以是一个函数。
一般情况下使用CASCADE就可以了。
修改后再次执行命令:
2)命令2:python manage.py migrate
2.Admin后台管理
们创建的发布会和嘉宾表 同样可以通过 Admin 后台去管理。
1.修改cmdb/admin.py文件
from django.contrib import admin from cmdb.models import Event, Guest # Register your models here. admin.site.register(Event) admin.site.register(Guest)
运行,查看admin后台:
2.修改admin.py文件,显示更多字段
from django.contrib import admin from cmdb.models import Event, Guest # Register your models here. class EventAdmin(admin.ModelAdmin): #新建EventAdmin 类,继承 django.contrib.admin.ModelAdmin 类,保存着一个类的自定义配置,以供Admin 管理工具使用 #list_display:字段名称的数组,用于定义要在列表中显 示哪些字段,字段名称必须是模型中的 Event()类定义的 list_display = ['name','status','start_time','id'] search_fields = ['name'] #搜索栏 search_fields 用于创建表字段的搜索器,可以设置搜索关键字匹配多个表字段 list_filter = ['status'] #过滤器 用于创建字段过 class GuestAdmin(admin.ModelAdmin): list_display = ['realname','phone','email','sign','create_time','event'] search_fields = ['realname','phone'] #搜索栏 list_filter = ['sign'] #过滤器 admin.site.register(Event,EventAdmin) #:用 EventAdmin 选项注册 Event 模块。 admin.site.register(Guest,GuestAdmin)
运行程序:
3.数据访问
命令: python manage.py shell
通过shell命令,在该模式下操做数据表操作。
命令:from sign.models import Event, Guest #导入cmdb应用下的 models.py 中的 Event 表和 Guest 表。
命令:Event.objects.all() #获得 table(Event、Gues 表)中的所有对象。
3.Sqlitestudio
SQLiteStudio 是一个跨平台的 SQLite 数据库的管理工具.
4.安装mysql
mysql安装包:https://dev.mysql.com/downloads/file/?id=479141
环境:Django2.1+mysql版本5.7 ,因为5.5得不支持
5.安装 PyMySQL
命令:>python -m pip install PyMySQL
6.Django配置mysql
1.修改guest/settings.py文件
DATABASES = { 'default': { #'ENGINE': 'django.db.backends.sqlite3', #'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 'ENGINE': 'django.db.backends.mysql', 'HOST': '127.0.0.1', 'PORT': '', 'NAME': 'guest', 'USER': 'root', 'PASSWORD': 'root', 'OPTIONS': { 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'", } } }
注意:切换了数据库后,之前 Sqlite3 数据库里的数据并不能复制到 MySQL 中,所以需要重新进行数据 库同步,使数据模型重新在 MySQL 数据库中生成表。在编辑器中创建guest数据库。
3.修改guest/__init__.py
Django通过 PyMySQL驱动来连接MySQL数据库
import pymysql pymysql.install_as_MySQLdb()
4.执行数据库同步
命令:python manage.py migrat
运行成功页面:
更换了数据库,所以,Admin 后台超级管理员账号(admin/admin123456)也需要重新创建:
用户名为:admin 密码:admin123456
命令:python manage.py createsuperuser
7、Django模板
1.Django-bootstrap3
项目是将 BootStrap3(3 表示版本号)集成到 Django 中,作为 Django 的一个应用提供。 这样做的好处是在 Django 中用 bootstrap 会更加方便。
命令安装: pip install django-bootstrap3
1.修改guest/setting.py文件,添加bootstrap3
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'cmdb', 'bootstrap3', ]
2.发布会管理
1.发布会列表,修改 event_manage()视图。
from sign.models import Event,Guest #发布会管理 @login_required def event_manage(request): event_list=Event.objects.all() #用于查询所有发布会对象 #username = request.COOKIES.get('user','') ##读取浏览器cookie username=request.session.get('user','') # 读取浏览器 session return render(request,"event_manage.html",{"user":username, "events":event_list}) #通过 render()函数附加在 event_manage.html 页面 返回给客户端浏览器。 #发布会名称搜索 @login_required def search_name(request): username = request.session.get('user', '') search_name = request.GET.get("name", "") event_list = Event.objects.filter(name__contains=search_name) return render(request, "event_manage.html", {"user": username, "events": event_list})
2.修改/templates/event_manage.html 页面
<!DOCTYPE html> <html lang="en"> <head> {% load bootstrap3 %} {% bootstrap_css %} {% bootstrap_javascript %} <meta charset="UTF-8"> <title>Guest Manage</title> </head> <body role="document"> <!-- 导航栏 --> <nav class="navbar navbar-inverse navbar-fixed-top"> <div class="container"> <div class="navbar-header"> <a class="navbar-brand" href="/event_manage/">Guest Manage System</a> </div> <div id="navbar" class="collapse navbar-collapse"> <ul class="nav navbar-nav"> <li class="active"><a href="#">发布会</a></li> <li><a href="/guest_manage/">嘉宾</a></li> </ul> <ul class="nav navbar-nav navbar-right"> <li><a href="#">{{user}}</a></li> <li><a href="/logout/">退出</a></li> </ul> </div><!--/.nav-collapse --> </div> </nav> <!--发布会表单--> <div class="page-header" style="padding-top: 60px;"> <div id="navbar" class="navbar-collapse collapse"> <form class="navbar-form" method="get" action="/search_name/"> <div class="form-group"> <input name="name" type="text" placeholder="名称" class="form-control"> </div> <button type="submit" class="btn btn-success">搜索</button> </form> </div><!--/.navbar-collapse --> </div> <!--发布会列表--> <div class="row" style="padding-top: 80px;"> <div class="col-md-6"> <table class="table table-striped"> <thead> <tr> <th>id</th> <th>名称</th> <th>状态</th> <th>地址</th> <th>时间</th> <th style="width: 45px;">签到</th> <th>签到正式</th> </tr> </thead> <tbody> {% for event in events %} <tr> <td>{{ event.id }}</td> <td>{{ event.name }}</td> <td>{{ event.status }}</td> <td>{{ event.address }}</td> <td>{{ event.start_time }}</td> <td><a href="/sign_index/{{ event.id }}/" target="{{ event.id }}_blank">sign</a></td> <td><a href="/sign_index2/{{ event.id }}/" target="{{ event.id }}_blank">sign_web</a></td> </tr> {% endfor %} </tbody> </table> </div> </div> </body> </html>
3.添加搜索路径的路由/guest/urls.py
re_path(r'^search_name/$',views.search_name),
运行:
3.嘉宾管理
1.创建/templates/guest_manage.html
<!DOCTYPE html> <html lang="en"> <head> {% load bootstrap3 %} {% bootstrap_css %} {% bootstrap_javascript %} <meta charset="UTF-8"> <title>Title</title> </head> <body role="document"> <!-- 导航栏 --> <nav class="navbar navbar-inverse navbar-fixed-top"> <div class="container"> <div class="navbar-header"> <a class="navbar-brand" href="/guest_manage/">Guest Manage System</a> </div> <div id="navbar" class="collapse navbar-collapse"> <ul class="nav navbar-nav"> <li><a href="/event_manage/">发布会</a></li> <li class="active"><a href="#about">嘉宾</a></li> </ul> <ul class="nav navbar-nav navbar-right"> <li><a href="#">{{user}}</a></li> <li><a href="/logout/">退出</a></li> </ul> </div><!--/.nav-collapse --> </div> </nav> < <!--发布会表单--> <div class="page-header" style="padding-top: 60px;"> <!-- 搜索功能--> <div id="navbar" class="navbar-collapse collapse"> <form class="navbar-form" method="get" action="/search_phone/"> <div class="form-group"> <input name="phone" type="text" placeholder="手机号" class="form-control"> </div> <button type="submit" class="btn btn-success">搜索</button> </form> </div><!--/.navbar-collapse --> </div> <!--嘉宾列表 --> <div class="row" style="padding-top: 80px;"> <div class="col-md-6"> <table class="table table-striped"> <thead> <tr> <th>id</th> <th>名称</th> <th>手机</th> <th>Email</th> <th>签到</th> <th>发布会id</th> </tr> </thead> <tbody> {% for guest in guests %} <tr> <td>{{ guest.id }}</td> <td>{{ guest.realname }}</td> <td>{{ guest.phone }}</td> <td>{{ guest.email }}</td> <td>{{ guest.sign }}</td> <td>{{ guest.event }}</td> </tr> {% endfor %} </tbody> </table> </div> </div> <!-- 列表分页器 --> <div class="pagination"> <span class="step-links"> {% if guests.has_previous %} <a href="?phone={{ phone }}&page={{ guests.previous_page_number }}">previous</a> {% endif %} <span class="current"> Page {{ guests.number }} of {{ guests.paginator.num_pages }}. </span> {% if guests.has_next %} {% if phone %} <a href="?phone={{ phone }}&page={{ guests.next_page_number }}">next</a> {% else %} <a href="?page={{ guests.next_page_number }}">next</a> {% endif %} {% endif %} </span> </div> </body> </html>
2./guest/urls.py 文件中添加嘉宾路径的路由
re_path(r'^guest_manage/$',views.guest_manage), re_path(r'^search_phone/$',views.search_phone),
3.views.py 文件,创建 guest_manage()视图函数
from cmdb.models import Event,Guest # 嘉宾管理 @login_required def guest_manage(request): username = request.session.get('user', '') guest_list = Guest.objects.all() return render(request, "guest_manage.html", {"user": username, "guests": guest_list}) #嘉宾手机号搜索 @login_required def search_phone(request): username = request.session.get('user', '') search_phone = request.GET.get("phone", "") guest_list = Guest.objects.filter(phone__contains=search_phone) return render(request, "guest_manage.html", {"user": username, "guests": guest_list})
运行:
4.分页,修改views.py
from django.core.paginator import Paginator,EmptyPage,PageNotAnInteger # 嘉宾管理 @login_required def guest_manage(request): username = request.session.get('user', '') guest_list = Guest.objects.all() paginator = Paginator(guest_list, 2) page = request.GET.get('page') try: contacts = paginator.page(page) except PageNotAnInteger: contacts = paginator.page(1) except EmptyPage: contacts = paginator.page(paginator.num_pages) return render(request, "guest_manage.html", {"user": username, "guests": contacts})
运行:
4.签到
1.templates/event_manage.html 页面增加签到链接
<td> <a href="/sign_index/{{ event.id }}/" target="{{ event.id }}_blank"> sign</a> </td>
注释:当点击 sign 链接时,路径会默认跳转到“/sign_index/{{event.id }}/”路径。其中{{event.id}} 为发布会 的 id。target="{{event.id}}_blank" 属性表示链接在新窗口打开。
2./guest/urls.py 文件中添加路径路由。
re_path(r'^sign_index/(?P<event_id>[0-9]+)/$', views.sign_index),
注释:(?P<event_id>[0-9]+) 配置二级目录,发布会 id,要求必须为数字。而且匹配的数字,将会作为 sign_index() 视图函数的参数。
3./sign/views.py 文件,创建 sign_index()视图函数
from django.shortcuts import render,get_object_or_404 #签到页面 @login_required def sign_index(request,event_id): event=get_object_or_404(Event,id=event_id) return render(request,'sign_index.html',{'event':event})
4.创建.../templates/sign_index.html 签到页面
<!DOCTYPE html> <html lang="en"> <head> {% load bootstrap3 %} {% bootstrap_css %} {% bootstrap_javascript %} <meta charset="UTF-8"> <title>签到</title> </head> <body> <!-- 导航栏 --> <nav class="navbar navbar-inverse navbar-fixed-top"> <div class="container"> <div class="navbar-header"> <a class="navbar-brand" href="#">{{ event.name }}</a> </div> <div id="navbar" class="collapse navbar-collapse"> <ul class="nav navbar-nav"> <li><a href="/event_manage/">发布会</a></li> <li><a href="/guest_manage/">嘉宾</a></li> </ul> </div><!--/.nav-collapse --> </div> </nav> <!--签到表单--> <div class="page-header" style="padding-top: 80px;"> <div id="navbar" class="navbar-collapse collapse"> <form class="navbar-form" action="/sign_index_action/{{ event.id }}/" method="post"> <div class="input-group"> <input type="text" class="form-control" placeholder="输入手机号" name="phone"> </div> <button type="submit" class="btn btn-success">签到</button><br> <font color="#663399"> <br>{{ hint }} <br>{{ guest.realname }} <br>{{ guest.phone }} </font> </form> </div><!-- /.col-lg-6 --> </div><!-- /.row --> </div> </body> </html>
运行:
5.签到动作
1.guest/urls.py 文件,添加签到路径的路由
re_path(r'^sign_index_action/(?P<event_id>[0-9]+)/$', views.sign_index_action),
2.sign/views.py 文件,创建 sign_index_action()视图函数
# 签到动作 @login_required def sign_index_action(request,event_id): event = get_object_or_404(Event, id=event_id) phone = request.POST.get('phone','') result = Guest.objects.filter(phone = phone) if not result: return render(request, 'sign_index.html', {'event': event, 'hint': 'phone error.'}) result = Guest.objects.filter(phone=phone,event_id=event_id) if not result: return render(request, 'sign_index.html', {'event': event, 'hint':'eventidorphoneerror.'}) result = Guest.objects.get(phone=phone,event_id=event_id) if result.sign: return render(request, 'sign_index.html', {'event': event, 'hint': "user has sign in."}) else: Guest.objects.filter(phone=phone,event_id=event_id).update(sign = '') return render(request, 'sign_index.html', {'event': event, 'hint':'sign in success!', 'guest': result})
注释:查询 Guest 表判断用户输入的手机号是否存在,如果不存在将提示用户“手机号为空或不存在”。
然后,通过手机和发布会 id 两个条件来查询 Guest 表,如果结果为空将提示用户“该用户未参加此次发布会”。最后,再通过手机号查询 Guest 表,判断该手机号的签到状态是否为 1,如果为 1,表示已经签过到了, 返回用户“已签到”,否则,将提示用户“签到成功!”,并返回签到用户的信息。
3.修改.../templates/sign_index.html 页面,增加 sign_index_action()视图函数返回的提示信息的位置。
运行:
5.退出登录
1./urls.py 文件,添加退出目录的路由
re_path(r'^logout/$', views.logout),
2./views.py 文件,创建 logout()视图函数
# 退出登录
@login_required
def logout(request):
auth.logout(request) #退出登录
response = HttpResponsePermanentRedirect('/index/')
return response
8、Django测试
1.unittest framework
单元测试:unittest
HTTP 接口自动化测试:unittest+Requests
WebUI 自动化测试:unittest+Selenium
移动自动化测试:unittest+Appium
1.Django 的单元测试使用 Python 标准库模块:unittest。该模块定义使用基于类的方法测试。在我们创建 Django 应用时,默认已经帮我们生成了 tests.py 文件,打开.../sign/tests.py 文件,编写测试如下代码:
from django.test import TestCase from cmdb.models import Event,Guest from datetime import datetime from django.contrib.auth.models import User ''' # Create your tests here. class ModelTest(TestCase): #创建ModelTest类,继承django.test的TestCase类 #模型测试 #在 setUp()初始化方法中,创建一条发布会和嘉宾数据 def setUp(self): Event.objects.create(id=1, name="oneplus 3 event", status=True, limit=2000, address='shenzhen', start_time='2016-08-31 02:18:22') Guest.objects.create(id=1,event_id=1, realname='huahua', phone='13711001101',email='alen@mail.com', sign=False) def test_event_models(self): #测试发布会表 result = Event.objects.get(name="oneplus 3 event") self.assertEqual(result.address, "shanghai") self.assertTrue(result.status) def test_guest_models(self): #测试嘉宾表 result = Guest.objects.get(phone='17322222222') self.assertEqual(result.realname, "huahua") self.assertFalse(result.sign) ''' class IndexPageTest(TestCase): #测试index登录首页 def test_index_page_renders_index_template(self): #断言是否用给定的index.html模版响应 response = self.client.get('/index/') #client.get()方法从TestCase父类继承而来,用于请求一个路径, self.assertEqual(response.status_code, 200) #assertEqual()服务器对客户端的应答是否 为 200, self.assertTemplateUsed(response, 'index.html') #assertTemplateUsed()断言是否用给定的是 index.html模版响应 ''' class LoginActionTest(TestCase): #测试登录动作 def setUp(self): User.objects.create_user('admin', 'admin@mail.com', 'admin123456') def test_add_author_email(self): #测试添加用户 # user = User.objects.get(username="admin") self.assertEqual(user.username, "admin") self.assertEqual(user.email, "admin@mail.com") def test_login_action_username_password_null(self): # 用户名密码为空 response = self.client.post('/login_action/', {'username': '', 'password': ''}) self.assertEqual(response.status_code, 200) self.assertIn(b"username or password null!", response.content) def test_login_action_username_password_error(self): # 用户名密码错误 response = self.client.post('/login_action/', {'username': 'abc', 'password': '123'}) self.assertEqual(response.status_code, 200) self.assertIn(b"username or password error!", response.content) def test_login_action_success(self): #登录成功 response = self.client.post('/login_action/', data={'username': 'admin', 'password': 'admin123456'}) self.assertEqual(response.status_code, 302) class EventMangeTest(TestCase): # 发布会管理 def setUp(self): User.objects.create_user('admin', 'admin@mail.com', 'admin123456') Event.objects.create(name="xiaomi5", limit=2000, address='beijing', status=1, start_time='2017-8-10 12:30:00') login_user = {'username': 'admin', 'password': 'admin123456'} self.client.post('/login_action/', data=login_user) # 预先登录 def test_add_event_data(self): #测试添加发布会 event = Event.objects.get(name="xiaomi5") self.assertEqual(event.address, "beijing") def test_event_mange_success(self): # 测试发布会:xiaomi5 response = self.client.post('/event_manage/') self.assertEqual(response.status_code, 200) self.assertIn(b"xiaomi5", response.content) self.assertIn(b"beijing", response.content) def test_event_mange_search_success(self): # 测试发布会搜索 response = self.client.post('/search_name/', {"name": "xiaomi5"}) self.assertEqual(response.status_code, 200) self.assertIn(b"xiaomi5", response.content) self.assertIn(b"beijing", response.content) class GuestManageTest(TestCase): # 嘉宾管理 def setUp(self): User.objects.create_user('admin', 'admin@mail.com', 'admin123456') Event.objects.create(id=1,name="xiaomi5", limit=2000, address='beijing', status=1, start_time='2017-8-10 12:30:00') Guest.objects.create(realname="alen", phone=18611001100,email='alen@mail.com', sign=0, event_id=1) login_user = {'username': 'admin', 'password': 'admin123456'} self.client.post('/login_action/', data=login_user) # 预先登录 def test_add_guest_data(self): # 测试添加嘉宾 guest = Guest.objects.get(realname="alen") self.assertEqual(guest.phone, "18611001100") self.assertEqual(guest.email, "alen@mail.com") self.assertFalse(guest.sign) def test_event_mange_success(self): # 测试嘉宾信息: alen response = self.client.post('/guest_manage/') self.assertEqual(response.status_code, 200) self.assertIn(b"alen", response.content) self.assertIn(b"18611001100", response.content) def test_guest_mange_search_success(self): #测试嘉宾搜索 response = self.client.post('/search_phone/',{"phone":"18611001100"}) self.assertEqual(response.status_code, 200) self.assertIn(b"alen", response.content) self.assertIn(b"18611001100", response.content) class SignIndexActionTest(TestCase): # 发布会签到 def setUp(self): User.objects.create_user('admin', 'admin@mail.com', 'admin123456') Event.objects.create(id=1, name="xiaomi5", limit=2000, address='beijing', status=1, start_time='2017-8-10 12:30:00') Event.objects.create(id=2, name="oneplus4", limit=2000, address='shenzhen', status=1, start_time='2017-6-10 12:30:00') Guest.objects.create(realname="alen", phone=18611001100, email='alen@mail.com', sign=0, event_id=1) Guest.objects.create(realname="una", phone=18611001101, email='una@mail.com', sign=1, event_id=2) login_user = {'username': 'admin', 'password': 'admin123456'} self.client.post('/login_action/', data=login_user) def test_sign_index_action_phone_null(self): #手机号为空 response = self.client.post('/sign_index_action/1/', {"phone": ""}) self.assertEqual(response.status_code, 200) self.assertIn(b"phone error.", response.content) def test_sign_index_action_phone_or_event_id_error(self): # 手机号或发布会id错误 response = self.client.post('/sign_index_action/2/', {"phone": "18611001100"}) self.assertEqual(response.status_code, 200) self.assertIn(b"event id or phone error.", response.content) def test_sign_index_action_user_sign_has(self): # 用户已签到 response = self.client.post('/sign_index_action/2/', {"phone": "18611001101"}) self.assertEqual(response.status_code, 200) self.assertIn(b"user has sign in.", response.content) def test_sign_index_action_sign_success(self): # 签到成功 response = self.client.post('/sign_index_action/1/', {"phone": "18611001100"}) self.assertEqual(response.status_code, 200) self.assertIn(b"sign in success!", response.content) ''' ''' 切换到项目的根目录下,通过 manage.py 所提供的“test”命令运行测试 测试运行命令方式: 1、运行所有用例: python manage.py test 2、运行cmdb应用下的所有用例: python manage.py test cmdb 3、运行cmdb应用下的tests.py文件用例: python manage.py test cmdb.tests 4、运行 cmdb 应用 tests.py 测试文件下的 ModelTest 测试类 python manage.py test sign.tests.ModelTest 5、执行 ModelTest 测试类下面的 test_event_models 测试方法(用例): python manage.py test sign.tests.ModelTest.test_event_models 6、使用 -p (或 --pattern)参数模糊匹配测试文件 >python manage.py test -p test*.py ...... '''
运行D:\guest>python manage.py test
2.其中发现两个错误和解决办法
1.C:\Users\12978\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pymysql\cursors.py:170: Warning: (3135, "'NO_ZERO_DATE', 'NO_ZERO_IN_DATE' and 'ERROR_FOR_DIVISION
_BY_ZERO' sql modes should be used with strict mode. They will be merged with strict mode in a future release.")
result = self._query(query)
C:\Users\12978\AppData\Local\Programs\Python\Python36-32\lib\site-packages\pymysql\cursors.py:170: Warning: (3090, "Changing sql mode 'NO_AUTO_CREATE_USER' is deprecated. It
will be removed in a future release.")
result = self._query(query)
解决办法:
2.报错信息解决方案:
Django还有一些warning打印出来:/Users/jay/workspace/te/env/lib/python2.7/site-packages/django/db/models/fields/__init__.py:903: RuntimeWarning: DateTimeField TestSuite.update_time received a naive datetime (2014-06-15 14:38:37.873873) while time zone support is active. RuntimeWarning)
这个warning的原因是,Django配置为使用timezone的datetime格式,而datetime.now是不包含timezone信息的。
如果不需要在程序中特别处理时区(timezone-aware),在Django项目的settings.py文件中,可以直接设置为“USE_TZ = False”就省心了。然后,在models.py中简单的设置为“ create_time = models.DateTimeField(auto_now_add=True)”和“update_time = models.DateTimeField(auto_now=True)”。
如还要保持USE_TZ=True,则可设置为“default=datetime.now().replace(tzinfo=utc)” 。