Python的100天打卡(上)

首先附上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.渐近时间复杂度

  • O(c)常量时间复杂度-布隆过滤器/哈希存储
  • O(log2n)对数时间复杂度-折半查找(二分查找)
  • O(n)线性时间复杂度-顺序查找/桶排序
  • O(nlog2n)对数线性时间复杂度-高级排序算法(归并排序、快速排序)
  • O(n^2)平方时间复杂度-简单排序算法(选择、插入、冒泡)
  • O(n^3)立方时间复杂度-Floyd算法/矩阵乘法运算
  • O(2^n)几何级数时间复杂度-汉诺塔
  • O(n!)阶乘时间复杂度-旅行经销商问题NP

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.面向对象设计原则

  • 单一职责原则(类的设计要高内聚)
  • 开闭原则(软件实体应该对扩展开发对修改关闭)
  • 依赖倒转原则(已被弱化)
  • 里氏替换原则(任何时候可以用子类对象替换掉父类对象)
  • 接口隔离原则(Python中没有接口的概念)
  • 合成聚合复用原则(优先使用强关联关系而不是继承关系复用代码)
  • 最少知识原则(不要给没有必然联系的对象发消息)

9.设计模式

  • 创建型模式:单例/工厂/创建者/原型
  • 结构型模式:适配器/门面(外观)/代理
  • 行为型模式:迭代器/观察者/状态/策略

5.26

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

  • 使用了 yield 的函数被称为生成器(generator),也是一种迭代器
  • 每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行
  • 创建:generator_eg = (i*i for i in range(10))

2.迭代器__iter____next__

  • 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.多线程场景:

  • 程序需要维护许多共享的状态(尤其是可变状态),Python中的列表、字典、集合都是线程安全的,所以使用线程而不是进程维护共享状态的代价相对较小。
  • 程序会花费大量时间在I/O操作上,没有太多并行计算的需求且不需占用太多的内存。

9.多进程场景:

  • 程序执行计算密集型任务(如:字节码操作、数据处理、科学计算)
  • 程序的输入可以并行的分成块,并且可以将运算结果合并。
  • 程序在内存使用方面没有任何限制且不强依赖于I/O操作(如:读写文件、套接字等)

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常用标签

  • 标题和段落h1-h6,p
  • 上标和下标sup,sub
  • 换行br无结束,分格线hr无结束
  • 加粗strong
  • 缩写abbr,另一个不支持h5,就不写了
  • 引文斜体cite
  • 所有者信息address
  • 下划线ins,删除线del
  • 列表:有序(ol-li),无序(ul-li),定义(dl-一级dt-二级dd)
  • 链接Anchor对象,常用属性href、name、target等
  • 图片image
  • 图文figure-figcaption
  • 表格table-tr-td,caption,rowspan/colspan,thead/tbody/tfoot
  • 表单form,action/method
  • 音视频audio/video

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.基础命令(命令名称 [参数] [对象])

  • 获取登录信息w/who/last
  • 查看自己使用的Shell ps
  • 查看命令说明whatis
  • 查看命令的位置which/whereis
  • 查看帮助文档man/info/apropos
  • 切换用户su 用户名
  • 以管理员身份执行命令sudo
  • 登入登出logout/exit/adduser/userdel/passwd/ssh
  • 查看系统和主机名uname/hostname
  • 重启和关机reboot/init 6/shutdown/init 0
  • 查看历史命令history

5.文件和文件夹操作

  • 创建/删除目录(文件夹)mkdir/rmdir
  • 创建/删除文件touch/rm(-i 交互式删除,每个删除都会询问;-r 删除目录及目录中的所有文件夹和文件;-f强制删除,忽略不存在的文件,无任何提示)
  • 切换和查看当前目录cd/pwd
  • 查看目录内容ls(-l 以长格式查看;-a显示隐藏文件;-R遇到目录要递归展开;-d只列目录;-S按大小排序;-t按时间排序)
  • 查看文件内容cat/head/tail/more/less
  • 拷贝/移动文件cp/mv
  • 查找文件和查找内容find/grep(可用正则)
  • 链接ln
  • 压缩/解压缩/归档/解归档gzip/gunzip/xz/tar(归档是:把多个文件组合到一个文件中。归档好处是,把文件数目变少,有利于降多个文件作为电子邮件附件发送,以及备份文件。压缩是利用算法将文件有损或无损地处理,以达到保留最多文件信息,而令文件体积变小。压缩好处就是节约硬盘空间,以及减小电子邮件附件的大小,提高传输效率。)
  • 其它工具sort(文件内容排序)/uniq(检查删除重复)/diff(对比文件不同)/tr(替换文件内容)/cut(分割截取字节)/paste(合并文件内容)/file(查看文件类型/编码格式)/wc(统计文件信息)

6.管道和重定向

  • 定义:管道是一种通信机制,通常用于进程间的通信(也可通过socket进行网络通信),它表现出来的形式将前面每一个进程的输出(stdout)直接作为下一个进程的输入(stdin),管道命令使用|作为界定符号
  • 查找当前目录下文件个数find ./ | wc -l
  • 列出当前路径下的文件和文件夹,并加编号ls | cat -n
  • 输出重定向>是定向输出到文件,如果文件不存在,就创建文件;如果文件存在,就将其清空;一般我们备份清理日志文件的时候,就是这种方法:先备份日志,再用>,将日志文件清空(文件大小变成0字节)/>>这个是将输出内容追加到目标文件中。如果文件不存在,就创建文件;如果文件存在,则将新的内容追加到那个文件的末尾,该文件中的原有内容不受影响/2>是将错误输出
  • 输入重定向<

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

9.文件系统
9.1 目录

  • /bin 基本命令的二进制文件
  • /boot 引导加载程序的静态文件
  • /dev 设备文件
  • /etc 配置文件
  • /home 普通用户目录的父目录
  • /lib 共享库文件
  • /lib64 共享64位库文件
  • /lost+found 存放未链接文件
  • /media 自动识别设备的挂载目录
  • /mnt 临时挂载文件系统的挂载点
  • /opt 可选插件软件包安装位置
  • /proc 内核和进程信息
  • /root 超级管理员用户主目录
  • /run 存放系统运行时需要的东西
  • /sbin 超级用户的二进制文件
  • /sys 设备的伪文件系统
  • /tmp 临时文件夹
  • /usr 用户应用目录
  • /var 变量数据目录

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

  • 启动vim + 文件名
  • i进入编辑模式
  • w保存q退出
  • -d比较文件

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

  • yum search + 包名 搜索软件包
  • yum list installed列出安装的软件包(yum list installed | grep zlib)
  • yum install + 包名安装软件包
  • yum remove + 包名删除软件包
  • yum update更新所有软件包
  • yum check-update检查有哪些可以更新的软件包
  • yum info + 包名显示软件包的信息

12.2 rpm(Redhat Package Manager)

  • rpm -ivh <包名>.rpm安装软件包
  • rpm -e <包名>删除软件包
  • rpm -qa | grep +包名查询是否安装该软件

13.配置服务

  • systemctl start firewalld启动服务
  • systemctl stop firewalld停止服务
  • systemctl restart firewallld重启服务
  • systemctl status firewalld查看服务
  • systemctl enable firewalld设置开机自启
  • systemctl disable firewalld关闭开机自启

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

  • ssh安全远程连接
  • wget通过网络获取资源(b后台下载模式/O下载到指定的目录/r递归下载)
  • ip显示/操作网络配置
  • ping检查网络可达性
  • netstat查看网络服务和端口netstat -nap | grep nginx
  • scp安全文件拷贝
  • sftp安全文件传输(help/ls显示远端目录列表/lls显示本地目录列表/cd切换远端路径/lcd切换本地路径/mkdir创建远端目录/lmkdir创建本地目录/pwd显示远端当前工作目录/lpwd显示本地当前工作目录/get下载文件/put上传文件/rm删除远端文件/bye/exit/quit退出sftp)

16.进程管理

  • ps查看进程ps -ef/ps -ef | grep+进程名
  • kill终止进程
  • ctrl+Z&将进程置于后台
  • jobs查询后台进程
  • bg让进程在后台继续运行
  • fg将后台进程置于前台
  • ctrl+C结束前台的进程
  • top进程监控

17.系统性能

  • sar查看系统活动信息
  • free查看内存使用情况
  • pmap查看进程使用内存情况
  • iostat报告设备CPU和I/O统计信息

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

1.NoSQL按存储类型分为:

  • 列族数据库:按列存储数据,最大的特点是方便存储结构化和半结构化数据,方便做数据压缩,对针对某一列或者某几列的查询有非常大的I/O优势,适合于批量数据处理和即时查询(Hypertable)
  • 文档数据库:一般存储JSON数据,内容是文档型的,这样就有机会对某些字段建立索引,实现关系数据库的某些功能,但不提供对参照完整性和分布事务的支持(MongoDB)
  • KV数据库:通过Key快速查询到Value,有基于内存和基于磁盘两种实现方式(Redis)
  • 图数据库:使用图结构进行语义查询的数据库,它使用节点、边和属性来表示和存储数据。图数据库从设计上就可以简单快速的检索(难以在关系系统中建模的)复杂层次结构
  • 对象数据库:通过类似面向对象语言的语法操作数据库,通过对象的方式存取数据

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

  • 官网下载 stable 版本
  • 解压:tar zxvf redis-5.0.5.tar
  • 移动到: mv redis-5.0.5 /usr/local/
  • 切换到:cd /usr/local/redis-5.0.5/
  • 编译测试 sudo make test
  • 编译安装 sudo make install
  • 启动 Redis: redis-server(我就不进行配置了)

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

  • 连接:mongo --host 127.0.0.1
  • 显示数据库:show dbs
  • 创建并切换数据库:use +库名
  • 创建collection:db.createCollection('集合名')
  • 显示所有collection:show collections
  • 删除collection:db.集合名.drop()
  • 插入数据1:db.students.insert({stuid: 1001, name: 'Steven', age: 38})
  • 插入数据2:db.students.save({stuid: 1002, name: '王大锤', tel: '13012345678', gender: '男'})
  • 查看数据:db.students.find()
  • 更新数据:db.students.update({stuid: 1001}, {'$set': {tel: '12345678901', gender: '男'}})注意"'
  • 插入更新数据:db.students.update({stuid: 1003}, {'$set': {name: '白元芳', tel: '13022223333', gender: '男'}}, upsert=true)
  • 查看格式化后的数据:db.students.find().pretty()
  • 查看stuid大于1001的数据:db.students.find({stuid: {'$gt': 1001}}).pretty()
  • 查看stuid大于1001的且只显示name和tel数据:db.students.find({stuid: {'$gt': 1001}}, {_id: 0, name: 1, tel: 1}).pretty()
  • 查询name为“steven”或者tel为“13022223333”的数据:db.students.find({'$or': [{name: 'steven'}, {tel: '13022223333'}]}, {_id: 0, name: 1, tel: 1}).pretty()注意大小写
  • 查看跳过第1条且只查1看条数据:db.students.find().skip(1).limit(1).pretty()
  • 查看排序后的数据(1表示升序,-1表示降序):db.students.find({}, {_id: 0, stuid: 1, name: 1}).sort({stuid: -1})
  • 根据指定条件创建索引:db.students.ensureIndex({name: 1})

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基本命令

  • select version();查看服务器版本
  • show databases;查看所有数据库
  • use mysql;切换到mysql数据库
  • show tables;查看数据库所有的表
  • ? contents;/? functions;/? numeric functions;/? round;/? data types;/? longblob;查看帮助

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 静态项目

  • 创建:django-admin startproject +项目名 +路径
  • 项目内的文件:
  • manage.py: 一个让你用各种方式管理 Django 项目的命令行工具。
  • 项目名/__init__.py:一个空文件,告诉 Python 这个目录应该被认为是一个 Python 包。
  • 项目名/settings.py:Django 项目的配置文件。
    oa/urls.py:Django 项目的 URL 声明,就像你网站的“目录”。
  • 项目名/wsgi.py:作为你的项目的运行在 WSGI 兼容的Web服务器上的入口。
  • cd到项目中,启动服务器运行项目:python3 manage.py runserver命令后面可以加上IP:PORT
  • 默认127.0.0.1:8000显示项目
  • 项目名/settings.py修改配置文件(时区,语言)
 # 设置语言代码
 LANGUAGE_CODE = 'zh-hans'
 # 设置时区
 TIME_ZONE = 'Asia/Shanghai'
 USE_TZ = False # 不使用UTC时间(不跨时区)

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

  • 项目文件
  • __init__.py:一个空文件,告诉 Python 这个目录应该被认为是一个 Python 包。
  • admin.py:可以用来注册模型,用于在Django的管理界面管理模型
  • apps.py:当前应用的配置。
  • migrations:存放与模型有关的数据库迁移信息。
  • models.py:存放应用的数据模型,即实体类及其之间的关系(MVC/MVT中的M)
  • tests.py:包含测试应用各项功能的测试类和测试函数。
  • views.py:处理请求并返回响应的函数(MVC中的C,MVT中的V)。
  • 修改视图文件views.py
  • 创建urls.py并映射URLtouch 应用名/urls.py
  • 合并项目目录下的urls.py文件
  • 重新运行项目,并打开127.0.0.1:8000/应用名
  • 修改views.py文件,刷新即新页面

3.4 使用视图模板

  • manage.py文件路径下创建templates文件:mkdir templates
  • 创建模板页index.htmltouch templates/index.html
  • 修改templates文件下的index.html、应用下的views.py、项目下的settings.py
  • 重新运行项目即可python3 manage.py runserver

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.后台管理系统

  • 创建超级管理员账号:python3 manage.py createsuperuser按提示输入用户名,邮箱和密码
  • 启动Web服务器python manage.py runserver
  • 登录后台服务系统http://127.0.0.1:8000/admin
  • 注册模型类:修改应用名/admin.py文件,通过注册模型管理类,可以在后台管理系统中更好的管理模型
  • 可以在管理员平台对模型进行C(Create新增)R(Read查看)U(Update更新)D(Delete删除)操作
  • 可以通过shell进行crud操作:python3 manage.py shell

8.Django模型最佳实践

  • 正确的为模型和关系字段命名。
  • 设置适当的related_name属性。
  • OneToOneField代替ForeignKeyField(unique=True)
  • 通过“迁移操作”(migrate)来添加模型。
  • 用NoSQL来应对需要降低范式级别的场景。
  • 如果布尔类型可以为空要使用NullBooleanField
  • 在模型中放置业务逻辑。
  • <ModelName>.DoesNotExists取代ObjectDoesNotExists
  • 在数据库中不要出现无效数据。
  • 不要对QuerySet调用len()函数。
  • QuerySetexists()方法的返回值用于if条件。
  • DecimalField来存储货币相关数据而不是FloatField
  • 定义__str__方法。
  • 不要将数据文件放在同一个目录中。

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

  • Django模型字段类
字段类 说明
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 字段在后台模型管理显示的名称,未指定时使用字段的名称
  • ForeignKey属性
  1. limit_choices_to:值是一个Q对象或返回一个Q对象,用于限制后台显示哪些对象。
  2. related_name:用于获取关联对象的关联管理器对象(反向查询),如果不允许反向,该属性应该被设置为'+',或者以'+'结尾。
  3. to_field:指定关联的字段,默认关联对象的主键字段。
  4. db_constraint:是否为外键创建约束,默认值为True。
  5. on_delete:外键关联的对象被删除时对应的动作,可取的值包括django.db.models中定义的:
  • CASCADE:级联删除。
  • PROTECT:抛出ProtectedError异常,阻止删除引用的对象。
  • SET_NULL:把外键设置为null,当null属性被设置为True时才能这么做。
  • SET_DEFAULT:把外键设置为默认值,提供了默认值才能这么做。
  • ManyToManyField属性
  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:基于正则表达式的模糊匹配查询
  • Q对象(用于执行复杂查询)
    eg: 查询名字以“张”开头且工资大于等于5000或补贴大于等于1000的员工
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做法(实现用户跟踪)

  • URL重写(所谓URL重写就是在URL中携带sessionid,例如:http://www.example.com/index.html?sessionid=123456)
  • 隐藏域(隐式表单域,在提交表单的时候,可以通过在表单中设置隐藏域向服务器发送额外的数据。例如:<input type="hidden" name="sessionid" value="123456">)
  • 本地存储(cookie,localStorage,sessionStorage,IndexedDB等)

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封装的属性和方法:

  • COOKIES属性 - 该属性包含了HTTP请求携带的所有cookie
  • get_signed_cookie方法 - 获取带签名的cookie,如果签名验证失败,会产生BadSignature异常

11.HttpResponse封装的方法:

  • set_cookie方法 - 该方法可以设置一组键值对并将其最终将写入浏览器
  • set_signed_cookie方法 - 跟上面的方法作用相似,但是会对cookie进行签名来达到防篡改的作用。因为如果篡改了cookie中的数据,在不知道密钥和盐的情况下是无法生成有效的签名,这样服务器在读取cookie时会发现数据与签名不一致从而产生BadSignature异常。需要说明的是,这里所说的密钥就是我们在Django项目配置文件中指定的SECRET_KEY,而盐是程序中设定的一个字符串,你愿意设定为什么都可以,只要是一个有效的字符串

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框架提供了如下所示的内置记录器:

  • django - 在Django层次结构中的所有消息记录器
  • django.request - 与请求处理相关的日志消息。5xx响应被视为错误消息;4xx响应被视为为警告消息
  • django.server - 与通过runserver调用的服务器所接收的请求相关的日志消息。5xx响应被视为错误消息;4xx响应被记录为警告消息;其他一切都被记录为INFO
  • django.template - 与模板渲染相关的日志消息
  • django.db.backends - 有与数据库交互产生的日志消息,如果希望显示ORM框架执行的SQL语句,就可以使用该日志记录器。

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中间件

  • 中间件是安插在Web应用请求和响应过程之间的组件,它在整个Web应用中扮演了拦截过滤器的角色,通过中间件可以拦截请求和响应,并对请求和响应进行过滤(简单的说就是执行额外的处理)。
  • Django中间件在settings.py文件MIDDLEWARE
    1). django.middleware.common.CommonMiddleware:基础设置中间件,可以处理以下一些配置参数。
    • DISALLOWED_USER_AGENTS - 不被允许的用户代理(浏览器)
    • APPEND_SLASH - 是否追加/
    • USE_ETAG - 浏览器缓存相关

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对数据序列化

  • 安装pip3 install bpmappers
  • 编写映射器(实现对象到字典转换)
    vim voteApp/views.py
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)
  • 修改键名,删除属性
    vim voteApp/views.py
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的翻译文档吧。
由于内容过长,分成上下两部分吧

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容