Python 学习笔记

代码编写过程中的需要注意事项

1.PEP是Python Enhancement Proposal的缩写,通常翻译为“Python增强提案”
2.类总是使用驼峰格式命名,即所有单词首字母大写其余字母小写,类名应该简明,精确,并足以从中理解类所完成的工作
3.一行列数 : PEP 8 规定为 79 列,这有些苛刻了。根据自己的情况,比如不要超过满屏时编辑器的显示列数。这样就可以在不动水平游标的情况下,方便的查看代码。
4.一个函数 : 不要超过 30 行代码, 即可显示在一个屏幕类,可以不使用垂直游标即可看到整个函数。
5.一个类 : 不要超过 200 行代码,不要有超过 10 个方法。
6.一个模块 不要超过 500 行。
7.有时间多看看 PEP 8 和google Python代码规范  [Python PEP 8](https://www.python.org/dev/peps/pep-0008/)

Python 空格的使用

    1. 变量、函数和属性应该使用小写字母来拼写,如果有多个单词就使用下划线进行连接。
    2. 类中受保护的实例属性,应该以一个下划线开头。
    3. 类中私有的实例属性,应该以两个下划线开头。
    4. 类和异常的命名,应该每个单词首字母大写。
    5. 模块级别的常量,应该采用全大写字母,如果有多个单词就用下划线进行连接。
    6. 类的实例方法,应该把第一个参数命名为`self`以表示对象自身。
    7. 类的类方法,应该把第一个参数命名为`cls`以表示该类自身。

Python 表达式和语句

    1. 采用内联形式的否定词,而不要把否定词放在整个表达式的前面。例如`if a is not b`就比`if not a is b`更容易让人理解。
    2. 不要用检查长度的方式来判断字符串、列表等是否为`None`或者没有元素,应该用`if not x`这样的写法来检查它。
    3. 就算`if`分支、`for`循环、`except`异常捕获等中只有一行代码,也不要将代码和`if`、`for`、`except`等写在一起,分开写才会让代码更清晰。
    4. `import`语句总是放在文件开头的地方。
    5. 引入模块的时候,`from math import sqrt`比`import math`更好。
    6. 如果有多个`import`语句,应该将其分为三部分,从上到下分别是Python标准模块、第三方模块和自定义模块,每个部分内部应该按照模块名称的字母表顺序来排列。

Python 运算符

Python支持多种运算符,下表大致按照优先级从高到低的顺序列出了所有的运算符,我们会陆续使用到它们。

| 运算符                                                       | 描述                           |
| ------------------------------------------------------------ | ------------------------------ |
| `[]` `[:]`                                                   | 下标,切片                     |
| `**`                                                         | 指数                           |
| `~` `+` `-`                                                  | 按位取反, 正负号               |
| `*` `/` `%` `//`                                             | 乘,除,模(取余),整除               |
| `+` `-`                                                      | 加,减                         |
| `>>` `<<`                                                    | 右移,左移                     |
| `&`                                                          | 按位与                         |
| `^` `|`                                                      | 按位异或,按位或               |
| `<=` `<` `>` `>=`                                            | 小于等于,小于,大于,大于等于 |
| `==` `!=`                                                    | 等于,不等于                   |
| `is`  `is not`                                               | 身份运算符                     |
| `in` `not in`                                                | 成员运算符                     |
| `not` `or` `and`                                             | 逻辑运算符                     |
| `=` `+=` `-=` `*=` `/=` `%=` `//=` `**=` `&=` `|=` `^=` `>>=` `<<=` | (复合)赋值运算符             |

Python语言进阶

使用生成式(用股票价格大于100元的股票构造一个新的字典)

prices = {
    'AAPL': 191.88,
    'GOOG': 1186.96,
    'IBM': 149.24,
    'ORCL': 48.44,
    'ACN': 166.89,
    'FB': 208.09,
    'SYMC': 21.29
}

prices2 = {key: value for key, value in prices.items() if value > 100}
print(prices2)

嵌套的列表

names = ['关羽', '张飞', '赵云', '马超', '黄忠']
courses = ['语文', '数学', '英语']
# 录入五个学生三门课程的成绩
# 错误 - 参考http://pythontutor.com/visualize.html#mode=edit
# scores = [[None] * len(courses)] * len(names)
scores = [[None] * len(courses) for _ in range(len(names))]
for row, name in enumerate(names):
    for col, course in enumerate(courses):
        scores[row][col] = float(input(f'请输入{name}的{course}成绩: '))
print(scores)

heapq、itertools等的用法

import heapq    

list1 = [34, 25, 12, 99, 87, 63, 58, 78, 88, 92]
list2 = [
    {'name': 'IBM', 'shares': 100, 'price': 91.1},
    {'name': 'AAPL', 'shares': 50, 'price': 543.22},
    {'name': 'FB', 'shares': 200, 'price': 21.09},
    {'name': 'HPQ', 'shares': 35, 'price': 31.75},
    {'name': 'YHOO', 'shares': 45, 'price': 16.35},
    {'name': 'ACME', 'shares': 75, 'price': 115.65}
]
print(heapq.nlargest(3, list1))
print(heapq.nsmallest(3, list1))
print(heapq.nlargest(2, list2, key=lambda x: x['price']))
print(heapq.nlargest(2, list2, key=lambda x: x['shares']))

排列 / 组合 / 笛卡尔积

import itertools

for val in itertools.permutations('ABCD'):
    print(val)
print('-' * 50)
for val in itertools.combinations('ABCDE', 3):
    print(val)
print('-' * 50)
for val in itertools.product('ABCD', '123'):
    print(val)

collections模块下的工具类(重要)

 
from collections import Counter

words = [
    'look', 'into', 'my', 'eyes', 'look', 'into', 'my', 'eyes',
    'the', 'eyes', 'the', 'eyes', 'the', 'eyes', 'not', 'around',
    'the', 'eyes', "don't", 'look', 'around', 'the', 'eyes',
    'look', 'into', 'my', 'eyes', "you're", 'under'
]
counter = Counter(words)
print(counter.most_common(3))

穷举法、贪婪法、分治法、动态规划

# 公鸡5元一只 母鸡3元一只 小鸡1元三只
# 用100元买100只鸡 问公鸡/母鸡/小鸡各多少只
for x in range(20):
    for y in range(33):
        z = 100 - x - y
        if 5 * x + 3 * y + z // 3 == 100 and z % 3 == 0:
            print(x, y, z)
    
# A、B、C、D、E五人在某天夜里合伙捕鱼 最后疲惫不堪各自睡觉
# 第二天A第一个醒来 他将鱼分为5份 扔掉多余的1条 拿走自己的一份
# B第二个醒来 也将鱼分为5份 扔掉多余的1条 拿走自己的一份
# 然后C、D、E依次醒来也按同样的方式分鱼 问他们至少捕了多少条鱼
fish = 1
while True:
    total = fish
    enough = True
    for _ in range(5):
        if (total - 1) % 5 == 0:
            total = (total - 1) // 5 * 4
        else:
            enough = False
            break
    if enough:
        print(fish)
        break
    fish += 1

#动态规划 - 适用于有重叠子问题和最优子结构性质的问题
#使用动态规划方法所耗时间往往远少于朴素解法(用空间换取时间)
def fib(num, temp={}):
    """用递归计算Fibonacci数"""
    if num in (1, 2):
        return 1
    try:
        return temp[num]
    except KeyError:
        temp[num] = fib(num - 1) + fib(num - 2)
        return temp[num]

面向对象相关知识

 
 
#三大支柱:封装、继承、多态
#月薪结算系统
#部门经理每月15000 程序员每小时200 销售员1800底薪+销售额5%提成

from abc import ABCMeta, abstractmethod

class Employee(metaclass=ABCMeta):
      def __init__(self, name):
          self._name = name
  
      @property
      def name(self):
          return self._name
  
      @abstractmethod
      def get_salary(self):
          pass
  
  
  class Manager(Employee):
      def get_salary(self):
          return 15000.0
  
  
  class Programmer(Employee):
      def __init__(self, name):
          self._working_hour = 0
          super().__init__(name)
  
      @property
      def working_hour(self):
          return self._working_hour
  
      @working_hour.setter
      def working_hour(self, hour):
          self._working_hour = hour if hour > 0 else 0
  
      def get_salary(self):
          return 200.0 * self.working_hour
  
  
  class Salesman(Employee):
      def __init__(self, name):
          self._sales = 0.0
          super().__init__(name)
  
      @property
      def sales(self):
          return self._sales
  
      @sales.setter
      def sales(self, sales):
          self._sales = sales if sales > 0 else 0
  
      def get_salary(self):
          return 1800.0 + self.sales * 0.05
  
  
  def main():
      emps = [
          Manager(u"刘备"), Manager(u"曹操"), Programmer(u"许褚"),
          Salesman(u"貂蝉"), Salesman(u"赵云"), Programmer(u"张辽"),
          Programmer(u"关羽"), Programmer(u"周瑜")
      ]
      for emp in emps:
          if isinstance(emp, Programmer):
              emp.working_hour = int(input(u"本月工作时间: "))
          elif isinstance(emp, Salesman):
              emp.sales = float(input(u"本月销售额: "))
          print("%s: %.2f" % (emp.name, emp.get_salary()))

函数的使用方式

1.将函数视为“一等公民”
2.高阶函数的用法(filter、map以及它们的替代品)
3.位置参数、可变参数、关键字参数、命名关键字参数
4.参数的元信息(代码可读性问题)
5.匿名函数和内联函数的用法(lambda函数)
6.闭包和作用域问题(LEGB)
7.装饰器函数(使用装饰器和取消装饰器)
8.输出函数执行时间的装饰器。

计算机图像相关知识

1. 颜色:如果你有使用颜料画画的经历,那么一定知道混合红、黄、蓝三种颜料可以得到其他的颜色,
    事实上这三种颜色就是被我们称为美术三原色的东西,它们是不能再分解的基本颜色。在计算机中,我们可以将红、
    绿、蓝三种色光以不同的比例叠加来组合成其他的颜色,因此这三种颜色就是色光三原色,
    所以我们通常会将一个颜色表示为一个RGB值或RGBA值(其中的A表示Alpha通道,它决定了透过这个图像的像素,也就是透明度)。
2. 像素:对于一个由数字序列表示的图像来说,最小的单位就是图像上单一颜色的小方格,这些小方块都有一个明确的位置和被分配的色彩数值,
    而这些一小方格的颜色和位置决定了该图像最终呈现出来的样子,它们是不可分割的单位,我们通常称之为像素(pixel)。
    每一个图像都包含了一定量的像素,这些像素决定图像在屏幕上所呈现的大小。

用Pillow操作图像

1.剪裁图片
2.调整图片大小
3.旋转和翻转
4.操作像素
5.添加水印

处理Excel电子表格官方文档

Python的OpenPyXL模块让我们可以在Python程序中读取和修改Excel电子表格。
关于OpenPyXL的使用手册和使用文档可以查看它的文档

处理Word文档

| 属性          | 描述       |
| ------------- | ---------- |
| bold          | 粗体       |
| italic        | 斜体       |
| underline     | 下划线     |
| strike        | 删除线     |
| double_strike | 双删除线   |
| all_caps      | 大写首字母 |
| small_caps    | 大写首字母 |
| shadow        | 带阴影     |
| outline       | 轮廓显示   |
| rtl           | 从右向左   |
| imprint       | 凹嵌页面   |
| emboss        | 凸出页面   |

处理PDF文档

PDF是Portable Document Format的缩写,使用.pdf作为文件扩展名

数据分析与数据挖掘学习总结

0.提出问题(准确描述问题和分析问题,为数据分析的主体思路)
1.数据采集
2.原始数据完整性检查
    python缺失值有3种:    (None,NA,NaN)
        1)Python内置的None值  None的数据类型 <class 'NoneType'>
        2)在pandas中,将缺失值表示为NA,表示不可用not available  NaN的数据类型 <class 'float'>
        3)对于数值数据,pandas使用浮点值NaN(Not a Number)表示缺失数据。
3.数据清洗、整理(缺、误、多,数据处理过程中40-60%时间处于数据清洗)
    1).检查数据
            读入数据中的第一步,就是检查数据,看看都有哪些变量,这些变量分布如何,是不是存在错误的观测。
    2).缺失值处理
            处理缺失值方法可分为3类:删除记录、数据插补和不处理
            缺失是随机发生的吗?如果是,可以用中位数/众数进行填充,也可以使用均值填充。
            或者说缺失其实是有潜在发生机制的吗?比如年龄大的人在问卷调查中更不愿意透露年龄,这样关于年龄的缺失就不是随机发生的,如果使用均值或者中位数进行填补可能会产生很大偏差。这时需要利用年龄和其他自变量的关系对缺失值进行估计。比如可以基于那些没有缺失值的数据来建模,然后拟合模型预测缺失值。
            如果建模的目的是预测,大部分情况下不会很严格地研究缺失机制(缺失机制很明显的时候除外),在缺失机制不太清楚的情况下,可以当成随机缺失进行填补(使用均值中位数或者用K-近邻)
    3).异常值处理
            样本量大,不在乎这几个样本,那么就可以删除这些不合理的值
            样本量小,并且获取这些数据不易而且不能删除,那就先把这些值设为缺失状态
    4).数据集成
            数据集成是将多个数据源合并存放在一个一致的数据存储中的过程
            实体识别
                实体识别的任务是统一不同源数据的矛盾
            同名异义
                数据源A中的属性ID和数据源B中的属性ID分别描述的是菜品编号和订单编号,即描述的是不同的实体
            异名同义
                数据源A中的sales_dt 和数据源B中的sales_date 都是描述销售日期的
            单位不统一
                描述同一个实体分别用的是国际单位和中国传统的计量单位。
            冗余属性识别
                数据集成往往导致数据冗余,例如:
                1.同一属性多次出现
                2.同一属性命名不一致导致重复
                对于冗余属性要先分析,检测到后再将其删除。
                有些冗余属性可以用相关性分析检测。给定两个数值型的属性A和B,根据其属性值 ,用相关系数度量一个属性在多大程度上蕴含另一个属性
    5).数据变换
            数据变换主要是对数据进行规范化处理,将数据转换成“适当的”形式,以适用于挖掘任务及算法的需要
            中心化、标准化,数据规范化对于基于距离的挖掘算法尤为重要
                中心化是通过将变量的每个观测减去该变量均值,这样中心化后的变量观测值为0。
                标准化是将变量观测除以变量标准差,标准化后的变量标准差为1
            规范化(归一化)
                为了消除指标之间的量纲和取值范围差异的影响,需要进行标准化处理,将数据按照比例进行缩放,使之落入一个特定的区域,便于进行综合分析
            1.最小-最大规范化
                最小-最大规范化也称为离差标准化,是对原始数据的线性变换,将数据值映射到[0, 1]之间
            2.零-均值规范化
                零-均值规范化也称为标准差标准化,经过处理的数据的均值为0,标准差为1
            3.小数标定规范化
                通过移动属性值的小数位数,将属性值映射到[-1, 1]之间,移动的小数位数取决于属性值绝对值的最大值
    6).数据规约
            数据规约的意义在于:
                降低无效、错误数据对建模的影响,提高建模的准确性
                少量且具有代表性的数据将大幅缩减数据挖掘所需的时间
                降低存储数据的成本
            属性规约
                属性规约的目标是寻找 出最小的属性子集并确保新数据子集的概率分布尽可能地接近原来数据集的概率分布。属性规约常用的有一下几种方法:
                合并属性:将旧属性合并为新属性
                逐步向前选择
                逐步向后删除
                决策树归纳:用决策树对原始数据进行建模,没有出现在这个决策树上的属性均可认为是无关属性。
                主成分分析
            数值规约指通过选择替代的、较小的数据来减少数据量。常用方法有:
                直方图法
                聚类
                抽样
                 聚类抽样
                 分层抽样
                参数回归
    7).变量表示
4.从不同角度对数据进行分析,构建模型
(模型需要业务知识、算法+数据结构知识、python应用等共同建立,模型的建立需要经验的积累,同样也需要技术的学习。数据的好坏决定的数据分析结果的下限,而模型的优劣则决定了上限。)
5.数据可视化(可视化的结果并不一定要有多炫酷,而是要让看得人可以花最少的时间,就可以领会到图表索要展示的信息)
6.汇总报告(词云,数据可视化图表)

Python爬虫相关思路:

爬取验证过程:

 1.use_agent  模拟浏览器,会用到fake模块,生成浏览器use_agent 列表
 2.IP proxy (常用代理免费代理,西刺,代理获取后还需要验证代理的有效性)
 3.cookies_pool,高并发,海量数据爬取,并应用redis 存储,当然也会出现cookie的失效与验证
 4.验证码的处理,常用有走相关扫码平台(云扫码后台人工处理或者图片验证码API)

验证码类型:

    1.常见数字与字母组成,直接通过机器识别,正确率95% 图片灰度变成纯白,图片每个字符的切割识别
    2.逻辑计算
    3.词语识别
    4.图片块的移动,selenium
    4.图片识别与点击 12306

爬取页面分析过程:

    1.网页静态页面  request+beautifulsoup,对于表格类的数据爬取直接采用pandas read_HTML直接获取数据的dataframe list
    2.动态页面(ajax异步加载) selenium+Xpath(对于需要验证登入的适用)

爬取结果与数据清洗存储数据库

常用的爬虫框架:scrapy

Scrapy主要包括了以下组件:

引擎(Scrapy): 用来处理整个系统的数据流处理, 触发事务(框架核心)
调度器(Scheduler): 用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址
下载器(Downloader): 用于下载网页内容, 并将网页内容返回给蜘蛛(Scrapy下载器是建立在twisted这个高效的异步模型上的)
爬虫(Spiders): 爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面
项目管道(Pipeline): 负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。
下载器中间件(Downloader Middlewares): 位于Scrapy引擎和下载器之间的框架,主要是处理Scrapy引擎与下载器之间的请求及响应。
爬虫中间件(Spider Middlewares): 介于Scrapy引擎和爬虫之间的框架,主要工作是处理蜘蛛的响应输入和请求输出。
调度中间件(Scheduler Middewares): 介于Scrapy引擎和调度之间的中间件,从Scrapy引擎发送到调度的请求和响应。


scrapy: 框架基本按照入口文件加上配置文件进行流程控制(下载器,存储器,request请求数据处理,中间件)
scrapy_redis:分布式爬虫框架(多台服务器共享以个爬取队列)

Scrapy运行流程大概如下:

1.引擎从调度器中取出一个链接(URL)用于接下来的抓取
2.引擎把URL封装成一个请求(Request)传给下载器,下载器把资源下载下来,并封装成应答包(Response)
3.爬虫解析Response
4.若是解析出实体(Item),则交给实体管道进行进一步的处理。
  若是解析出的是链接(URL),则把URL交给Scheduler等待抓取

关系型数据概述

1. 数据持久化。
2. 数据库发展史。
3. 关系型数据库特点。
   - 理论基础:集合论和关系代数。
   - 具体表象:用二维表(有行和列)组织数据。
   - 编程语言:结构化查询语言(SQL Structured Query Language)。
4. E-R图。
   - 实体 - 矩形框
   - 属性 - 椭圆框
   - 关系 - 菱形框
   - 映射 - 1:1 / 1:N / M:N
关系型数据库产品
 

数据完整性

1. 实体完整性 - 每个实体都是独一无二的
   - 主键 / 唯一约束 / 唯一索引
2. 引用完整性(参照完整性)
   - 外键
3. 域完整性 - 数据是有效的
   - 数据类型
   - 非空约束
   - 默认值约束
   - 检查约束

MySQL 性能优化总结

[MYSQL性能优化]
    数据量的大小决定了,需要采用的优化策略
    [用法优化]
        1>数据类型选择只要遵循小而简单的原则就好,内存少
        2>通常来说把可为NULL的列改为NOT NULL不会对性能提升有多少帮助,只是如果计划在列上创建索引,就应该将该列设置为NOT NULL
        3>使用索引的基本情况和索引使用类型[B+Tree(balance平衡二叉树)(主键索引/唯一索引/全文索引/普通索引)]:
            0.主键索引  alter table 表名 add primary key (列名);  alter table 表名 drop primary key 索引名;  字符字段最好不要做主键
              普通索引  create index 索引名 on 表 (列1,列名2);  alter table 表名 drop index 索引名;
              唯一索引  create unique index 索引名  on 表名 (列表..);  alter table 表名 drop unique key 索引名;
              查询索引  show keys from 表名  show index(es) from 表名
            1.MySQL不会使用索引的情况:非独立的列:    id + 1 = 5    id = 4
            2.避免多个范围条件
    [习惯约束]
        0>查询缓存(SQL_CACHE和SQL_NO_CACHE 参数设置,具体情况具体分析是否开启,对于写密集型应用,不建议开启)
        1>尽量别用select *,需要什么字段就写什么字段,减少通信间数据包的大小和数量,从而加快客户端/服务端通信
        2>子查询劲量不要用,会降低sql的性能
        3>尽量不用 or
        4>not 尽量别用
        5>尽量少 join
        6>避免类型转换
        7>原则上禁用mysql函数,特殊情况再议
[表]
    [建表]
        字段定义为NOT NULL约束,可以使用0或者空字符串来代替NULL,字段建表的时候要给 default
        字段类型尽量使用最小、最简单的数据类型
    [拆表]
       [水平拆分]
           水平拆分按照行进行拆分,常见的就是分库分表
       [垂直拆分]
           垂直拆分按照字段进行拆分,其实就是把组成一行的多个列分开放到不同的表中,与表名称相近的字段放在一张表里面
       [冗余表]
    [缓存]
        MySQL内部:在系统调优参数介绍了相关设置
        数据访问层:比如MyBatis针对SQL语句做缓存,而Hibernate可以精确到单个记录,这里缓存的对象主要是持久化对象Persistence Object
        应用服务层:这里可以通过编程手段对缓存做到更精准的控制和更多的实现策略,这里缓存的对象是数据传输对象Data Transfer Object
        Web层:针对web页面做缓存
        浏览器客户端:用户端的缓存
    [读写分离]
        从库读主库写,一般不要采用双主或多主引入很多复杂性,尽量采用文中的其他方案来提高性能
    [表分区]
        最适合的场景数据的时间序列性比较强,则可以按时间来分区
        分区的类型:  RANGE分区  LIST分区  HASH分区  KEY分区
        CREATE TABLE members (
            firstname VARCHAR(25) NOT NULL,
            lastname VARCHAR(25) NOT NULL,
            username VARCHAR(16) NOT NULL,
            email VARCHAR(35),
            joined DATE NOT NULL
        )
        PARTITION BY RANGE( YEAR(joined) ) (
            PARTITION p0 VALUES LESS THAN (1960),
            PARTITION p1 VALUES LESS THAN (1970),
            PARTITION p2 VALUES LESS THAN (1980),
            PARTITION p3 VALUES LESS THAN (1990),
            PARTITION p4 VALUES LESS THAN MAXVALUE
        );

    [索引]
    [limit优化]
        mysql到了百万级分页是个极限,就算用索引,联合索引,此时需要SQL 写成子查询会更快
    [sort优化]
        sort_buffer_size和max_length_for_sort_data 参数的相关优化,最好的情况是避免排序,合理利用索引因为索引本身也是有序的
        MySQL无法使用索引排序的情况:
        1>对不同的索引键做 ORDER BY :   SELECT * FROM t1 ORDER BY key1, key2;
        2>在非连续的索引键部分上做 ORDER BY:   SELECT * FROM t1 WHERE key2=constant ORDER BY key_part2;
        3>同时使用了 ASC 和 DESC:   SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 ASC;
        4>用于搜索记录的索引键和做 ORDER BY 的不是同一个:  SELECT * FROM t1 WHERE key2=constant ORDER BY key1;
        5>表索引中的记录是不是按序存储(Extra显示为Using index):  EXPLAIN SELECT t1 ORDER BY key1;
        6>mysql一次查询只能使用一个索引。如果要对多个字段使用索引,建立复合索引
        7>在ORDER BY操作中,MySQL只有在排序条件不是一个查询条件表达式的情况下才使用索引
    [联合索引优化]
    [前缀索引]
    [hint优化]
[引擎]
        目前广泛使用的是MyISAM和InnoDB两种引擎
    [MyISAM]
            不支持行锁,读取时对需要读到的所有表加锁,写入时则对表加排它锁
            不支持事务
            不支持外键
            不支持崩溃后的安全恢复
            在表有读取查询的同时,支持往表中插入新纪录
            支持BLOB和TEXT的前500个字符索引,支持全文索引
            支持延迟更新索引,极大提升写入性能
            对于不会进行修改的表,支持压缩表,极大减少磁盘空间占用
    [InnoDB]
            支持行锁,采用MVCC来支持高并发
            支持事务
            支持外键
            支持崩溃后的安全恢复
            不支持全文索引

[运维]
    [数据库部署]
    [配置]
    [系统]
    [硬件]
        根据MySQL是CPU密集型还是I/O密集型,通过提升CPU和内存、使用SSD,都能显著提升MySQL性能
    [bin-log]
[其他tips]
    1>INSERT IGNORE INTO  重复行数提示插入行条数为0
      INSERT INTO  ON DUPLICATE KEY UPDATE   重复行数用之后的数据更新
    2>OPTIMIZE TABLE只对MyISAM,BDB和InnoDB表起作用,尤其是MyISAM表的作用最为明显。只需要对包含可变长度的文本数据类型的表整理即可
      OPTIMIZE TABLE运行过程中,MySQL会锁定表,不可随便优化,以免数据库锁定或者重启,风险较大
      OPTIMIZE TABLE对于InnoDB,可能会显示「 Table does not support optimize, doing recreate + analyze instead」此时需要重启数据库
    3>对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引
    4>应尽量避免在 where 子句中对字段进行 null 值判断以及where 子句中使用!=或<>或or操作符,不做列运算,不用函数和触发器,可根据EXPLAIN来查看是否用了索引还是全表扫描
        select id from t where num is null --->>  select id from t where num=0
        select id from t where num=10 or num=20  --->> select id from t where num in (10,20)
        此表中并没数据  alter table 表名 alter column 字段 set default 0;
        表中已存在数据  update tablename set 字段=0;  alter table 表名 alter column 字段 set default 0;
    5>innot in 也要慎用,能用 between 就不要用 in
        select id from t where num in(1,2,3) --->> select id from t where num between 1 and 3
    6>尽量避免在 where 子句中对字段进行表达式操作
        select id from t where num/2=100 --->> select id from t where num=100*2
    7>索引可以提高select效率,但降低了insert及update的效率,insert 或 update 时有可能会重建索引,一个表的索引数最好不要超过6个,值分布很稀少的字段不适合建索引
    8>尽量使用数字型字段,字符型会降低查询,连接的性能和增加存储开销,处理查询和连接时逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次
    9>尽可能的使用 varchar/nvarchar 代替 char/nchar
    10>临时表并不是不可使用,适当地使用它们可以使某些例程更有效,当需要重复引用大型表或常用表中的某个数据集时,最好使用导出表作为数据集,将数据导出或者创建临时表。
    11>单表不要有太多字段,建议在20以内
    12>避免使用NULL字段,很难查询优化且占用额外索引空间,尽量使用TIMESTAMP而非DATETIME,尽量使用TINYINT、SMALLINT、MEDIUM_INT作为整数类型而非INT,如果非负则加上UNSIGNED

web 开发:

1.Web开发的早期阶段,开发者需要手动编写每个页面,例如一个新闻门户网站,每天都要修改它的HTML页面,
2.这样随着网站规模和体量的增大,这种方式就变得极度糟糕。为了解决这个问题,开发人员想到了用外部程序来为Web服务器生成动态内容,
3.也就是说HTML页面以及页面中的动态内容不再通过手动编写而是通过程序自动生成。最早的时候,这项技术被称为CGI(公共网关接口),
4.当然随着时间的推移,CGI暴露出的问题也越来越多,例如大量重复的样板代码,总体性能较为低下等,因此在时代呼唤新英雄的背景下,
5.PHP、ASP、JSP这类Web应用开发技术在上世纪90年代中后期如雨后春笋般涌现。通常我们说的Web应用是指通过浏览器来访问网络资源的应用程序,
6.因为浏览器的普及性以及易用性,Web应用使用起来方便简单,免除了安装和更新应用程序带来的麻烦,而且也不用关心用户到底用的是什么操作系统,
甚至不用区分是PC端还是移动端。

web 应用机制和术语

| 术语          | 解释                                                         |
| ------------- | ------------------------------------------------------------ |
| **URL/URI**   | 统一资源定位符/统一资源标识符,网络资源的唯一标识            |
| **域名**      | 与Web服务器地址对应的一个易于记忆的字符串名字                |
| **DNS**       | 域名解析服务,可以将域名转换成对应的IP地址                   |
| **IP地址**    | 网络上的主机的身份标识,通过IP地址可以区分不同的主机         |
| **HTTP**      | 超文本传输协议,构建在TCP之上的应用级协议,万维网数据通信的基础 |
| **反向代理**  | 代理客户端向服务器发出请求,然后将服务器返回的资源返回给客户端 |
| **Web服务器** | 接受HTTP请求,然后返回HTML文件、纯文本文件、图像等资源给请求者 |
| **Nginx**    | [反向代理](https://zh.wikipedia.org/wiki/%E5%8F%8D%E5%90%91%E4%BB%A3%E7%9086)
                 [负载均衡](https://zh.wikipedia.org/wiki/%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1)
                 [HTTP缓存](https://zh.wikipedia.org/wiki/HTTP%E7%BC%93%E5%AD%98) |

HTTP协议

HTTP(超文本传输协议)是构建于TCP(传输控制协议)之上应用级协议,它利用了TCP提供的可靠的传输服务实现了Web应用中的数据交换。按照维基百科上的介绍,设计HTTP最初的目的是为了提供一种发布和接收[HTML](https://zh.wikipedia.org/wiki/HTML)页面的方法,也就是说这个协议是浏览器和Web服务器之间传输的数据的载体。
关于这个协议的详细信息以及目前的发展状况,阮一峰老师的[《HTTP 协议入门》](http://www.ruanyifeng.com/blog/2016/08/http.html)、[《互联网协议入门》](http://www.ruanyifeng.com/blog/2012/05/internet_protocol_suite_part_i.html)系列以及[《图解HTTPS协议》](http://www.ruanyifeng.com/blog/2014/09/illustration-ssl.html)进行了解

HTTP请求(请求行+请求头+空行+[消息体])

HTTP响应(响应行+响应头+空行+消息体)

Django概述

Python的Web框架有上百个,比它的关键字还要多。所谓Web框架,就是用于开发Web服务器端应用的基础设施(通常指封装好的模块和一系列的工具)。
事实上,即便没有Web框架,我们仍然可以通过socket或[CGI](https://zh.wikipedia.org/wiki/%E9%80%9A%E7%94%A8%E7%BD%91%E5%85%B3%E6%8E%A5%E5%8F%A3)来开发Web服务器端应用,但是这样做的成本和代价在实际开发中通常是不能接受的。通过Web框架,我们可以化繁为简,同时降低创建、更新、扩展应用程序的工作量。Python的Web框架中比较有名的有:Flask、Django、Tornado、Pyramid、Bottle、Web2py、web.py等。

在基于Python的Web框架中,Django是所有重量级选手中最有代表性的一位,开发者可以基于Django快速的开发可靠的Web应用程序,因为它减少了Web开发中不必要的开销,对常用的设计和开发模式进行了封装,并对MVC架构提供了支持(MTV)。许多成功的网站和App都是基于Django框架构建的,国内比较有代表性的网站包括:知乎、豆瓣网、果壳网、搜狐闪电邮箱、101围棋网、海报时尚网、背书吧、堆糖、手机搜狐网、咕咚、爱福窝、果库等。

django web 应用的工作流程

0.settings配置
1.jinjia2 HTML template模板引擎,模板引擎所涉及的模板语言编码
2.URLS 使用与配置
3.index 使用与配置
4.model 设计与使用
5.form和modelform的使用
6.django 常用的内置函数

django web 前后端开发应用的快速上手

Python 环境准备工作

1. 检查Python环境:Django 1.11需要Python 2.7或Python 3.4以上的版本;Django 2.0需要Python 3.4以上的版本。
   >>> import sys
   >>> sys.version
   >>> sys.version_info

2. 创建项目文件夹并切换到该目录,例如我们要实例一个OA(办公自动化)项目。
   $ mkdir oa
   $ cd oa

3. 创建并激活虚拟环境。
   $ python3 -m venv venv
   $ source venv/bin/activate

   > 注意:Windows系统下是执行`venv/Scripts/activate.bat`批处理文件。

4. 更新包管理工具pip。
   (venv)$ python -m pip install --upgrade pip
   > 注意:请注意终端提示符发生的变化,前面的`(venv)`说明我们已经进入虚拟环境,而虚拟环境下的python和pip已经是Python 3的解释器和包管理工具了。

5. 安装Django。
   (venv)$ pip install django
   或指定版本号来安装对应的Django的版本。
   (venv)$ pip install django==1.11

6. 检查Django的版本。
   (venv)$ python -m django --version
   (venv)$ django-admin --version
   (venv)$ python
   >>> import django
   >>> django.get_version()

   下图展示了Django版本和Python版本的对应关系,在我们的项目中我们选择了最新的Django 2.0的版本。

   | Django版本 | Python版本              |
   | ---------- | ----------------------- |
   | 1.8        | 2.7、3.2、3.3、3.4、3.5 |
   | 1.9、1.10  | 2.7、3.4、3.5           |
   | 1.11       | 2.7、3.4、3.5、3.6      |
   | 2.0        | 3.4、3.5、3.6           |

7. 使用`django-admin`创建项目,项目命名为oa。
   (venv)$ django-admin startproject oa.

   > 注意:上面的命令最后的那个点,它表示在当前路径下创建项目。

   执行上面的命令后看看生成的文件和文件夹,作用如下所示:
   1. `manage.py`: 一个让你用各种方式管理 Django 项目的命令行工具。
   2. `oa/__init__.py`:一个空文件,告诉 Python 这个目录应该被认为是一个 Python 包。
   3. `oa/settings.py`:Django 项目的配置文件。
   4. `oa/urls.py`:Django 项目的 URL 声明,就像你网站的“目录”。
   5. `oa/wsgi.py`:作为你的项目的运行在 WSGI 兼容的Web服务器上的入口。

8. 启动服务器运行项目。
   (venv)$ python manage.py runserver

   在浏览器中输入<http://127.0.0.1:8000>访问我们的服务器,效果如下图所示。

django 页面创建

1. 创建名为hrs(人力资源系统)的应用(注:一个项目可以包含多个应用)。
   (venv)$ python manage.py startapp hrs

   执行上面的命令会在当前路径下创建hrs目录,其目录结构如下所示:

       `__init__.py`:一个空文件,告诉 Python 这个目录应该被认为是一个 Python 包。
       `admin.py`:可以用来注册模型,让Django自动创建管理界面。
       `apps.py`:当前应用的配置。
       `migrations`:存放与模型有关的数据库迁移信息。
    `__init__.py`:一个空文件,告诉 Python 这个目录应该被认为是一个 Python 包。
       `models.py`:存放应用的数据模型,即实体类及其之间的关系(MVC/MVT中的M)。
       `tests.py`:包含测试应用各项功能的测试类和测试函数。
       `views.py`:处理请求并返回响应的函数(MVC中的C,MVT中的V)。

2. 进入应用目录修改视图文件views.py
   (venv)$ cd hrs
   (venv)$ vim views.py
   
   from django.http import HttpResponse
   
   def index(request):
       return HttpResponse('<h1>Hello, Django!</h1>')

3. 在应用目录创建一个urls.py文件并映射URL
   (venv)$ touch urls.py
   (venv)$ vim urls.py

   from django.urls import path
   from hrs import views
   
   urlpatterns = [
       path('', views.index, name='index'),
   ]

   > 说明:上面使用的path函数是Django 2.x中新添加的函数,除此之外还有re_path是支持正则表达式的URL映射函数;
              Django 1.x中是用url函数来设定URL映射。

4. 切换到项目目录,修改该目录下的urls.py文件,对应用中设定的URL进行合并。
   (venv) $ cd ..
   (venv) $ cd oa
   (venv) $ vim urls.py

   from django.contrib import admin
   from django.urls import path, include
   
   urlpatterns = [
       path('admin/', admin.site.urls),
       path('hrs/', include('hrs.urls')),
   ]

5. 启动项目并访问应用
   (venv)$ cd ..
   (venv)$ python manage.py runserver

   在浏览器中访问 http://localhost:8000/hrs 

   > 说明:如果想实现远程访问,需要先确认防火墙是否已经打开了8000端口,
             而且需要在配置文件settings.py中修改ALLOWED_HOSTS的设置,添加一个'*'表示允许所有的客户端访问Web应用。

6. 修改views.py生成动态内容。
   (venv)$ cd hrs
   (venv)$ vim views.py
   from io import StringIO
   from django.http import HttpResponse
   
   depts_list = [
       {'no': 10, 'name': '财务部', 'location': '北京'},
       {'no': 20, 'name': '研发部', 'location': '成都'},
       {'no': 30, 'name': '销售部', 'location': '上海'},
   ]
   
   def index(request):
       output = StringIO()
       output.write('<html>\n')
       output.write('<head>\n')
       output.write('\t<meta charset="utf-8">\n')
       output.write('\t<title>首页</title>')
       output.write('</head>\n')
       output.write('<body>\n')
       output.write('\t<h1>部门信息</h1>\n')
       output.write('\t<hr>\n')
       output.write('\t<table>\n')
       output.write('\t\t<tr>\n')
       output.write('\t\t\t<th>部门编号</th>\n')
       output.write('\t\t\t<th>部门名称</th>\n')
       output.write('\t\t\t<th>所在地</th>\n')
       output.write('\t\t</tr>\n')
       for dept in depts_list:
           output.write('\t\t<tr>\n')
           output.write(f'\t\t\t<td>{dept["no"]}</td>\n')
           output.write(f'\t\t\t<td>{dept["name"]}</td>\n')
           output.write(f'\t\t\t<td>{dept["location"]}</td>\n')
           output.write('\t\t</tr>\n')
       output.write('\t</table>\n')
       output.write('</body>\n')
       output.write('</html>\n')
       return HttpResponse(output.getvalue())

7. 再次使用下面的命令来启动服务器并查看程序的运行结果
    (venv)$ cd ..
    (venv)$ python manage.py runserver

使用jinjia2视图模板

上面通过拼接HTML代码的方式生成动态视图的做法在实际开发中无法进行迭代。为了解决这个问题,我们可以提前准备一个模板页,所谓模板页就是一个带占位符的HTML页面,当我们将程序中获得的数据替换掉页面中的占位符时,一个动态页面就产生了。

我们可以用Django框架中template模块的Template类创建模板对象,通过模板对象的render方法实现对模板的渲染。所谓的渲染就是用数据替换掉模板页中的占位符,Django框架通过shortcuts模块的快捷函数render简化了渲染模板的操作,具体的用法如下所示。

1. 先回到manage.py文件所在的目录创建一个templates文件夹。
   (venv)$ cd ..
   (venv)$ mkdir templates
   (venv)$ cd templates

2. 创建模板页index.html。
   (venv)$ touch index.html
   (venv)$ vim index.html

   <!DOCTYPE html>
   <html lang="en">
   <head>
       <meta charset="UTF-8">
       <title>首页</title>
   </head>
   <body>
       <h1>部门信息</h1>
       <hr>
       <table>
           <tr>
               <th>部门编号</th>
               <th>部门名称</th>
               <th>所在地</th>
           </tr>
           {% for dept in depts_list %}
           <tr>
               <td>{{ dept.no }}</td>
               <td>{{ dept.name }}</td>
               <td>{{ dept.location }}</td>
           <tr>
           {% endfor %}
       </table>
   </body>
   </html>

   注意在模板页中我们使用了`{{ greeting }}`这样的模板占位符语法,也使用了`{% for %}`这样的模板指令,如果对此不熟悉并不要紧,我们会在后续的内容中进一步的讲解模板的用法。

3. 回到应用目录,修改views.py文件。
   (venv)$ cd ..
   (venv)$ cd hrs
   (venv)$ vim views.py

   from django.shortcuts import render
   
   depts_list = [
       {'no': 10, 'name': '财务部', 'location': '北京'},
       {'no': 20, 'name': '研发部', 'location': '成都'},
       {'no': 30, 'name': '销售部', 'location': '上海'},
   ]
   
   
   def index(request):
       return render(request, 'index.html', {'depts_list': depts_list})

   需要修改settings.py文件,配置模板文件所在的路径

4. 切换到项目目录修改settings.py文件。
   (venv)$ cd ..
   (venv)$ cd oa
   (venv)$ vim 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',
               ],
           },
       },
   ]

5. 重新运行项目并查看结果。
   (venv)$ cd ..
   (venv)$ python manage.py runserver

django 深入模型

我们提到了Django是基于MVC架构的Web框架,MVC架构追求的是“模型”和“视图的解耦合。所谓“模型”说得更直白一些就是数据,
通常也被称作“数据模型”。在实际的项目中,数据模型通常通过数据库实现持久化操作,而关系型数据库在很长一段时间都是持久化的首选方案,
下面以MySQL为例来说明如何使用关系型数据库来实现持久化操作。

django 配置关系型数据库MySQL

1. 进入oa文件夹,修改项目的settings.py文件,首先将我们之前创建的应用hrs添加已安装的项目中,然后配置MySQL作为持久化方案。
   (venv)$ cd oa
   (venv)$ vim settings.py

   INSTALLED_APPS = [
       'django.contrib.admin',
       'django.contrib.auth',
       'django.contrib.contenttypes',
       'django.contrib.sessions',
       'django.contrib.messages',
       'django.contrib.staticfiles',
       'hrs',
   ]
   
   DATABASES = {
       'default': {
           'ENGINE': 'django.db.backends.mysql',
           'NAME': 'oa',
           'HOST': 'localhost',
           'PORT': 3306,
           'USER': 'root',
           'PASSWORD': '123456',
       }
   }
   
   在配置ENGINE属性时,常用的可选值包括:

   `'django.db.backends.sqlite3'`:SQLite嵌入式数据库。
   `'django.db.backends.postgresql'`:BSD许可证下发行的开源关系型数据库产品。
   `'django.db.backends.mysql'`:转手多次目前属于甲骨文公司的经济高效的数据库产品。
   `'django.db.backends.oracle'`:甲骨文公司的关系型数据库旗舰产品。

   其他的配置可以参考官方文档中[数据库配置](https://docs.djangoproject.com/zh-hans/2.0/ref/databases/#third-party-notes)的部分。

   NAME属性代表数据库的名称,如果使用SQLite它对应着一个文件,在这种情况下NAME的属性值应该是一个绝对路径;使用其他关系型数据库MySQL,
   则要配置对应的HOST(主机)、PORT(端口)、USER(用户名)、PASSWORD(口令)等属性。

2. 安装MySQL客户端工具,Python 3中使用PyMySQL,Python 2中用MySQLdb
   (venv)$ pip install pymysql

   如果使用Python 3需要修改**项目**的`__init__.py`文件并加入如下所示的代码,这段代码的作用是将PyMySQL视为MySQLdb来使用,从而避免Django找不到连接MySQL的客户端工具而询问你:“Did you install mysqlclient? ”(你安装了mysqlclient吗?)。

   import pymysql
   pymysql.install_as_MySQLdb()

3. 运行manage.py并指定migrate参数实现数据库迁移,为应用程序创建对应的数据表,当然在此之前需要**先启动MySQL数据库服务器并创建名为oa的数据库**,在MySQL中创建数据库的语句如下所示。
   drop database if exists oa;
   create database oa default charset utf8;

   (venv)$ cd ..
   (venv)$ python manage.py migrate
   Operations to perform:
     Apply all migrations: admin, auth, contenttypes, sessions
   Running migrations:
     Applying contenttypes.0001_initial... OK
     Applying auth.0001_initial... OK
     Applying admin.0001_initial... OK
     Applying admin.0002_logentry_remove_auto_add... OK
     Applying contenttypes.0002_remove_content_type_name... OK
     Applying auth.0002_alter_permission_name_max_length... OK
     Applying auth.0003_alter_user_email_max_length... OK
     Applying auth.0004_alter_user_username_opts... OK
     Applying auth.0005_alter_user_last_login_null... OK
     Applying auth.0006_require_contenttypes_0002... OK
     Applying auth.0007_alter_validators_add_error_messages... OK
     Applying auth.0008_alter_user_username_max_length... OK
     Applying auth.0009_alter_user_last_name_max_length... OK
     Applying sessions.0001_initial... OK

4. 可以看到,Django帮助我们创建了10张表,这些都是使用Django框架需要的东西,稍后我们就会用到这些表。除此之外,我们还应该为我们自己的应用创建数据模型。如果要在hrs应用中实现对部门和员工的管理,我们可以创建如下所示的数据模型。
   (venv)$ cd hrs
   (venv)$ vim models.py

   from django.db import models
   
   class Dept(models.Model):
       """部门类"""
       
       no = models.IntegerField(primary_key=True, db_column='dno', verbose_name='部门编号')
       name = models.CharField(max_length=20, db_column='dname', verbose_name='部门名称')
       location = models.CharField(max_length=10, db_column='dloc', verbose_name='部门所在地')
   
       class Meta:
           db_table = 'tb_dept'
   
   
   class Emp(models.Model):
       """员工类"""
       
       no = models.IntegerField(primary_key=True, db_column='eno', verbose_name='员工编号')
       name = models.CharField(max_length=20, db_column='ename', verbose_name='员工姓名')
       job = models.CharField(max_length=10, verbose_name='职位')
       # 自参照完整性多对一外键关联
       mgr = models.ForeignKey('self', on_delete=models.SET_NULL, null=True, blank=True, verbose_name='主管编号')
       sal = models.DecimalField(max_digits=7, decimal_places=2, verbose_name='月薪')
       comm = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True, verbose_name='补贴')
       # 多对一外键关联
       dept = models.ForeignKey(Dept, db_column='dno', on_delete=models.PROTECT, verbose_name='所在部门')
   
       class Meta:
           db_table = 'tb_emp'
   ```
   说明:上面定义模型时使用了字段类及其属性,其中
             IntegerField对应数据库中的integer类型,
               CharField对应数据库的varchar类型,
               DecimalField对应数据库的decimal类型,
               ForeignKey用来建立多对一外键关联。
               字段属性primary_key用于设置主键,max_length用来设置字段的最大长度,
               db_column用来设置数据库中与字段对应的列,verbose_name则设置了Django后台管理系统中该字段显示的名称。

5. 通过模型创建数据表。
   (venv)$ cd ..
   (venv)$ python manage.py makemigrations hrs
   Migrations for 'hrs':
     hrs/migrations/0001_initial.py
       - Create model Dept
       - Create model Emp
   (venv)$ python manage.py migrate
   Operations to perform:
     Apply all migrations: admin, auth, contenttypes, hrs, sessions
   Running migrations:
     Applying hrs.0001_initial... OK

    执行完数据模型迁移操作之后,可以在通过图形化的MySQL客户端工具查看到E-R图(实体关系图)。

django 在后台管理模型

 
django 在后台管理模型
1. 创建超级管理员账号
   (venv)$ python manage.py createsuperuser
   Username (leave blank to use 'hao'): jackfrued
   Email address: jackfrued@126.com
   Password: 
   Password (again): 
   Superuser created successfully.

2. 启动Web服务器,登录后台管理系统
   (venv)$ python manage.py runserver

   访问 http://127.0.0.1:8000/admin,会来到如下图所示的登录界面。


   登录后进入管理员操作平台


   至此我们还没有看到之前创建的模型类,需要在应用的admin.py文件中模型进行注册。

3. 注册模型类
   (venv)$ cd hrs
   (venv)$ vim admin.py

   from django.contrib import admin
   from hrs.models import Emp, Dept
   
   admin.site.register(Dept)
   admin.site.register(Emp)

   注册模型类后,就可以在后台管理系统中看到


4. 对模型进行CRUD操作

   可以在管理员平台对模型进行C(新增)D(删除)U(更新) R(查看)操作,如下图所示。

   添加新的部门


   查看所有部门


   更新和删除部门。


5. 注册模型管理类。

   再次修改admin.py文件,通过注册模型管理类,可以在后台管理系统中更好的管理模型。
   from django.contrib import admin
   
   from hrs.models import Emp, Dept
   
   class DeptAdmin(admin.ModelAdmin):
   
       list_display = ('no', 'name', 'location')
       ordering = ('no', )
   
   
   class EmpAdmin(admin.ModelAdmin):
   
       list_display = ('no', 'name', 'job', 'mgr', 'sal', 'comm', 'dept')
       search_fields = ('name', 'job')
   
   
   admin.site.register(Dept, DeptAdmin)
   admin.site.register(Emp, EmpAdmin)




   为了更好的查看模型数据,可以为Dept和Emp两个模型类添加`__str__`魔法方法。

   from django.db import models
   
   
   class Dept(models.Model):
       """部门类"""
      
       
       def __str__(self):
           return self.name
   
   
   class Emp(models.Model):
       """员工类"""
       
       mgr = models.ForeignKey('self', on_delete=models.SET_NULL, null=True, blank=True, verbose_name='直接主管')
   
       def __str__(self):
           return self.name


   修改代码后刷新查看Emp模型的页面,效果如下图所示

django 使用ORM完成模型的CRUD操作

 
 
在了解了Django提供的模型管理平台之后,我们来看看如何从代码层面完成对模型的CRUD(Create / Read / Update / Delete)操作。
我们可以通过manage.py开启Shell交互式环境,然后使用Django内置的ORM框架对模型进行CRUD操作。

(venv)$ cd ..
(venv)$ python manage.py shell
Python 3.6.4 (v3.6.4:d48ecebad5, Dec 18 2017, 21:07:28) 
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> 

#新增
>>>
>>> from hrs.models import Dept, Emp
>>> dept = Dept(40, '研发2部', '深圳')
>>> dept.save()
>
#更新

>>>
>>> dept.name = '研发3部'
>>> dept.save()
```

#查询

查询所有对象。

>>>
>>> Dept.objects.all()
<QuerySet [<Dept: 研发1部>, <Dept: 销售1部>, <Dept: 运维1部>, <Dept: 研发3部>]>


过滤数据

>>> 
>>> Dept.objects.filter(name='研发3部') # 查询部门名称为“研发3部”的部门
<QuerySet [<Dept: 研发3部>]>
>>>
>>> Dept.objects.filter(name__contains='研发') # 查询部门名称包含“研发”的部门(模糊查询)
<QuerySet [<Dept: 研发1部>, <Dept: 研发3部>]>
>>>
>>> Dept.objects.filter(no__gt=10).filter(no__lt=40) # 查询部门编号大于10小于40的部门
<QuerySet [<Dept: 销售1部>, <Dept: 运维1部>]>
>>>
>>> Dept.objects.filter(no__range=(10, 30)) # 查询部门编号在10到30之间的部门
<QuerySet [<Dept: 研发1部>, <Dept: 销售1部>, <Dept: 运维1部>]>

查询单个对象

>>> 
>>> Dept.objects.get(pk=10)
<Dept: 研发1部>
>>>
>>> Dept.objects.get(no=20)
<Dept: 销售1部>
>>>
>>> Dept.objects.get(no__exact=30)
<Dept: 运维1部>

排序数据

>>>
>>> Dept.objects.order_by('no') # 查询所有部门按部门编号升序排列
<QuerySet [<Dept: 研发1部>, <Dept: 销售1部>, <Dept: 运维1部>, <Dept: 研发3部>]>
>>>
>>> Dept.objects.order_by('-no') # 查询所有部门按部门编号降序排列
<QuerySet [<Dept: 研发3部>, <Dept: 运维1部>, <Dept: 销售1部>, <Dept: 研发1部>]>

切片数据

>>>
>>> Dept.objects.order_by('no')[0:2] # 按部门编号排序查询1~2部门
<QuerySet [<Dept: 研发1部>, <Dept: 销售1部>]>
>>>
>>> Dept.objects.order_by('no')[2:4] # 按部门编号排序查询3~4部门
<QuerySet [<Dept: 运维1部>, <Dept: 研发3部>]>

高级查询。

>>>
>>> Emp.objects.filter(dept__no=10) # 根据部门编号查询该部门的员工
<QuerySet [<Emp: 乔峰>, <Emp: 张无忌>, <Emp: 张三丰>]>
>>>
>>> Emp.objects.filter(dept__name__contains='销售') # 查询名字包含“销售”的部门的员工
<QuerySet [<Emp: 黄蓉>]>
>>>
>>> Dept.objects.get(pk=10).emp_set.all() # 通过部门反查部门所有的员工
<QuerySet [<Emp: 乔峰>, <Emp: 张无忌>, <Emp: 张三丰>]>

> 说明1:由于员工与部门之间存在多对一外键关联,所以也能通过部门反向查询该部门的员工(从一对多关系中“一”的一方查询“多”的一方),反向查询属性默认的名字是`类名小写_set`(如上面例子中的`emp_set`),当然也可以在创建模型时通过`ForeingKey`的`related_name`属性指定反向查询属性的名字。如果不希望执行反向查询可以将`related_name`属性设置为`'+'`或以`'+'`开头的字符串。

> 说明2:查询多个对象的时候返回的是QuerySet对象,QuerySet使用了惰性查询,即在创建QuerySet对象的过程中不涉及任何数据库活动,等真正用到对象时(求值QuerySet)才向数据库发送SQL语句并获取对应的结果,这一点在实际开发中需要引起注意!

> 说明3:可以在QuerySet上使用`update()`方法一次更新多个对象。

#删除

>>>
>>> Dept.objects.get(pk=40).delete()
(1, {'hrs.Dept': 1})

### Django模型最佳实践

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

django模型定义参考

django字段

对字段名称的限制

- 字段名不能是Python的保留字,否则会导致语法错误
- 字段名不能有多个连续下划线,否则影响ORM查询操作

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             | 存储全局唯一标识符                                           |

django字段属性

通用字段属性

| 选项           | 说明                                                         |
| -------------- | ------------------------------------------------------------ |
| 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:指定维持多对多关系的中间表的表名。

django模型元数据选项

| 选项                  | 说明                                                         |
| --------------------- | ------------------------------------------------------------ |
| 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   | 设定对象的复数名称                                           |

django查询参考

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

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对象(用于执行复杂查询)的使用:

>>>
>>> from django.db.models import Q
>>> Emp.objects.filter(
...     Q(name__startswith=''),
...     Q(sal__gte=5000) | Q(comm__gte=1000)
... ) # 查询名字以“张”开头且工资大于等于5000或补贴大于等于1000的员工
<QuerySet [<Emp: 张三丰>]>

Python 常用模块包总结

[command] 
supervisorctl restart py_efin_web
supervisorctl reload py_efin_web
supervisorctl update py_efin_web
supervisorctd (supervisorct daemon 守护进程)

[program:py_efin_web(program_name)] 
command=/usr/local/bin/gunicorn --timeout=75 --workers=3  web:app -b 0.0.0.0:5000 
        --access-logfile /opt/code/py_db_mq/data/logs/access_log
用gunicorn启动命令    等待时间为75s  工作进程数为3  web flask启动的python文件 app flask应用程序实例  
监听IP与端口  0.0.0.0:5000  accesslogfile 运行日志 errorlog 错误日志
#use gunicorn conf start
#command=/usr/local/bin/gunicorn  web:app -c /etc/gunicorn.conf  采用 gunicorn 配置文件启动
directory=/opt/code/py_db_mq  flask 文件路径
stopsignal=QUIT  停止
autostart=true   自动启动
autorestart=true  自动重新启动
priority=999   优先级
stdout_logfile=/opt/code/py_db_mq/data/logs/log_supctl.log  supervisor 运行日志
stderr_logfile=/opt/code/py_db_mq/data/logs/log_supctl_err.log supervisor errorlog 错误日志

WSGI & uwsgi & uWSGI 区别

WSGI:全称是Web Server Gateway Interface,WSGI不是服务器,python模块,框架,API或者任何软件
WSGI只是一种规范,描述web server如何与web application通信的规范协议
WSGI规范协议:常用web框架有Bottle, Flask, Django
uwsgi:与WSGI一样是通信协议,是uWSGI服务器的独占协议,用于定义传输信息的类型(type of information)每一个uwsgi packet前4byte为传输信息类型的描述,与WSGI协议是两种东西,据说该协议是fcgi协议的10倍快
uWSGI:是一个web服务器,实现了WSGI协议、uwsgi协议、http协议等
WSGI协议主要包括server和application两部分

 

posted @ 2022-10-13 15:59  燕十三丶  阅读(143)  评论(0编辑  收藏  举报
AmazingCounters.com