django1

* <pre>━━━━━━神兽出没━━━━━━

*   ┏┓   ┏┓

*  ┏┛┻━━━┛┻┓

*  ┃   王   ┃

*  ┃         ┃

*  ┃ ┳┛ ┗┳ ┃

*  ┃       ┃

*  ┃   ┻   ┃

*  ┃       ┃

*  ┗━┓   ┏━┛

*    ┃   ┃    神兽保佑,代码无bug

*    ┃   ┃

*    ┃   ┗━━━┓

*    ┃       ┣┓

*    ┃       ┏┛

*    ┗┓┓┏━┳┓┏┛

*     ┃┫┫ ┃┫┫

*     ┗┻┛ ┗┻┛

*</pre>

本练习作者源于虫师

1、环境配置

  1. 安装django

pip install django

  1. 配置环境变量

将下面路径添加到系统环境变量的path中

C:\Users\12978\AppData\Local\Programs\Python\Python36-32\Scripts

在cmd.exe中运行:django-admin help

出现以下图表示配置成功!

django1

3、创建django项目

1.创建项目

方法一:点击:file-->new project,输入项目名称,创建项目。

方法二:win+r弹窗中输入命令cmd命令,输入:django-admin,罗列出 Django 所提供给我们的命令,其中使用“startproject”命令来创建项目,把项目创建在了D盘。

切换文件创建位置命令:cd /d d:

django1

输入创建项目名称:django-admin startproject guest      #项目名为guest

项目结构如下:

django1

与项目同名的目录中是配置文件,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

django1

目录如下:

django1

这样就创建了一个叫做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.项目地址配置:

django1

方法一:启动项目命令:>python manage.py runserver

方法二:直接使用pycharm运行,打开链接地址;

django1

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

django1

2.使用post请求

将 form 表单的中的属性改为 method="post",会报错,报错信息如下:

“CSRFverificationfailed.Requestaborted.”

( Cross-SiteRequestForgery, CSRF)Django 针对 CSRF 的保护措施是在生成的每个表单中放置一个自动生成的令牌,通过这个令牌判断 POST 请求是否来自同一个网站。

解决办法1:需要在 from 表单中添加{% csrf_token %}。

django1

解决办法2:在setting.py文件中注释掉csrf.

django1

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!'})

运行运行程序:

django1

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的运行页面:

django1

3.读取session时的问题

将cookie替换后再次登录会报错:“nosuchtable:django_session”

这个错误跟 Session 的机制有关,要服务器端记录用户的数据,那么一定要有地方来存放用户 Sessionid 对应的信息才对。所以,我们需要创建 django_session 表。

命令:python3 manage.py migrate

如下:

django1

在 guest 项目的根目录下会生成一个 db.sqlite3 文件.

django1

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 数据类型:

django1

2.数据迁移

1)命令:python manage.py makemigrations cmdb

使用命令生成数据表时的问题:

event=models.ForeignKey(Event)  关联外键会报错,外键值的后面加上 on_delete=models.CASCADE

报错信息:

django1

原因:在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就可以了。

修改后再次执行命令:

django1

2)命令2:python manage.py migrate

django1

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后台:

django1

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)

运行程序:

django1

3.数据访问

命令: python manage.py shell

通过shell命令,在该模式下操做数据表操作。

命令:from sign.models import Event, Guest  #导入cmdb应用下的 models.py 中的 Event 表和 Guest 表。

命令:Event.objects.all()    #获得 table(Event、Gues 表)中的所有对象。

django1

3.Sqlitestudio

SQLiteStudio 是一个跨平台的 SQLite 数据库的管理工具.

下载地址:http://sqlitestudio.pl/

django1

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

运行成功页面:

django1

更换了数据库,所以,Admin 后台超级管理员账号(admin/admin123456)也需要重新创建:

用户名为:admin   密码:admin123456

命令:python manage.py createsuperuser

django1

django1

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),

运行:

django1

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})

运行:

django1

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})

运行:

django1

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" 属性表示链接在新窗口打开。

django1

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>

运行:

django1

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()视图函数返回的提示信息的位置。

django1

运行:

django1

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

django1

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)

解决办法:

django1

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)” 。

上一篇:Thread基本介绍


下一篇:inspect模块详解