Python的100天打卡(上)

2019-05-08  本文已影响0人  CrazySteven

首先附上Python的官方中文文档
在刷微信中看到了一个百天打卡学习Python的项目,就参加了,下面是笔记。

5.8

1.历史
2.优缺点
3.应用领域
4.三大平台安装Python
5.编写hello,word并运行
6.介绍注释
7.IDE及插件
8.作业:


顺便运行了下小猪佩奇的代码,画的不错


5.9

1.“冯·诺依曼结构”
2.数据类型
3.变量命名规则
4.运算符
5.作业:
练习1:华氏温度转摄氏温度

f = input('请输入华氏温度:')
try:
    f = float(f)
except:
    print('请输入正确的华氏温度')
else:
    c = 5 * (f - 32) / 9
    print('华氏温度 %.2f = %.2f 摄氏度' % (f,c))

练习2:输入圆的半径计算计算周长和面积

import math
r = input('请输入圆的半径:')
try:
    r = float(r)
except:
    print('请输入正确的半径')
else:
    c = 2 * math.pi * r
    s = math.pi * r * r
    print('圆的周长为:%.2f,面积为:%.2f' % (c,s))

练习3:输入年份判断是不是闰年

year = input('请输入年份:')
try:
    year = int(year)
except:
    print('请输入正确的年份')
else:
    if (year > 0 and year < 9999):
        if((year % 4 == 0) and (year % 100 != 0)):
            print('%d是闰年' % year)
        elif year % 400 == 0:
            print('%d是闰年' % year)
        else:
            print('%d是平年' % year)
    else:
        print('请输入正确的年份')

5.10

1.if判断
2.作业:
练习1:英制单位与公制单位互换

value = input('请输入长度:')
try:
    value = float(value)
except:
    print('请输入正确的长度')
else:
    unit = input('请输入单位: ')
    if unit == 'in' or unit == '英寸':
        print('%.2f英寸 = %.2f厘米' % (value, value * 2.54))
    elif unit == 'cm' or unit == '厘米':
        print('%.2f厘米 = %.2f英寸' % (value, value / 2.54))
    else:
        print('请输入正确的单位')

练习2:掷骰子决定做什么

from random import randint
def dosth(var):
    return {
        1 : '好',
        2 : '很好',
        3 : '真好',
        4 : '太好',
        5 : '非常好',
        6 : '666'
    }.get(var,'error')
shaizi = randint(1, 6)
print(dosth(shaizi))

练习3:百分制成绩转等级制

s = input('请输入成绩: ')
try:
    s = float(s)
except:
    print('请输入正确的成绩')
else:
    if s >= 90:
        g = 'A'
    elif s >= 80:
        g = 'B'
    elif s >= 70:
        g = 'C'
    elif s >= 60:
        g = 'D'
    else:
        g = 'E'
    print('%.2f对应的等级: %c' % (s,g))

练习4:输入三条边长如果能构成三角形就计算周长和面积

import math
a = input('请输入整数边长a: ')
b = input('请输入整数边长b: ')
c = input('请输入整数边长c: ')
try:
    a = int(a)
    b = int(b)
    c = int(c)
except:
    print('请输入正确的边长a,b,c')
else:
    if a + b > c and b + c > a and a + c > b:
        perimeter = a+b+c
        area = math.sqrt((a + b + c) * (a + b - c) * (a + c - b) * (b + c - a)) / 4
        print('周长为:%d, 面积为:%.2f' % (perimeter, area))
    else:
        print('输入的三边不能构成三角形')

练习5:个人所得税计算器

新税法看了发现好麻烦,各种扣除累计的,不写了

5.11

1.for-in循环
2.while循环
3.作业:
练习1:输入一个数判断是不是素数

import math
n = input('请输入一个整数: ')
try:
    n = int(n)
except:
    print('请输入正确的数')
else:
    isP = n > 1
    m = int(math.sqrt(n))
    for i in range(2, m+1):
        if n % i == 0:
            isP = False
            break
    if isP: print('%d 是素数' % n)
    else  : print('%d 不是素数' % n)

练习2:输入两个正整数,计算最大公约数和最小公倍数

# " / "就表示 浮点数除法,返回浮点结果;" // "表示整数除法(向下取整)
m = input('请输入一个整数m: ')
n = input('请输入一个整数n: ')
try:
    m = int(m)
    n = int(n)
except:
    print('请输入正确的数')
else:
    if m < n:
        m, n = n, m
    for i in range(m, m-n, -1):
        if m % i == 0 and n % i == 0:
            print('%d和%d的最大公约数是:%d,最小公倍数是:%d' % (m, n, i, (m * n // i)))
            break

练习3:打印三角形图案

# 变量_表示并不使用此变量,只是为了阅读,忽略即可
# 参数end代表结尾字符,默认是换行/n
r = input('请输入行数r: ')
try:
    r = int(r)
except:
    print('请输入正确的行数')
else:
    if r < 1:
        print('请输入正确的行数')
    else:
        for i in range(r):
            for _ in range(r - i - 1):
                print(' ', end='')
            for _ in range(2 * i + 1):
                print('*', end='')
            print()

5.12

今日主要是复习和总结,只有5个练习。
1.寻找“水仙花数”

# 最简单直接的暴搜,我就不优化了
for x in range(1, 10):
    for y in range(0, 10):
        for z in range(0, 10):
            i = x * 100
            j = y * 10
            l = x * x * x
            m = y * y * y
            n = z * z * z
            if i+j+z == l+m+n:
                print(i+j+z)

2.寻找“完美数”

# 我只找1000内的完美数,同样的暴搜
for x in range(1, 1001):
    sum = 0
    for i in range(1, x):
        if x % i == 0:
            sum += I
    if(sum == x):print(x)

3.“百钱百鸡”

# 继续暴搜,简单无脑
x = 5
y = 3
z = 1 / 3
for i in range(0, 100 // x):
    for j in range(0, 100 // y):
        if (x * i + y * j + z * (100 - i - j) == 100):
            print('公鸡%d只,母鸡%d只,小鸡%d只' % (i,j,100-i-j))

4.生成“斐波拉切数列”

# 就列100个吧
r = [1,1]
while len(r) < 100:
    r.append(r[-1] + r[-2])
print(r)

5.Craps赌博游戏

# 只要继续赌,就没有所谓的赢
from random import randint
money = 1000
while money > 0:
    print('你的总资产为:', money)
    needs_go_on = False
    while True:
        debt = int(input('请下注: '))
        if debt > 0 and debt <= money:
            break
        else:print('您下注的金额不正确,请重新下注')
    first = randint(1, 6) + randint(1, 6)
    print('玩家摇出了%d点' % first)
    if first == 7 or first == 11:
        print('玩家胜!')
        money += debt
    elif first == 2 or first == 3 or first == 12:
        print('庄家胜!')
        money -= debt
    else:
        needs_go_on = True
    while needs_go_on:
        current = randint(1, 6) + randint(1, 6)
        print('玩家摇出了%d点' % current)
        if current == 7:
            print('庄家胜')
            money -= debt
            needs_go_on = False
        elif current == first:
            print('玩家胜')
            money += debt
            needs_go_on = False
print('You are bankrupt and the game is over!')

5.13

1.认识函数
2.函数的定义def
3.函数的参数
4.用模块管理函数
4.1 from 文件名 import 函数名
4.2 import 文件名 as 变量名
4.3 防止误运行,文件中加 if __name__ == '__main__':
5.练习:
练习1:实现计算求最大公约数和最小公倍数的函数

def gys(m, n):
    if m < n:
        m, n = n, m
    for i in range(m, m-n, -1):
        if m % i == 0 and n % i == 0:
            print('%d和%d的最大公约数是:%d,最小公倍数是:%d' % (m, n, i, (m * n // i)))
            
if __name__ == '__main__':
    m = input('请输入一个整数m: ')
    n = input('请输入一个整数n: ')
    try:
        m = int(m)
        n = int(n)
    except:
        print('请输入正确的数')
    else:
        gys(m, n)

练习2:实现判断一个数是不是回文数的函数

def hws(n):
    return n == n[::-1]

if __name__ == '__main__':
    n = input('请输入一个数n: ')
    try:
        int(n)
    except:
        print('请输入正确的数')
    else:
        print(n, '是回文数:', hws(n))

练习3:实现判断一个数是不是素数的函数

import math
def sushu(n):
    isP = n > 1
    m = int(math.sqrt(n))
    for i in range(2, m+1):
        if n % i == 0:
            isP = False
            break
    return isP

if __name__ == '__main__':
    n = input('请输入一个整数: ')
    try:
        n = int(n)
    except:
        print('请输入正确的数')
    else:
        print(n, '是素数:', sushu(n))

练习4:写一个程序判断输入的正整数是不是回文素数

import math
def hws(n):
    return n == n[::-1]

def sushu(n):
    isP = n > 1
    m = int(math.sqrt(n))
    for i in range(2, m+1):
        if n % i == 0:
            isP = False
            break
    return isP

def hwss(n):
    if hws(n):
        return sushu(m)
    else:
        return False

if __name__ == '__main__':
    n = input('请输入一个整数: ')
    try:
        m = int(n)
    except:
        print('请输入正确的数')
    else:
        print(n, '是回文素数:', hwss(n))

6.变量作用域(局部”->“嵌套nonlocal”->“全局global”->“内置”)
7.代码书写:

def main():
    # Todo: Add your code here
    pass


if __name__ == '__main__':
    main()

5.14

1.字符串
2.列表
3.元组
4.集合
5.字典
6.练习
练习1:在屏幕上显示跑马灯文字

import os
import time
def main():
    content = '这是一条跑马灯广告…………'
    while True:
        os.system('clear')  # windows用cls
        print(content)
        time.sleep(0.2)
        content = content[1:] + content[0]


if __name__ == '__main__':
    main()

练习2:设计一个函数产生指定长度的验证码,验证码由大小写字母和数字构成

import random
def creatCode(n = 6):
    all = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
    code = ''
    while len(code) < n:
        i = random.randint(0, len(all) - 1)
        code += all[I]
    return code

if __name__ == '__main__':
    print(creatCode())

练习3:设计一个函数返回给定文件名的后缀名

def findName(fileName):
    return fileName.split('.')[-1]

if __name__ == '__main__':
    print(findName('abc.mp3'))

练习4:设计一个函数返回传入的列表中最大和第二大的元素的值

def towMax(numlist):
    numlist.sort()
    return numlist[-1], numlist[-2]

if __name__ == '__main__':
    print(towMax([1,9,7,45,8,4,8,15,2,5]))

练习5:计算指定的年月日是这一年的第几天

# 就不对年月日做限制了
def yearToDay():
    year  = input('请输入年:')
    month = input('请输入月:')
    day   = input('请输入天:')
    try:
        year = int(year)
        month = int(month)
        day = int(day)
    except:
        print('请输入正确年月日')
    days = [31,28,31,30,31,30,31,31,30,31,30,31]
    sum = day
    i = 0
    if (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0):
        days[1] = 29
    while i < month-1:
        sum += days[I]
        i += 1
    print('这是该年的第',sum,'天')

if __name__ == '__main__':
    yearToDay()

练习6:打印杨辉三角

def yanghui():
    num = input('请输入行数:')
    try:
        num = int(num)
    except:
        print('请输入正确的行数')
    else:
        yh = [[]] * num
        for row in range(len(yh)):
            yh[row] = [0] * (row + 1)
            for col in range(len(yh[row])):
                if col == 0 or col == row:
                    yh[row][col] = 1
                else:
                    yh[row][col] = yh[row - 1][col] + yh[row - 1][col - 1]
                print(yh[row][col], end='\t')
            print()

if __name__ == '__main__':
    yanghui()

7.案例
案例1:双色球选号(sample函数)
案例2:约瑟夫环问题
案例3:井字棋游戏


5.15

1.面向对象编程的历史原因
2.定义类
2.1 class
2.2 def __init__(self, *args):
2.3 写在类中的函数,我们通常称之为(对象的)方法,这些方法就是对象可以接收的消息
3.创建和使用对象
4.private(__ + 属性名或方法名),public
5.封装
6.练习
练习1:定义一个类描述数字时钟

import time
class Clock(object):
    '''时间'''
    def __init__(self, h=0, m=0, s=0):
        '''初始化'''
        self.h = h
        self.m = m
        self.s = s
    def cal(self):
        '''计算时间'''
        self.s += 1
        if self.s == 60:
            self.s = 0
            self.m += 1
            if self.m == 60:
                self.m = 0
                self.h += 1
                if self.h == 24:
                    self.h = 0
    def __str__(self):
        '''显示时间'''
        return '%02d:%02d:%02d' % \
               (self.h, self.m, self.s)

if __name__ == '__main__':
    c = Clock(time.localtime().tm_hour, time.localtime().tm_min, time.localtime().tm_sec)
    while True:
        print(c)
        time.sleep(1)
        c.cal()

练习2:定义一个类描述平面上的点并提供移动点和计算到另一个点距离的方法

# 本来还加了个abs,后面反应过来平方了就不需要abs了
import math
class Point(object):
    '''时间'''
    def __init__(self, x=0, y=0):
        '''初始化'''
        self.x = x
        self.y = y
    def moveTo(self, x, y):
        self.x = x
        self.y = y
    def distance(self, p):
        '''计算距离'''
        a = p.x - self.x
        b = p.y - self.y
        return math.sqrt(a ** 2 + b ** 2)
    def __str__(self):
        '''描述自己'''
        return '(%s, %s)' % (str(self.x), str(self.y))

if __name__ == '__main__':
    p1 = Point(1,2)
    p2 = Point(3,5)
    print(p1,'\n',p2)
    p1.moveTo(10,6)
    print(p1)
    print(p1.distance(p2))

5.16

1.@property装饰器(getter)
2.@属性名.setter
3.绑定__slots__不继承
4.@staticmethod静态方法(参数无self)
5.@classmethod类方法(参数cls)
6.类的关系:is-a继承,has-a关联(聚合,合成),use-a依赖
7.继承:子类重写(override)父类(可用于抽象类)方法后,出现多态
8.@abstractmethod抽象方法,子类必须重写
9.三个案例,竟然没有练习,轻松一天,哈哈。。。

这张UML的图挺好:

5.17

今天的主要课程是GUI,由于以前接触过,用过PyQt等,发现Python的GUI虽然是跨平台,但体验很差,效率很低,个人认为目前用Python做GUI开发并不理想,所以今天的课程只做了解。
1.tkinter
2.Pygame开发游戏
3.Pygame的官方网站


5.18

1.文件操作(r默认读,w写,x不覆盖的写,a追加,b二进制,t文本模式,+重置更新)
2.open(路径,权限,编码)
3.open错误类型(FileNotFoundError,LookupError,UnicodeDecodeError)
4.finally关闭文件释放资源---“总是执行代码块”(try-except-else-finally)
5.读(read,for-in,readlines)
6.写(w,a追加式写入)
7.json模块(dump-load文件,dumps-loads字符串)
8.requests模块
9.《总结:Python中的异常处理》


5.19

今日课程主要是讲正则,用的时候再学吧,以前学过,不用就忘记了。。
《正则表达式30分钟入门教程》


5.20

1.进程和线程,几核CPU几个进程,一个进程内可有多个线程
2.并发编程:多进程、多线程、多进程+多线程
3.os.getpid()获取当前进程的PID,os.getppid()获取父进程的PID
4.注意zombie
5.os.fork(),fork函数一旦运行就会生出一条新的进程,2个进程一起执行,有2个返回值,返回值为大于0时,此进程为父进程,且返回的数字为子进程的PID;当返回值为0时,此进程为子进程。父进程结束时,子进程并不会随父进程立刻结束。同样,父进程不会等待子进程执行完
6.Process对象的start方法用来启动进程,而join方法表示等待进程执行结束。Process的参数一定要加,,当时看的时候以为作者笔误多了个,,后来发现不加的话,传进去的字符串每个字节都成了一个参数。。。
6.每个进程会复制父类的变量,解决:Queue类是可以被多个进程共享的队列
7.线程Thread
8.需要对“临界资源”加锁Lock()-acquire()-release()
9.Python的多线程并不能发挥CPU的多核特性“全局解释器锁”(GIL)
10.单进程单线程+异步I/O执行多任务(Ngix,Node.js),此编程模型称为协程,优势:不用线程切换、不用锁
11.案例:
1.将耗时间的任务放到线程中以获得更好的用户体验
2.使用多进程对复杂任务进行“分而治之”
12.可以将多个进程部署在不同的计算机上,做成分布式进程:通过multiprocessing.managers模块中提供的管理器将Queue对象通过网络共享出来(注册到网络上让其他计算机可以访问)


5.21

  1. 计算机网络发展史
  2. TCP/IP模型(自下向上:网络接口层、网络层、传输层、应用层,TCP:传输控制协议,功能:基于IP提供的寻址和路由服务而建立起来的负责实现端到端可靠传输的协议。IP:网际协议,功能:寻址和路由)
  3. 网络应用模式(C/S模式,B/S模式。C为客户端,B为浏览器,S为服务器)
  4. HTTP超文本传输协议,是一种用于分布式、协作式和超媒体信息系统的应用层协议。
  5. JSON是一种轻量级的数据交换语言,用来传输由属性值或者序列性的值组成的数据对象。
  6. requests是一个基于HTTP协议来使用网络的第三方库。
  7. socket套接字(TCP和UDP)
  8. SMTP(简单邮件传输协议),建立在TCP提供的可靠数据传输服务的基础上的应用级协议。
  9. 使用互亿无线短信平台发送短信

5.22

  1. 图像(RGBA,pixel)
  2. Pillow可以实现图像压缩和图像处理等各种操作。
  3. openpyxl读取和读取和修改Excel
  4. python-docx创建和修改Word文档
  5. 下面把例子整理成了一个类
from PIL import Image, ImageFilter

class GCImage(object):
    def __init__(self, path):
        self.path = path
        self.image = Image.open(self.path)
    def show(self):
        print(self.image.format, self.image.size, self.image.mode)
        self.image.show()
    def crop(self, rect):
        '''剪裁'''
        self.image.crop(rect).show()
    def thumb(self, size):
        '''缩略图'''
        self.image.thumbnail(size)
        self.show()
    def paste(self, img, rect, size):
        '''粘贴'''
        nImg = img.crop(rect)
        width, height = nImg.size
        width, height = int(width / 1.5), int(height / 1.5)
        self.image.paste(nImg.resize((width ,height)), size)
        self.show()
    def roAndTr(self, angle, direction):
        '''旋转和翻转'''
        self.image.rotate(angle).show()     #作者github中写成了image.rotata(180).show()
        self.image.transpose(direction).show()
    def makePixel(self, xRange, yRange, RGB):
        '''操作像素'''
        for x in xRange:
            for y in yRange:
                self.image.putpixel((x, y), rgb)
        self.image.show()
    def filter(self, mode):
        '''滤镜'''
        self.image.filter(mode).show()

if __name__ == '__main__':
    path1 = '.\\guido.jpg'
    img1 = GCImage(path1)
    # img1.show()

    # img1.crop((20,20,300,300))

    # img1.thumb((50,50))

    # path2 = '.\\luohao.png'
    # img2 = GCImage(path2)
    # rect = 80, 20, 310, 360
    # size = 172, 40
    # img2.paste(img1.image, rect, size)

    # img1.roAndTr(180, Image.FLIP_LEFT_RIGHT)

    # xRange = range(80,310)
    # yRange = range(20,360)
    # rgb = (66,66,66)
    # img1.makePixel(xRange, yRange, rgb)

    img1.filter(ImageFilter.CONTOUR)

5.23

数据结构和算法
1.渐近时间复杂度

2.渐近空间复杂度
3.排序算法-简单排序-搅拌排序-顺序查找-折半查找
4.使用生成式(推导式)语法
5.嵌套的列表
6.heapq、itertools
7.collections模块
8.常用算法


5.24

函数的使用方式
1.高阶函数(filter,map)

# filter用法(判断是否是素数,第一个参数为方法名,第二个参数为传入方法的参数)
# filter是通过生成 True 和 False 组成的迭代器将可迭代对象中不符合条件的元素过滤掉;而map返回的则是 True 和 False 组成的迭代器。
import math

def isPrime(n):
    isP = n > 1
    m = int(math.sqrt(n))
    for i in range(2, m+1):
        if n % i == 0:
            isP = False
            break
    return isP

if __name__ == '__main__':
    resultF = filter(isPrime, range(1,10))
    resultM = map(isPrime,range(1,10))
    print(list(resultF))
    print(list(resultM))

2.匿名函数和内联函数(lambda)
3.搜索变量顺序(Local->Embedded->Global->Built-in),关键字:global,nonlocal(声明使用嵌套作用域的变量,嵌套作用域内必须有该变量,否则报错)
4.装饰器函数(@wraps,记得VNPY就是用这个进行日志的记录;通过func.__wrapped__方式获得被装饰前的函数或类,来取消装饰器的作用)


5.25

面向对象相关知识
1.继承
2.封装
3.多态
4.类与类的关系:is-a(继承),has-a(关联/聚合/合成),use-a(依赖)
5.对象的复制(深复制/深拷贝/深度克隆和浅复制/浅拷贝/影子克隆)
6.垃圾回收、循环引用和弱引用(iOS中的ARC、MRC)
7.元编程和元类
8.面向对象设计原则

9.设计模式


5.26

迭代器和生成器
1.生成器yield

2.迭代器__iter____next__

3.当没有下一个元素的时候会抛出StopIteration错误
4.可迭代对象Iterable:
4.1 list,tuple,dict,set,str等集合数据类型
4.2 generator,包括生成器和带yield的generator function
4.3 集合数据类型用iter创建成Iterator
5.使用isinstance()判断一个对象是否为可Iterable对象

from collections import Iterable
isinstance([], Iterable)

def fib(n):
    i, a, b = 0, 0, 1
    while i < n:
        yield b
        a, b = b, a+b
        i += 1

if __name__ == '__main__':
    a = fib(10)
    try:
        x = a.__next__()
        while x:
            print(x)
            x = a.__next__()
    except StopIteration:
        print('输出完成')

5.27

并发编程
1.并发编程的三种方案:多线程(Thread类)、多进程(Process类)和异步I/O。
2.进程和线程的区别和联系:进程-操作系统分配内存的基本单位(一个进程可以包含一个或多个线程);线程-操作系统分配CPU的基本单位
3.并发编程优点:1)执行性能-让程序中没有因果关系的部分可以并发的执行;2)改善用户体验-让耗时间的操作不会造成程序的假死
4.临界资源就是被多个线程竞争的资源
5.锁(Lock/RLock)-保护临界资源,解决多个线程竞争一个资源的问题
6.信号量(Semaphore)-解决多个线程竞争多个资源(线程数>资源数)
7.threading模块的Condition实现线程调度(暂停线程执行/唤醒等待中的线程)
8.多线程场景:

9.多进程场景:

10.通过asyncio模块和awaitasync关键字来支持异步处理

import asyncio
loop = asyncio.get_event_loop()
future = asyncio.gather(prime_filter(2, 100), square_mapper(1, 100))
future.add_done_callback(lambda x: print(x.result()))
loop.run_until_complete(future)
loop.close()

get_event_loop获取系统默认的事件循环,通过gather函数获取future对象
11.aiohttp库提供了异步的HTTP客户端和服务器
12.还有很多第三方库,需要用的时候再google吧。。。


5.28-6.7(本来是十天的内容,但中间耽误了一天)

1.书:HTML and CSS: Design and Build Websites
2.HTML简史
3.H5新特性
4.标签:
4.1结构

+ head
    - title
    - meta
+ body

4.2常用标签

5.css
6.JavaScript
6.1 BOM浏览器对象模型
6.2 DOM文档对象模型

7.jQuery
8.Ajax
9.前端框架
9.1 渐进式框架Vue.js(话说只要不用学完就忘,去年学这个学了一段时间,能做简单的事情,现在已经忘完了。)
9.2 UI框架Element
9.3 报表框架-ECharts
9.4 基于弹性盒子的CSS框架Bulma
9.5 响应式布局框架Bootstrap


6.8-6.12 Linux

1.操作系统发展史(纸带->批处理系统->分时和实时系统->通用操作系统)
2.Linux概述
3.Linux优点
4.基础命令(命令名称 [参数] [对象])

5.文件和文件夹操作

6.管道和重定向

7.别名alias/unalias
8.日期和时间date/cal

9.文件系统
9.1 目录

9.2 访问权限(chmod修改权限/chown修改文件所有者)
10.磁盘管理(df查看使用情况/fdisk分区表操作/mkfs格式化/fsck文件系统检查/mount挂载/umount卸载)
11.编辑器Vim

12.软件安装和配置
12.1 包管理工具yum(Yellowdog Updater Modified)

12.2 rpm(Redhat Package Manager)

13.配置服务

14.计划任务crontab
15.网络访问和管理

16.进程管理

17.系统性能


6.13-6.18(中间又偷懒了一天)

1.NoSQL按存储类型分为:

2.Redis(高速缓存/排行榜/商品秒杀/投票点赞/分布式锁/消息队列)

2.1 Python中用Redis

import redis

def useRedis():
    client = redis.Redis(host='127.0.0.1', port=6379, password='')
    client.set('username', 'admin')
    client.hset('student', 'name', 'hao')
    client.hset('student', 'age', 38)
    print(client.keys('*'))
    print(client.get('username'))
    print(client.hgetall('student'))

if __name__ == '__main__':
    useRedis()

3.MongoDB
3.1关于安装及运行在以前的文章已经写过了,这里就不写了
3.2通过Shell操作MongoDB


3.3 在Python中使用Mongo

from pymongo import *

def useMongo():
    client = MongoClient('mongodb://127.0.0.1')
    db = client.school
    for student in db.students.find():
        print('学号:', student['stuid'])
        print('姓名:', student['name'])
    totalNum(db)
    db.students.remove()
    totalNum(db)
    coll = db.students
    coll.create_index([('stuid', ASCENDING)], unique=True)
    coll.insert_one({'stuid': int(1001), 'name': 'Steven', 'gender': True})
    coll.insert_many(
        [{'stuid': int(1002), 'name': '王大锤', 'gender': False}, {'stuid': int(1003), 'name': '白元芳', 'gender': True}])
    for student in coll.find({'gender': True}):
        print('学号:', student['stuid'])
        print('姓名:', student['name'])
        print('性别:', '男' if student['gender'] else '女')

def totalNum(db):
    num = db.students.find().count()
    print('数量:', num)

if __name__ == '__main__':
    useMongo()

4.关系型数据库(OracleDB2SQL ServerMySQLPostgreSQL)
5.MySQL
5.1关于MySQL的安装以前也写过,这里就不写了。区别是现在可以在安装的时候直接设置密码了,这点挺好。
5.2基本命令

5.3数据定义语言DDL(Data Definition Language)
5.4数据操纵语言DML(Data Manipulation Language)
5.5数据查询语言DQL(Data Query Language)
5.6数据控制语言DCL(Data Control Language)
5.7相关知识
5.8在Python中使用mysql

import pymysql
from pymysql.cursors import DictCursor

"""
先创建两张表,数据自己填
CREATE TABLE tb_dept (`no` VARCHAR(100) NOT NULL, `name` VARCHAR(100) NOT NULL, `loc` VARCHAR(100) NOT NULL);
CREATE TABLE tb_emp (`no` VARCHAR(100) NOT NULL, `name` VARCHAR(100) NOT NULL, `job` VARCHAR(100) NOT NULL, `sal` VARCHAR(100) NOT NULL );
"""

def addDepartment(con):
    no = int(input('编号: '))
    name = input('名字: ')
    loc = input('所在地: ')
    try:
        # 通过连接对象获取游标
        with con.cursor() as cursor:
            # 通过游标执行SQL并获得执行结果
            result = cursor.execute(
                'insert into tb_dept values (%s, %s, %s)',
                (no, name, loc)
            )
        if result == 1:
            print('添加成功!')
        # 操作成功提交事务
        con.commit()
    finally:
        # 关闭连接释放资源
        con.close()

def delDepartment(con):
    no = int(input('编号: '))
    try:
        with con.cursor() as cursor:
            result = cursor.execute(
                'delete from tb_dept where no=%s',
                (no,)
            )
        if result == 1:
            print('删除成功!')
    finally:
        con.close()

def updDepartment(con):
    no = int(input('编号: '))
    name = input('名字: ')
    loc = input('所在地: ')
    try:
        with con.cursor() as cursor:
            result = cursor.execute(
                'update tb_dept set name=%s, loc=%s where no=%s',
                (name, loc, no)
            )
        if result == 1:
            print('更新成功!')
    finally:
        con.close()

def finDepartment(con):
    try:
        with con.cursor(cursor=DictCursor) as cursor:
            cursor.execute('select no as dno, name as dname, loc as dloc from tb_dept')
            results = cursor.fetchall()
            print(results)
            print('编号\t名称\t\t所在地')
            for dept in results:
                print(dept['dno'], end='\t')
                print(dept['dname'], end='\t')
                print(dept['dloc'])
    finally:
        con.close()

class Emp(object):
    def __init__(self, no, name, job, sal):
        self.no = no
        self.name = name
        self.job = job
        self.sal = sal

    def __str__(self):
        return f'\n编号:{self.no}\n姓名:{self.name}\n职位:{self.job}\n月薪:{self.sal}\n'

def findEmpInfo(con):
    page = int(input('页码: '))
    size = int(input('大小: '))
    try:
        with con.cursor() as cursor:
            cursor.execute(
                'select no as eno, name as ename, job, sal from tb_emp limit %s,%s',
                ((page - 1) * size, size)
            )
            for emp_tuple in cursor.fetchall():
                emp = Emp(*emp_tuple)
                print(emp)
    finally:
        con.close()

if __name__ == '__main__':
    # 创建数据库连接对象
    con = pymysql.connect(host='localhost', port=3306,
                          database='mysql', charset='utf8',
                          user='root', password='stevenabc999')
    addDepartment(con)

6.19

1.Web机制和术语
2.HTTP协议
3.Django概述
3.1 Django安装conda install django
3.2 静态项目

 # 设置语言代码
 LANGUAGE_CODE = 'zh-hans'
 # 设置时区
 TIME_ZONE = 'Asia/Shanghai'
 USE_TZ = False # 不使用UTC时间(不跨时区)

3.3 创建动态应用python3 manage.py startapp +应用名

3.4 使用视图模板


6.20

1.配置MySQL(ENGINE属性可用:'django.db.backends.sqlite3''django.db.backends.postgresql''django.db.backends.mysql''django.db.backends.oracle'
2.安装pymysql(数据库内容时我们已经安装过了)
3.修改__init__.py文件,将PyMySQL视为MySQLdb来使用,从而避免Django找不到连接MySQL的客户端工具

import pymysql
pymysql.install_as_MySQLdb()

4.数据库迁移,为应用程序创建对应的数据表

# 准备工作:创建表
# drop database if exists +数据库名;
# create database +数据库名 default charset utf8;

合并数据表:python3 manage.py migrate
我运行到这里会报错,说django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.13 or newer is required; you have 0.9.3.
解决方法是:找到django的mysql下的base.py文件,我的路径是/Users/Crazy_Steven/miniconda3/lib/python3.7/site-packages/django/db/backends/mysql/base.py⁩将以下代码注释掉即可

if version < (1, 3, 13):
    raise ImproperlyConfigured('mysqlclient 1.3.13 or newer is required; you have %s.' % Database.__version__)

然后又报错了AttributeError: 'str' object has no attribute 'decode',解决办法很简单,错误上两行是

File "/Users/Crazy_Steven/miniconda3/lib/python3.7/site-packages/django/db/backends/mysql/operations.py", line 146, in last_executed_query
    query = query.decode(errors='replace')

只要按错误内容把该路径下的operations.py文件146行decode改成encode即可,然后继续python3 manage.py migrate
5.修改我们需要的数据模型vim 应用名/models.py
6.通过模型创建数据表python3 manage.py makemigrations 应用名并再次合并python3 manage.py migrate

E-R图

7.后台管理系统

8.Django模型最佳实践

9.模型定义参考(此内容当作字典,需要用的时候再来查,记不住)

字段类 说明
AutoField 自增ID字段
BigIntegerField 64位有符号整数
BinaryField 存储二进制数据的字段,对应Python的bytes类型
BooleanField 存储True或False
CharField 长度较小的字符串
DateField 存储日期,有auto_now和auto_now_add属性
DateTimeField 存储日期和日期,两个附加属性同上
DecimalField 存储固定精度小数,有max_digits(有效位数)和decimal_places(小数点后面)两个必要的参数
DurationField 存储时间跨度
EmailField 与CharField相同,可以用EmailValidator验证
FileField 文件上传字段
FloatField 存储浮点数
ImageField 其他同FileFiled,要验证上传的是不是有效图像
IntegerField 存储32位有符号整数。
GenericIPAddressField 存储IPv4或IPv6地址
NullBooleanField 存储True、False或null值
PositiveIntegerField 存储无符号整数(只能存储正数)
SlugField 存储slug(简短标注)
SmallIntegerField 存储16位有符号整数
TextField 存储数据量较大的文本
TimeField 存储时间
URLField 存储URL的CharField
UUIDField 存储全局唯一标识符
选项 说明
null 数据库中对应的字段是否允许为NULL,默认为False
blank 后台模型管理验证数据时,是否允许为NULL,默认为False
choices 设定字段的选项,各元组中的第一个值是设置在模型上的值,第二值是人类可读的值
db_column 字段对应到数据库表中的列名,未指定时直接使用字段的名称
db_index 设置为True时将在该字段创建索引
db_tablespace 为有索引的字段设置使用的表空间,默认为DEFAULT_INDEX_TABLESPACE
default 字段的默认值
editable 字段在后台模型管理或ModelForm中是否显示,默认为True
error_messages 设定字段抛出异常时的默认消息的字典,其中的键包括null、blank、invalid、invalid_choice、unique和unique_for_date
help_text 表单小组件旁边显示的额外的帮助文本。
primary_key 将字段指定为模型的主键,未指定时会自动添加AutoField用于主键,只读。
unique 设置为True时,表中字段的值必须是唯一的
verbose_name 字段在后台模型管理显示的名称,未指定时使用字段的名称
  1. limit_choices_to:值是一个Q对象或返回一个Q对象,用于限制后台显示哪些对象。
  2. related_name:用于获取关联对象的关联管理器对象(反向查询),如果不允许反向,该属性应该被设置为'+',或者以'+'结尾。
  3. to_field:指定关联的字段,默认关联对象的主键字段。
  4. db_constraint:是否为外键创建约束,默认值为True。
  5. on_delete:外键关联的对象被删除时对应的动作,可取的值包括django.db.models中定义的:
  1. symmetrical:是否建立对称的多对多关系。
  2. through:指定维持多对多关系的中间表的Django模型。
  3. throughfields:定义了中间模型时可以指定建立多对多关系的字段。
  4. db_table:指定维持多对多关系的中间表的表名。
选项 说明
abstract 设置为True时模型是抽象父类
app_label 如果定义模型的应用不在INSTALLED_APPS中可以用该属性指定
db_table 模型使用的数据表名称
db_tablespace 模型使用的数据表空间
default_related_name 关联对象回指这个模型时默认使用的名称,默认为<model_name>_set
get_latest_by 模型中可排序字段的名称。
managed 设置为True时,Django在迁移中创建数据表并在执行flush管理命令时把表移除
order_with_respect_to 标记对象为可排序的
ordering 对象的默认排序
permissions 创建对象时写入权限表的额外权限
default_permissions 默认为('add', 'change', 'delete')
unique_together 设定组合在一起时必须独一无二的字段名
index_together 设定一起建立索引的多个字段名
verbose_name 为对象设定人类可读的名称
verbose_name_plural 设定对象的复数名称

按字段查找可以用的条件:

  1. exact / iexact:精确匹配/忽略大小写的精确匹配查询
  2. contains / icontains / startswith / istartswith / endswith / iendswith:基于like的模糊查询
  3. in:集合运算
  4. gt / gte / lt / lte:大于/大于等于/小于/小于等于关系运算
  5. range:指定范围查询(SQL中的between…and…
  6. year / month / day / week_day / hour / minute / second:查询时间日期
  7. isnull:查询空值(True)或非空值(False)
  8. search:基于全文索引的全文检索
  9. regex / iregex:基于正则表达式的模糊匹配查询
from django.db.models import Q
Emp.objects.filter(Q(name__startswith='张'),Q(sal__gte=5000) | Q(comm__gte=1000))

6.21

投票应用,具体的需求是用户进入应用首先查看到“学科介绍”页面,该页面显示了一个学校所开设的所有学科;通过点击某个学科,可以进入“老师介绍”页面,该页面展示了该学科所有老师的详细情况,可以在该页面上给老师点击“好评”或“差评”,但是会先跳转到“登录页”要求用户登录,登录成功才能投票;对于未注册的用户,可以在“登录页”点击“新用户注册”进入“注册页”完成用户注册,注册成功后会跳转到“登录页”,注册失败会获得相应的提示信息。

1.cd到项目目录创建项目:django-admin startproject djangoVote
2.修改配置文件(djangoVote/settings.py)中的语言为中文:LANGUAGE_CODE = 'zh-hans'
3.cd到项目中创建投票应用:python3 manage.py startapp voteApp
4.配置数据库
vim djangoVote/settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'voteApp',
]

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'djangoVote',
        'HOST': 'localhost',
        'PORT': 3306,
        'USER': 'root',
        'PASSWORD': 'stevenabc999',
    }
}

vim djangoVote/__init__.py

import pymysql
pymysql.install_as_MySQLdb()
drop database if exists djangoVote;
create database djangoVote default charset utf8;

5.合并数据库
python3 manage.py migrate
6.修改数据模型vim voteApp/models.py

from django.db import models


class Subject(models.Model):
    """学科"""
    no = models.AutoField(primary_key=True, verbose_name='编号')
    name = models.CharField(max_length=31, verbose_name='名称')
    intro = models.CharField(max_length=511, verbose_name='介绍')

    def __str__(self):
        return self.name

    class Meta:
        db_table = 'tb_subject'
        verbose_name_plural = '学科'


class Teacher(models.Model):
    """老师"""
    no = models.AutoField(primary_key=True, verbose_name='编号')
    name = models.CharField(max_length=15, verbose_name='姓名')
    gender = models.BooleanField(default=True, choices=((True, '男'), (False, '女')), verbose_name='性别')
    birth = models.DateField(null=True, verbose_name='出生日期')
    intro = models.CharField(max_length=511, default='', verbose_name='')
    good_count = models.IntegerField(default=0, verbose_name='好评数')
    bad_count = models.IntegerField(default=0, verbose_name='差评数')
    photo = models.CharField(max_length=255, verbose_name='照片')
    subject = models.ForeignKey(to=Subject, on_delete=models.PROTECT, db_column='sno', verbose_name='所属学科')

    def __str__(self):
        return self.name

    class Meta:
        db_table = 'tb_teacher'
        verbose_name_plural = '老师'

7.“生成迁移”和“执行迁移”
python3 manage.py makemigrations voteApp
python3 manage.py migrate
8.使用Django提供的后台管理应用来添加学科和老师信息(测试数据)

from django.contrib import admin
from django.contrib.admin import ModelAdmin

from vote.models import Teacher, Subject


class SubjectModelAdmin(ModelAdmin):
    """学科模型管理"""
    list_display = ('no', 'name')
    ordering = ('no', )


class TeacherModelAdmin(ModelAdmin):
    """老师模型管理"""
    list_display = ('no', 'name', 'gender', 'birth', 'good_count', 'bad_count', 'subject')
    ordering = ('no', )
    search_fields = ('name', )


admin.site.register(Subject, SubjectModelAdmin)
admin.site.register(Teacher, TeacherModelAdmin)

9.创建使用模版(样式自己处理)
mkdir templates
touch templates/subject.html
vim templates/subject.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>学科信息</title>
    <style>
        body {
            width: 960px;
            margin: 0 auto;
        }
        .sub {
            margin: 20px 10px;
        }
    </style>
</head>
<body>
    <h1>所有学科</h1>
    <hr>
    <div id="container">
        {% for subject in subjects %}
        <dl>
            <dt>
                <a href="/teachers?sno={{ subject.no }}">
                    {{ subject.name }}
                </a>
            </dt>
            <dd>{{ subject.intro }}</dd>
        </dl>
        {% endfor %}
    </div>
</body>
</html>

touch templates/teacher.html
vim templates/teacher.html

<!DOCTYPE html>
{% load staticfiles %}
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>讲师信息</title>
    <style>
        body {
            width: 960px;
            margin: 0 auto;
        }
        .sub {
            margin: 20px 10px;
        }
    </style>
</head>
<body>
    <h1>{{ subject.name }}学科老师信息</h1>
    <hr>
    {% if teachers %}
    {% for teacher in teachers %}
    <div class="teacher">
        <div class="photo">
            <img src="{% static teacher.photo %}" height="140" alt="">
        </div>
        <div class="info">
            <h3>{{ teacher.name }}</h3>
            <p>{{ teacher.detail }}</p>
            <p class="comment">
                <a href="/praise/?tno={{ teacher.no }}">好评</a>
                (<span>{{ teacher.good_count }}</span>)
                &nbsp;&nbsp;
                <a href="/criticize/?tno={{ teacher.no }}">差评</a>
                (<span>{{ teacher.bad_count }}</span>)
            </p>
        </div>
    </div>
    {% endfor %}
    {% else %}
    <h3>暂时没有该学科的老师信息</h3>
    {% endif %}
    <p>
        <a href="/">返回首页</a>
    </p>
</body>
</html>

vim djangoVote/settings.py

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

10.修改views.py文件

def show_subjects(request):
    """查看所有学科"""
    subjects = Subject.objects.all()
    return render(request, 'subject.html', {'subjects': subjects})
def show_teachers(request):
    """查看指定学科的老师"""
    try:
        sno = int(request.GET['sno'])
        subject = Subject.objects.get(no=sno)
        teachers = Teacher.objects.filter(subject__no=sno)
        context = {'subject': subject, 'teachers': teachers}
        return render(request, 'teacher.html', context)
    except (KeyError, ValueError, Subject.DoesNotExist):
        return redirect('/')

11.加载静态资源
vim djangoVote/settings.py

STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static'), ]
STATIC_URL = '/static/'

12.修改urls.py文件,配置用户请求的URL和视图函数的对应关系
vim djangoVote/urls.py

from django.contrib import admin
from django.urls import path

from vote import views

urlpatterns = [
    path('', views.show_subjects),
    path('teachers/', views.show_teachers),
    path('admin/', admin.site.urls),
]

13.启动项目
python3 manage.py runserver
14.修改项目的urls.py文件,为“好评”和“差评”功能映射对应的URL
vim djangoVote/urls.py

    path('praise/', views.praise_or_criticize),
    path('criticize/', views.praise_or_criticize),

15.修改views文件,添加投票
vim voteApp/views.py

def praise_or_criticize(request):
    """好评和差评"""
    try:
        tno = int(request.GET['tno'])
        teacher = Teacher.objects.get(no=tno)
        if request.path.startswith('/praise'):
            teacher.good_count += 1
        else:
            teacher.bad_count += 1
        teacher.save()
        data = {'code': 200, 'hint': '操作成功'}
    except (KeyError, ValueError, Teacher.DoseNotExist):
        data = {'code': 404, 'hint': '操作失败'}
    return JsonResponse(data)

16.修改teacher.html模板页,引入jQuery库来实现事件处理、Ajax请求和DOM操作

<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script>
    $(() => {
        $('.comment>a').on('click', (evt) => {
            evt.preventDefault()
            let anchor = $(evt.target)
            let url = anchor.attr('href')
            $.getJSON(url, (json) => {
                if (json.code == 10001) {
                    let span = anchor.next()
                    span.text(parseInt(span.text()) + 1)
                } else {
                    alert(json.hint)
                }
            })
        })
    })
</script>

Done.


6.22

实现“用户注册”和“用户登录”的功能,并限制只有登录的用户才能为老师投票

1.添加用户模型
vim voteApp/models.py

class User(models.Model):
    """用户"""
    no = models.AutoField(primary_key=True, verbose_name='编号')
    username = models.CharField(max_length=20, unique=True, verbose_name='用户名')
    password = models.CharField(max_length=32, verbose_name='密码')
    regdate = models.DateTimeField(auto_now_add=True, verbose_name='注册时间')

    class Meta:
        db_table = 'tb_user'
        verbose_name_plural = '用户'

2.合并数据库
python3 manage.py makemigrations voteApp
python3 manage.py migrate
3.添加用户注册和登录模版页(样式自己写)
touch templates/register.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户注册</title>
    <style>
        body {
            width: 960px;
            margin: 0 auto;
        }
        .sub {
            margin: 20px 10px;
        }
    </style>
</head>
<body>
    <h1>用户注册</h1>
    <hr>
    <p class="hint">{{ hint }}</p>
    <form action="/register/" method="post">
        {% csrf_token %}
        <div class="input">
            <label for="username">用户名:</label>
            <input type="text" id="username" name="username">
        </div>
        <div class="input">
            <label for="password">密码:</label>
            <input type="password" id="password" name="password">
        </div>
        <div class="input">
            <label for="repassword">确认密码:</label>
            <input type="password" id="repassword" name="repassword">
        </div>
        <div class="input">
            <input type="submit" value="注册">
            <input type="reset" value="重置">
        </div>
    </form>
    <a href="/login">返回登录</a>
</body>
</html>

在添加登录页前先把验证码做好
touch voteApp/authCode.py
vim voteApp/authCode.py

#coding=utf-8
import random
import string
import sys
from io import BytesIO
import math
from PIL import Image,ImageDraw,ImageFont,ImageFilter
 
#字体的位置,不同版本的系统会有不同
font_path = '/Library/Fonts/Arial.ttf'
#生成验证码图片的高度和宽度
size = (200,75)
#背景颜色,默认为白色
bgcolor = (255,255,255)
#干扰线颜色。默认为红色
linecolor = (255,0,0)
#是否要加入干扰线
draw_line = True
#加入干扰线条数的上下限
line_number = (1,5)

def gene_line(draw,width,height):
    """绘制干扰线"""
    begin = (random.randint(0, width), random.randint(0, height))
    end = (random.randint(0, width), random.randint(0, height))
    draw.line([begin, end], fill = rangem_color())

def rangem_color(start=0, end=255, opacity=255):
    """获得随机颜色"""
    red = random.randint(start, end)
    green = random.randint(start, end)
    blue = random.randint(start, end)
    if opacity is None:
        return red, green, blue
    return red, green, blue, opacity
#生成验证码
def gene_code(number,text):
    width,height = size #宽和高
    image = Image.new('RGBA',(width,height),bgcolor) #创建图片
    font = ImageFont.truetype(font_path,40) #验证码的字体
    draw = ImageDraw.Draw(image)  #创建画笔
    font_width, font_height = font.getsize(text)
    draw.text(((width - font_width) / number, (height - font_height) / number),text,
            font= font,fill=rangem_color()) #填充字符串
    if draw_line:
        gene_line(draw,width,height)
    image = image.transform((width+20,height+10), Image.AFFINE, (1,-0.3,0,-0.1,1,0),Image.BILINEAR)  #创建扭曲
    image = image.filter(ImageFilter.EDGE_ENHANCE_MORE) #滤镜,边界加强
    # image.save('idencode.png') #保存验证码图片
    image_bytes = BytesIO()
    image.save(image_bytes, format='PNG')
    return image_bytes.getvalue()

touch templates/login.html
vim templates/login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户登录</title>
    <style>
        body {
            width: 960px;
            margin: 0 auto;
        }
        .sub {
            margin: 20px 10px;
        }
    </style>
</head>
<body>
    <h1>用户登录</h1>
    <hr>
    <p class="hint">{{ hint }}</p>
    <form action="/login/" method="post">
        <input type="hidden" name="backurl" value="{{ backurl }}">
        {% csrf_token %}
        <div class="input">
            <label for="username">用户名:</label>
            <input type="text" id="username" name="username">
        </div>
        <div class="input">
            <label for="password">密码:</label>
            <input type="password" id="password" name="password">
        </div>
        <div class="input captcha">
            <label for="captcha">验证码:</label>
            <input type="text" id="captcha" name="captcha">
            <img src="/captcha/">
        </div>
        <div class="input">
            <input type="submit" value="登录">
            <input type="reset" value="重置">
        </div>
    </form>
    <a href="/register">注册新用户</a>
</body>
</html>

4.修改views.py文件
vim voteApp/views.py

USERNAME_PATTERN = re.compile(r'\w{4,20}')

class RegisterForm(forms.ModelForm):
    repassword = forms.CharField(min_length=8, max_length=20)
    
    def clean_username(self):
        username = self.cleaned_data['username']
        if not USERNAME_PATTERN.fullmatch(username):
            raise ValidationError('用户名由字母、数字和下划线构成且长度为4-20个字符')
        return username
        
    def clean_password(self):
        password = self.cleaned_data['password']
        if len(password) < 8 or len(password) > 20:
            raise ValidationError('无效的密码,密码长度为8-20个字符')
        return to_md5_hex(self.cleaned_data['password'])

    def clean_repassword(self):
        repassword = to_md5_hex(self.cleaned_data['repassword'])
        if repassword != self.cleaned_data['password']:
            raise ValidationError('密码和确认密码不一致')
        return repassword

    class Meta:
        model = User
        exclude = ('no', 'regdate')

def to_md5_hex(message):
    return hashlib.md5(message.encode()).hexdigest()

def register(request):
    page, hint = 'register.html', ''
    if request.method == 'POST':
        form = RegisterForm(request.POST)
        if form.is_valid():
            form.save()
            page = 'login.html'
            hint = '注册成功,请登录'
        else:
            hint = '请输入有效的注册信息'
    return render(request, page, {'hint': hint})

# 用来随机生成一个字符串
ALL_CHARS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
def gene_text(length=4):
    """成一个字符串"""
    selected_chars = random.choices(ALL_CHARS, k=length)
    return ''.join(selected_chars)

def get_captcha(request):
    """获得验证码"""
    number = 4
    text = gene_text()
    image = gene_code(number,text)
    return HttpResponse(image, content_type='image/png')
    
class LoginForm(forms.Form):
    username = forms.CharField(min_length=4, max_length=20)
    password = forms.CharField(min_length=8, max_length=20)
    captcha = forms.CharField(min_length=4, max_length=4)

    def clean_username(self):
        username = self.cleaned_data['username']
        if not USERNAME_PATTERN.fullmatch(username):
            raise ValidationError('无效的用户名')
        return username

    def clean_password(self):
        return to_md5_hex(self.cleaned_data['password'])
        
def login(request):
    hint = ''
    if request.method == 'POST':
        form = LoginForm(request.POST)
        if form.is_valid():
            username = form.cleaned_data['username']
            password = form.cleaned_data['password']
            user = User.objects.filter(username=username, password=password).first()
            if user:
                return redirect('/')
            else:
                hint = '用户名或密码错误'
        else:
            hint = '请输入有效的登录信息'
    return render(request, 'login.html', {'hint': hint})

5.修改admin.py文件
vim voteApp/admin.py

class UserForm(forms.ModelForm):
    password = forms.CharField(min_length=8, max_length=20,
                               widget=forms.PasswordInput, label='密码')

    def clean_username(self):
        username = self.cleaned_data['username']
        if not USERNAME_PATTERN.fullmatch(username):
            raise ValidationError('用户名由字母、数字和下划线构成且长度为4-20个字符')
        return username
        
    def clean_password(self):
        password = self.cleaned_data['password']
        return to_md5_hex(self.cleaned_data['password'])

    class Meta:
        model = User
        exclude = ('no', )


class UserAdmin(admin.ModelAdmin):
    list_display = ('no', 'username', 'password')
    ordering = ('no', )
    form = UserForm
    list_per_page = 10


admin.site.register(User, UserAdmin)

6.关联urls.py文件
vim djangoVote/urls.py

path('captcha/', views.get_captcha),
path('login/', views.login, name='login'),
path('register/', views.register, name='register'),

至此,注册登录功能完成,下一步写逻辑。


6.23

1.客户端记住并在每次请求时带上sessionid做法(实现用户跟踪)

2.Django对session的支持
3.完成上个项目中登录对验证码的验证
vim voteApp/views.py

def get_captcha(request):
    """获得验证码"""
    number = 4
    text = gene_text()
    image = gene_code(number,text)
    request.session['captcha'] = text
    return HttpResponse(image, content_type='image/png')
    
def login(request: HttpRequest):
    """登录"""
    hint = ''
    if request.method == 'POST':
        form = LoginForm(request.POST)
        if form.is_valid():
            # 对验证码的正确性进行验证
            captcha_from_user = form.cleaned_data['captcha']
            captcha_from_sess = request.session.get('captcha', '')
            if captcha_from_sess.lower() != captcha_from_user.lower():
                hint = '请输入正确的验证码'
            else:
                username = form.cleaned_data['username']
                password = form.cleaned_data['password']
                user = User.objects.filter(username=username, password=password).first()
                if user:
                    # 登录成功后将用户编号和用户名保存在session中
                    request.session['userid'] = user.no
                    request.session['username'] = user.username
                    return redirect('/')
                else:
                    hint = '用户名或密码错误'
        else:
            hint = '请输入有效的登录信息'
    return render(request, 'login.html', {'hint': hint})

4.创建首页
touch templates/header.html
vim templates/header.html

<div class="user">
    {% if request.session.userid %}
    <span>{{ request.session.username }}</span>
    <a href="/logout">注销</a>
    {% else %}
    <a href="/login">登录</a>&nbsp;&nbsp;
    {% endif %}
    <a href="/register">注册</a>
</div>

5.增加注销功能
vim voteApp/views.py

def logout(request):
    """注销"""
    request.session.flush()
    return redirect('/')

6.Django框架默认的session过期时间为两周(1209600秒),可在settings.py文件中修改(# 配置会话的超时时间为1天(86400秒) SESSION_COOKIE_AGE = 86400)
7.设置关闭浏览器窗口时让会话过期,不再保留用户的任何信息(cookie中的sessionid失效),可在settings.py文件中修改(# 设置为True在关闭浏览器窗口时session就过期 SESSION_EXPIRE_AT_BROWSER_CLOSE = True)
8.将session放入缓存中(默认放在数据库中),在settings.py文件中修改

# 配置将会话对象放到缓存中存储
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
# 配置使用哪一组缓存来保存会话
SESSION_CACHE_ALIAS = 'default'

9.修改session数据默认的序列化方式,可以将默认的JSONSerializer修改为PickleSerializer
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'
10.HttpRequest封装的属性和方法:

11.HttpResponse封装的方法:

12.登录时检查浏览器是否支持cookie
vim voteApp/views.py

def login(request):
    hint = ''
    if request.method == 'POST':
        if request.session.test_cookie_worked():
            request.session.delete_test_cookie()
            form = LoginForm(request.POST)
            if form.is_valid():
                # 对验证码的正确性进行验证
                captcha_from_user = form.cleaned_data['captcha']
                captcha_from_sess = request.session.get('captcha', '')
                if captcha_from_sess.lower() != captcha_from_user.lower():
                    hint = '请输入正确的验证码'
                else:
                    username = form.cleaned_data['username']
                    password = form.cleaned_data['password']
                    user = User.objects.filter(username=username, password=password).first()
                    if user:
                        # 登录成功后将用户编号和用户名保存在session中
                        request.session['userid'] = user.no
                        request.session['username'] = user.username
                        return redirect('/')
                    else:
                        hint = '用户名或密码错误'
            else:
                hint = '请输入有效的登录信息'
        else:
            return HttpResponse("Please enable cookies and try again.")
    request.session.set_test_cookie()
    return render(request, 'login.html', {'hint': hint})

6.24

1.导出一个包含所有老师信息的Excel表格
vim voteApp/views.py

def export_teachers_excel(request):
    # 创建工作簿
    wb = xlwt.Workbook()
    # 添加工作表
    sheet = wb.add_sheet('老师信息表')
    # 查询所有老师的信息(注意:这个地方稍后需要优化)
    queryset = Teacher.objects.all()
    # 向Excel表单中写入表头
    colnames = ('姓名', '介绍', '好评数', '差评数', '学科')
    for index, name in enumerate(colnames):
        sheet.write(0, index, name)
    # 向单元格中写入老师的数据
    props = ('name', 'detail', 'good_count', 'bad_count', 'subject')
    for row, teacher in enumerate(queryset):
        for col, prop in enumerate(props):
            value = getattr(teacher, prop, '')
            if isinstance(value, Subject):
                value = value.name
            sheet.write(row + 1, col, value)
    # 保存Excel
    buffer = BytesIO()
    wb.save(buffer)
    # 将二进制数据写入响应的消息体中并设置MIME类型
    resp = HttpResponse(buffer.getvalue(), content_type='application/vnd.ms-excel')
    # 中文文件名需要处理成百分号编码
    filename = quote('老师.xls')
    # 通过响应头告知浏览器下载该文件以及对应的文件名
    resp['content-disposition'] = f'attachment; filename="{filename}"'
    return resp

2.映射url
vim djangoVote/urls.py
path('excel/', views.export_teachers_excel),
3.生成图表
vim voteApp/views.py

def get_teachers_data(request):
    # 查询所有老师的信息(注意:这个地方稍后也需要优化)
    queryset = Teacher.objects.all()
    # 用生成式将老师的名字放在一个列表中
    names = [teacher.name for teacher in queryset]
    # 用生成式将老师的好评数放在一个列表中
    good = [teacher.good_count for teacher in queryset]
    # 用生成式将老师的差评数放在一个列表中
    bad = [teacher.bad_count for teacher in queryset]
    # 返回JSON格式的数据
    return JsonResponse({'names': names, 'good': good, 'bad': bad})
    
def get_charts(request):
    return render(request, f'teacherCharts.html')

映射url:
vim djangoVote/urls.py

path('teachers_data/', views.get_teachers_data),
path('charts/',views.get_charts),

touch templates/teacherCharts.html
vim templates/teacherCharts.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>老师评价统计</title>
</head>
<body>
    <div id="main" style="width: 600px; height: 400px"></div>
    <p>
        <a href="/">返回首页</a>
    </p>
    <script src="https://cdn.bootcss.com/echarts/4.2.1-rc1/echarts.min.js"></script>
    <script>
        var myChart = echarts.init(document.querySelector('#main'))
        fetch('/teachers_data/')
            .then(resp => resp.json())
            .then(json => {
                var option = {
                    color: ['#f00', '#00f'],
                    title: {
                        text: '老师评价统计图'
                    },
                    tooltip: {},
                    legend: {
                        data:['好评', '差评']
                    },
                    xAxis: {
                        data: json.names
                    },
                    yAxis: {},
                    series: [
                        {
                            name: '好评',
                            type: 'bar',
                            data: json.good
                        },
                        {
                            name: '差评',
                            type: 'bar',
                            data: json.bad
                        }
                    ]
                }
                myChart.setOption(option)
            })
    </script>
</body>
</html>

4.配置日志
vim djangoVote/settings.py

LOGGING = {
    'version': 1,
    # 是否禁用已经存在的日志器
    'disable_existing_loggers': False,
    # 日志格式化器
    'formatters': {
        'simple': {
            'format': '%(asctime)s %(module)s.%(funcName)s: %(message)s',
            'datefmt': '%Y-%m-%d %H:%M:%S',
        },
        'verbose': {
            'format': '%(asctime)s %(levelname)s [%(process)d-%(threadName)s] '
                      '%(module)s.%(funcName)s line %(lineno)d: %(message)s',
            'datefmt': '%Y-%m-%d %H:%M:%S',
        }
    },
    # 日志过滤器
    'filters': {
        # 只有在Django配置文件中DEBUG值为True时才起作用
        'require_debug_true': {
            '()': 'django.utils.log.RequireDebugTrue',
        },
    },
    # 日志处理器
    'handlers': {
        # 输出到控制台
        'console': {
            'class': 'logging.StreamHandler',
            'level': 'DEBUG',
            'filters': ['require_debug_true'],
            'formatter': 'simple',
        },
        # 输出到文件(每周切割一次)
        'file1': {
            'class': 'logging.handlers.TimedRotatingFileHandler',
            'filename': 'access.log',
            'when': 'W0',
            'backupCount': 12,
            'formatter': 'simple',
            'level': 'INFO',
        },
        # 输出到文件(每天切割一次)
        'file2': {
            'class': 'logging.handlers.TimedRotatingFileHandler',
            'filename': 'error.log',
            'when': 'D',
            'backupCount': 31,
            'formatter': 'verbose',
            'level': 'WARNING',
        },
    },
    # 日志器记录器
    'loggers': {
        'django': {
            # 需要使用的日志处理器
            'handlers': ['console', 'file1', 'file2'],
            # 是否向上传播日志信息
            'propagate': True,
            # 日志级别(不一定是最终的日志级别)
            'level': 'DEBUG',
        },
    }
}

4.1 formatters是日志格式化器,其中格式占位符表示:

占位符 说明
%(name)s 记录器的名称
%(levelno)s 数字形式的日志记录级别
%(levelname)s 日志记录级别的文本名称
%(filename)s 执行日志记录调用的源文件的文件名称
%(pathname)s 执行日志记录调用的源文件的路径名称
%(funcName)s 执行日志记录调用的函数名称
%(module)s 执行日志记录调用的模块名称
%(lineno)s 执行日志记录调用的行号
%(created)s 执行日志记录的时间
%(asctime)s 日期和时间
%(msecs)s 毫秒部分
%(thread)d 线程ID(整数)
%(threadName)s 线程名称
%(process)d 进程ID (整数)

4.2 handlers用来指定日志处理器,可用的处理器包括:

处理器 说明
logging.StreamHandler(stream=None) 可以向类似与sys.stdout或者sys.stderr的任何文件对象输出信息
logging.FileHandler(filename, mode='a', encoding=None, delay=False) 将日志消息写入文件
logging.handlers.DatagramHandler(host, port) 使用UDP协议,将日志信息发送到指定主机和端口的网络主机上
logging.handlers.HTTPHandler(host, url) 使用HTTP的GET或POST方法将日志消息上传到一台HTTP 服务器
logging.handlers.RotatingFileHandler(filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=False) 将日志消息写入文件,如果文件的大小超出maxBytes指定的值,那么将重新生成一个文件来记录日志
logging.handlers.SocketHandler(host, port) 使用TCP协议,将日志信息发送到指定主机和端口的网络主机上

4.3 Python中定义了六个级别的日志,按照从低到高的顺序依次是:NOTSET、DEBUG、INFO、WARNING、ERROR、CRITICAL。

4.4 Django框架提供了如下所示的内置记录器:

5.配置Django-Debug-Toolbar
5.1 安装Django-Debug-Toolbar
pip3 install django-debug-toolbar
5.2 修改settings.py
vim djangoVote/settings.py
5.3 修改urls.py
vim djangoVote/urls.py

if settings.DEBUG:

    import debug_toolbar

    urlpatterns.insert(0, path('__debug__/', include(debug_toolbar.urls)))

6.ORM代码优化
6.1 多对一关联(如投票应用中的老师和学科)queryset = Teacher.objects.all().select_related('subject')
6.2 多对多关联(如电商网站中的订单和商品)使用prefetch_related()方法来加载关联对象
6.3 用QuerySet的only()方法来指定需要查询的属性,也可以用QuerySet的defer()方法来指定暂时不需要查询的属性queryset = Teacher.objects.all().only('name', 'good_count', 'bad_count')
6.4 统计出每个学科的老师好评和差评的平均数queryset = Teacher.objects.values('subject').annotate( good=Avg('good_count'), bad=Avg('bad_count'))


6.25

1.实现登录验证(修改views.py和teacher.html文件)
vim voteApp/views.py

def praise_or_criticize(request):
    """投票"""
    if 'username' in request.session:
        try:
            tno = int(request.GET.get('tno', '0'))
            teacher = Teacher.objects.get(no=tno)
            if request.path.startswith('/praise'):
                teacher.good_count += 1
            else:
                teacher.bad_count += 1
            teacher.save()
            data = {'code': 200, 'message': '操作成功'}
        except (ValueError, Teacher.DoesNotExist):
            data = {'code': 404, 'message': '操作失败'}
    else:
        data = {'code': 401, 'message': '请先登录'}
    return JsonResponse(data)

vim templates/teacher.html

<script>
    $(() => {
        $('.comment > a').on('click', (evt) => {
            evt.preventDefault()
            let a = $(evt.target)
            $.getJSON(a.attr('href'), (json) => {
                if (json.code == 200) {
                    let span = a.next()
                    span.text(parseInt(span.text()) + 1)
                } else if (json.code == 401) {
                    window.location.href = '/login/?backurl=' + location.href
                } else {
                    alert(json.message)
                }
            })
        })
    })
</script>

2.Django中间件

2). django.middleware.security.SecurityMiddleware:安全相关中间件,可以处理和安全相关的配置项。
+ SECURE_HSTS_SECONDS - 强制使用HTTPS的时间
+ SECURE_HSTS_INCLUDE_SUBDOMAINS - HTTPS是否覆盖子域名
+ SECURE_CONTENT_TYPE_NOSNIFF - 是否允许浏览器推断内容类型
+ SECURE_BROWSER_XSS_FILTER - 是否启用跨站脚本攻击过滤器
+ SECURE_SSL_REDIRECT - 是否重定向到HTTPS连接
+ SECURE_REDIRECT_EXEMPT - 免除重定向到HTTPS

3). django.contrib.sessions.middleware.SessionMiddleware:会话中间件
4). django.middleware.csrf.CsrfViewMiddleware:通过生成令牌,防范跨请求份伪的造中间件
5). django.middleware.clickjacking.XFrameOptionsMiddleware:通过设置请求头参数,防范点击劫持攻击的中间件
6). 在请求的过程中,上面的中间件会按照1-5的顺序执行,然后是URL解析,最后请求才会来到视图函数;在响应的过程中,上面的中间件会按照5-1的顺序执行,与请求时中间件执行的顺序正好相反

3.自定义中间件(实现用户登录验证的功能)
3.1创建中间件文件
touch voteApp/middlewares.py
3.2修改中间件文件
vim voteApp/middlewares.py

from django.http import JsonResponse
from django.shortcuts import redirect

# 需要登录才能访问的资源路径
LOGIN_REQUIRED_URLS = {
    '/excel/', '/teachers_data/','/charts/'
}


def check_login_middleware(get_resp):

    def wrapper(request, *args, **kwargs):
        # 请求的资源路径在上面的集合中
        if request.path in LOGIN_REQUIRED_URLS:
            # 会话中包含userid则视为已经登录
            if 'userid' not in request.session:
                # 判断是不是Ajax请求
                if request.is_ajax():
                    # Ajax请求返回JSON数据提示用户登录
                    return JsonResponse({'code': 10003, 'hint': '请先登录'})
                else:
                    backurl = request.get_full_path()
                    # 非Ajax请求直接重定向到登录页
                    return redirect(f'/login/?backurl={backurl}')
        return get_resp(request, *args, **kwargs)

    return wrapper

3.3修改配置文件,激活中间件使其生效
vim djangoVote/setting
'voteApp.middlewares.check_login_middleware',
中间件执行的顺序是非常重要的,对于有依赖关系的中间件必须保证被依赖的中间件要置于依赖它的中间件的前面,就好比我们刚才自定义的中间件要放到SessionMiddleware的后面,因为我们要依赖这个中间件为请求绑定的session对象才能判定用户是否登录


6.26

1.前后端分离(提升开发效率/增强代码的可维护性/支持多终端和服务化架构)
2.返回Json数据
eg:

def show_subjects(request):
    # 通过循环遍历查询学科得到的QuerySet对象
    queryset = Subject.objects.all()
    # 创建学科列表容器
    subjects = []
    # 将每个学科的数据处理成一个字典,在将字典保存在容器中
    for subject in queryset:
        subjects.append({
            'no': subject.no,
            'name': subject.name,
            'intro': subject.intro,
            'isHot': subject.is_hot
        })
    # 用JsonResponse完成对列表的序列化
    return JsonResponse(subjects, safe=False)
    # 由于JsonResponse序列化的是一个列表而不是字典,所以需要指定safe参数的值为False才能完成对subjects的序列化,否则会产生TypeError异常。

3.使用bpmappers对数据序列化

from bpmappers.djangomodel import ModelMapper
from voteApp.models import Subject

class SubjectMapper(ModelMapper):
   
    class Meta:
        model = Subject
        
def show_subjects(request):
    queryset = Subject.objects.all()
    subjects = []
    for subject in queryset:
        subjects.append(SubjectMapper(subject).as_dict())
    return JsonResponse(subjects, safe=False)
from bpmappers import RawField
from bpmappers.djangomodel import ModelMapper

from voteApp.models import Subject


class SubjectMapper(ModelMapper):
    # 修改键名
    isHot = RawField('is_hot')

    class Meta:
        model = Subject
        # 删除 
        exclude = ('create_date', 'is_hot')

4.使用Vue.js渲染页面(重新改写subjects.html页面,使用Vue.js来渲染页面)
vim templates/subjects.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>学科</title>
</head>
<body>
    <h1>所有学科</h1>
    <hr>
    <div id="app">
        <div v-for="subject in subjects">
            <h3>
                <a :href="getTeachersHref(subject.no)">{{ subject.name }}</a>
                <img v-if="subject.isHot" src="/static/images/hot.png" width="32">
            </h3>
            <p>{{ subject.intro }}</p>
        </div>
    </div>
    <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
    <script>
        const app = new Vue({
            el: '#app',
            data: {
                subjects: []
            },
            created() {
                fetch('/subjects/')
                    .then(resp => resp.json())
                    .then(json => this.subjects = json)
            },
            methods: {
                getTeachersHref(sno) {
                    return `/static/teachers.html/?sno=${sno}`
                }
            }
        })
    </script>
</body>
</html>

动静分离,静态资源通过Nginx或Apache服务器进行部署,生成动态内容的Python程序部署在uWSGI或者Gunicorn服务器上,对动态内容的请求由Nginx或Apache路由到uWSGI或Gunicorn服务器上。


6.28-7.9(6.27休息一天)

没课件了,看Django的翻译文档吧。
由于内容过长,分成上下两部分吧

上一篇 下一篇

猜你喜欢

热点阅读