安装Nginx:
yum install -y nginx
systemctl start nginx
systemctl stop nginx
安装MariaDB(MySQL的一个分支):
yum install -y mariadb mariadb-server
systemctl start mariadb
mysql -u root
安装MySQL:
- 清除掉所有跟mariadb相关的东西
yum list installed | grep mariadb | awk '{print $1}' | xargs yum erase -y - 清理之前的数据和日志文件(如果存在)
rm -rf /var/lib/mysql
rm -f /var/log/mysqld.log - 下载MySQL官方提供的RPM包并解归档
wget https://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-5.7.28-1.el7.x86_6ls4.rpm-bundle.tar
tar -xvf mysql-5.7.28-1.el7.x86_64.rpm-bundle.tar - 安装依赖库
yum install -y libaio - 使用rpm包管理工具安装MySQL
rpm -ivh mysql-community-common-5.7.28-1.el7.x86_64.rpm
rpm -ivh mysql-community-libs-5.7.28-1.el7.x86_64.rpm
rpm -ivh mysql-community-client-5.7.28-1.el7.x86_64.rpm
rpm -ivh mysql-community-server-5.7.28-1.el7.x86_64.rpm - 启动服务查看随机密码
systemctl start mysqld
cat /var/log/mysqld.log | grep password - 用客户端工具连接MySQL
mysql -u root -p - 修改root用户口令
set global validate_password_policy=0;
set global validate_password_length=6;
alter user 'root'@'localhost' identified by '123456';
安装Python3
- 安装依赖项
yum install -y zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel libffi-devel - 下载Python3源代码
wget https://www.python.org/ftp/python/3.7.6/Python-3.7.6.tar.xz - 解压缩和解归档
xz -d Python-3.7.6.tar.xz
tar -xvf Python-3.7.6.tar - 执行安装前的配置
cd Python-3.7.6
./configure --prefix=/usr/local/python37 --enable-optimizations - 构建和安装
make && make install - 注册环境变量
vim ~/.bash_profile
PATH=$PATH:/usr/local/python37/bin
export PATH
安装Redis:
下载Redis官方源代码
wget http://download.redis.io/releases/redis-5.0.7.tar.gz解压缩和解归档
gunzip redis-5.0.7.tar.gz
tar -xf redis-5.0.7.tar构建和安装
cd redis-5.0.7启动Redis服务
redis-server --requirepass 口令 >> redis.log &Redis客户端工具
redis-cli
网络应用的模式:
~ C/S - 客户端/服务器模式
~ B/S - 浏览器/服务器模式
~ P2P - 对等模式URL/URI - 统一资源定位符/统一资源标识符
https://www.baidu.com:443/index.html
https://www.baidu.com:443/img/bd_logo1.pngDNS - 域名服务器(将服务器的域名转换成对应的IP地址)
反向代理 - 代理用户浏览器向服务器发起请求
~ 保护真正的服务器免于直接被攻击
~ 配置负载均衡,将流量分摊到多台服务器上Web服务器 - 处理静态资源 - Nginx / Apache
应用服务器 - uWSGI / Gunicorn
其他服务器
~ 缓存服务器 - Redis
~ 数据库服务器 - MySQL
~ 邮件服务器 - Sendmail
~ 文件服务器 - NFS / FastDFS
~ 消息队列服务器 - RabbitMQ / KafkaHTTP - 超文本传输协议
~ HTTP请求报文
请求行 - GET /path/resource HTTP/1.1
请求头 - 键值对
\r\n
消息体 - 发给服务器的数据
~ HTTP响应报文
响应行 - HTTP/1.1 200 OK
响应头 - 键值对
\r\n
消息体 - 服务器返回的数据
CPython - C语言实现的Python解释器
Jython - Java实现的Python解释器
IronPython - C#实现的Python解释器
PyPy - Python实现的Python解释器 - JIT
Anaconda - 不仅有Python解释器还有诸多三方库
配置PATH环境变量保证在命令行模式下
Python解释器和相关工具在任何路径都能运行
Web应用 - 基于浏览器来使用的应用程序
手机App - 应用中呈现的数据和内容是通过服务端的程序动态生成的
用程序动态生成页面内容
用Python编写服务器端的程序,为Web和手机应用生成动态内容
开发效率高 + 生态圈非常繁荣
Django / Flask / Tornado / Sanic / FastAPI
pip install django==2.1.14 -i https://pypi.doubanio.com/simple/
创建并运行Django项目
~ 第一种方式:
- django-admin startproject django1906
- 使用PyCharm打开项目并创建虚拟环境
~ File ---> Settings ---> Project --->
Project Interpreter ---> Add
~ Terminal --->
python -m venv venv / virtualenv
(--python=/usr/bin/python3 venv)
source venv/bin/activate / "venv/Scripts/activate" - 安装项目所需依赖项
pip install django==2.1.15 - 运行项目
~ python manage.py runserver
~ Add Configuration --> + --> Python
--> Script Path (manage.py)
--> Parameters (runserver)
~ 第二种方式:
- 用PyCharm创建一个普通的Python项目
- 安装Django所需的依赖项
~ pip install django==2.1.14 - 把Python项目变成Django项目
~ django-admin startproject django1906 . - 运行项目
~ 第三种方式:
- 克隆项目到本地
~ 使用PyCharm的"get from version control"
~ git clone [email protected]:jackfrued/django1906.git - 创建虚拟环境
~ Linux/macOS: source venv/bin/activate
~ Windows: "venv/Scripts/activate" - 重建依赖项
~ pip install -r requirements.txt
GitLab ---> Git私服
对本地代码实施版本控制并同步到版本控制服务器
- git init ---> 将普通文件夹变成版本仓库
- git add . ---> 将文件从工作区同步到暂存区
- git commit -m '...' ---> 将文件提交到本地仓库
- git status / git log
- git remote add origin
---> 绑定远端仓库 - git push -u origin master ---> 将本地代码推到服务器
DDL --> create drop alter
DML --> insert delete update
DQL --> select
DCL --> grant revoke
-- 创建数据库
create database django1906 default charset utf8;
-- 创建用户
create user 'jackfrued'@'%' identified by '123456';
-- 给用户授权
grant all privileges on django1906.* to 'jackfrued'@'%';
如果要在django2.2版本以上使用pymysql
需要在init中添加pymysql代码之后然后改源代码
高内聚 低耦合
high cohesion low coupling
项目架构模式:MVC架构模式
数据和显示分离(模型和视图解耦合)
同一个模型可以渲染成不同的视图,同一个视图可以加载不同的模型
Model - View - Controller
模型 视图 控制器
数据 数据的显示
Model - Template - View
模型 模板 视图(一部分控制器)
创建应用
~ python manage.py startapp 应用名字
~ django-admin startapp 应用名字
Django框架本身扮演了一部分控制器的角色
views.py - 控制器
~ 接收用户的请求,验证用户请求
~ 操作模型
~ 产生响应(渲染页面)
Django ORM框架
对象关系映射框架 - 解决对象模型到关系模型双向转换问题
Fruit ---> fruit ---> fruit.save()
---> fruit.delete()
cursor.execute('insert into tb_fruit values (%s, %s)')
INSTALLED_APPS = [
.....
'polls',
]
python manage.py makemigrations polls
python manage.py migrate
insert into tb_subject (name, intro) values
('Python+人工智能', 'Python是一种跨平台的计算机程序设计语言。是一种面向对象的动态类型语言,最初被设计用于编写自动化脚本(shell),随着版本的不断更新和语言新功能的添加,越来越多被用于独立的、大型项目的开发。'),
('JavaEE+分布式', 'Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程。'),
('HTML5+跨平台', 'HTML5是构建Web内容的一种语言描述方式。HTML5是互联网的下一代标准,是构建以及呈现互联网内容的一种语言方式.被认为是互联网的核心技术之一。HTML产生于1990年,1997年HTML4成为互联网标准,并广泛应用于互联网应用的开发。');
创建Django后台超级管理员账号
python manage.py createsuperuser
http://127.0.0.1:8000/admin
{% load staticfiles %}
{% static 静态资源 %}
Ajax - Asynchronous JavaScript and XML
YAML
JSON - JavaScript Object Notation
{
"from": "Jack",
"to": "Tom",
"content": "Hello, world!"
}
CSRF ---> 跨站请求伪造 ---> {% csrf_token %}
Cross Site Request Forge
HTTP协议是无连接状态协议 --->两次请求之间不会保存用户的任何数据
再次请求服务器的时候 服务器无法得知请求来自哪个用户的请求
一般情况下服务器应用都需要记住用户来为用户提供更好的服务
要记住用户有三种辅助方式:(用户跟踪)
1.URL重写 http://www.baidu.com/uid=xxxxxx
2.隐藏域(影视表单域) --> 埋点
3.浏览器本地存储
cookie - 浏览器中的临时文件可以保存键值对
cookie 中的数据在发起HTTP请求式 会自动加载请求头中
window.localStorage / window.sessionStorage
cookie和sisson的关系
request.session --->服务器内存中的一个对象
cookie --> 用户浏览器临时文件 -->cookie中保存了sisson的ID
csfr跨站请求
非ASCII字符以及特殊字符都不能出现在URL中
加密解密 rsa / aes
编码解码 base64 / 百分号编码
from urllib.parse import quote
编码
result = quote(字符串)
解码
result = unquote(百分号字符)
摘要签名 md5 sha1 sha256 sha512
pycharm 取消双击shift
1、按ctrl+shift+a,弹出搜索框
2、输入registry,然后按回车
3、找到“ide.suppress.double.click.handler”,将后面的复选框勾上
4、勾选上复选框后直接点击close,然后双击shift的时候就不会再出现全局搜索框了
可以将错误的日志写进指定的文件
如果空文件夹想纳入版本控制,直接在目录中添加一个无意义的.gitkeep文件就行,默认版本控制不会加入空文件夹
strftime() -- 时间格式化
vue中的过滤器
? : 写法是vue中的三元操作符
vue中阻止时间默认行为
orderdict -- 有序字典
类.object.filter(条件).only(字段) -- 查询的时候指定字段
类.object.filter(条件).defer(字段) -- 查询的时候排除字段
在标签中加入v-cloak
可以让数据没加载出来的时候不显示标签源码
利用redis做缓存,利用延迟同步的原理,先将数据存到redis,最后以周期同步的方式更新到数据库
mysql给相应权限
安装django_debug-toobar流程
pycharm删除依赖项
RESTful
数据反向工程--表转模型
python manage.py inspectdb > common/models.py --- 将数据库中的表转化到common应用中的models.py文件中
实体 --> 数据 --> 数据接口 --> 网络API(HTTP/HTTPS)
REST架构 --> RESTful API --> 无状态、幂等性
REpresentational State Transfer ---> 表述性状态转移
最适合互联网应用的架构
水平扩展 ---> 单机结构 ---> 多机结构(分布式集群)
HTTP --> 无连接无状态
URL --> Universal Resource Locator --> 资源
HTTP协议 请求行 GET/POST/DELETE/PUT/PATCH
新建 - POST
查看 - GET
更新 - PUT / PATCH
删除 - DELETE
设计URL --> 名词
查询数据库为空的数据
多对一、一对多的优化筛选
改变全局下载路劲:
在C:\Users\Administrator 目录下创建pip文件夹
在pip文件夹中创建一个pip.ini文件
文件中写入
[global]
index-url=https://pypi.doubanio.com/simple
保存搞定
创建序列化器的时候筛选
class DistrictSimpleSerializer(serializers.ModelSerializer):
class Meta:
model = District
# 需要序列化的
fields = ('distid', 'name')
# 不需要序列化的用exclude
# 查询所有用fields=(__all__)
创建类view接口
1.先创建序列器
2.在view中创建视图类
3.配置路径
优化view类中的筛选机制(类的继承,实现一个类同时完成查多和查一)
1.创建序列器(在需要的字段相同的情况下只用建造一个)
class EstateView(ListAPIView, RetrieveAPIView):
queryset = Estate.objects.all()
serializer_class = EstateSimpleSerializer
def get(self, request, *args, **kwargs):
if 'pk' in kwargs:
return RetrieveAPIView.get(self, request, *args, **kwargs)
else:
return ListAPIView.get(self, request, *args, **kwargs)
路劲配置(局部路径)
urlpatterns = [
path('districts/', get_provinces),
path('districts//', get_cities),
path('estates/', EstateView.as_view()),
path('estates/', EstateView.as_view()),
path('agents/', show_agent)
多重继承实现增删改查全接口
序列器部分
为了实现不同的请求通过不同的序列器需要增加多个序列器
class EstateSimpleSerializer(serializers.ModelSerializer):
class Meta:
model = Estate
fields = ('estateid', 'name', 'hot', 'intro')
class EstatePostSerializer(serializers.ModelSerializer):
class Meta:
model = Estate
exclude = ('agents',)
view中的代码部分:
class EstateView(ListAPIView, RetrieveAPIView, CreateAPIView, DestroyAPIView):
queryset = Estate.objects.all().defer('district', 'agents')
def get_serializer_class(self):
if self.request.method == 'POST':
return EstatePostSerializer
else:
return EstateSimpleSerializer
def get(self, request, *args, **kwargs):
if 'pk' in kwargs:
return RetrieveAPIView.get(self, request, *args, **kwargs)
else:
return ListAPIView.get(self, request, *args, **kwargs)
用RetrieveUpdateDestroyAPIView方法实现增删改查全接口
class EstateView(ListCreateAPIView,RetrieveUpdateDestroyAPIView):
queryset = Estate.objects.all().defer('district', 'agents')
def get_serializer_class(self):
if self.request.method in ('POST','PUT','PATCH'):
return EstatePostSerializer
else:
return EstateSimpleSerializer
def get(self, request, *args, **kwargs):
if 'pk' in kwargs:
return RetrieveAPIView.get(self, request, *args, **kwargs)
else:
return ListAPIView.get(self, request, *args, **kwargs)
requests请求接口
import json
import requests
resp = requests.get('http://localhost:8000/api/estates/')
estates = json.loads(resp.text)
for index, estate in enumerate(estates):
print(index, estate)
利用ModelViewSet创建视图集
增删改查全套
class HouseTypeViewSet(ModelViewSet):
queryset = HouseType.objects.all()
serializer_class = HouseTypeSerializer
只能读
class HouseTypeViewSet(ReadOnlyModelViewSet):
queryset = HouseType.objects.all()
serializer_class = HouseTypeSerializer
局部路劲配置
# 创建路由器
router = SimpleRouter()
# 注册视图集(这里路径不能加/,路由器会自动加)
router.register('housetypes', HouseTypeViewSet)
# 最后把注册好的路径列表和原列表合并
urlpatterns += router.urls
创建全局分页
setting中修改REST的配置
# 给REST_FRAMEWORK增加配置,添加分页功能
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 5,
}
如果不想被全局分页影响修改视图中的pagination_class
class HouseTypeViewSet(ModelViewSet):
queryset = HouseType.objects.all()
serializer_class = HouseTypeSerializer
# 修改默认不分页就不被全局分页所影响
pagination_class = None
给view视图集增加缓存功能
1.在类继承的多是多继承一个CacheResponseMixin
class EstateView(CacheResponseMixin, ListCreateAPIView, RetrieveUpdateDestroyAPIView):
queryset = Estate.objects.all().defer('district', 'agents')
def get_serializer_class(self):
if self.request.method in ('POST', 'PUT', 'PATCH'):
return EstatePostSerializer
else:
return EstateSimpleSerializer
def get(self, request, *args, **kwargs):
if 'pk' in kwargs:
return RetrieveAPIView.get(self, request, *args, **kwargs)
else:
return ListAPIView.get(self, request, *args, **kwargs)
2.在setting中增加一个配置代码
# 配置混入类,增加是视图集缓存功能
REST_FRAMEWORK_EXTENSIONS = {
'DEFAULT_CACHE_RESPONSE_TIMEOUT': 120,
'DEFAULT_USE_CACHE': 'default',
'DEFAULT_OBJECT_CACHE_KEY_FUNC': 'rest_framework_extensions.utils.default_object_cache_key_func',
'DEFAULT_LIST_CACHE_KEY_FUNC': 'rest_framework_extensions.utils.default_list_cache_key_func',
}
=========================================================
Django中利用method_decorator装饰器可以加在类上,指定什么装饰器装在什么方法上
待更新!
通过接口筛选数据
1.手写代码:
2.利用django-filter三方库
1)先安装:pip install django-filter
2)将django_filters添加到settings.py中的INSTALLED_APPS中
OrderingFilter -- 排序(可以不加)
filter_fields -- 通过什么筛选(后面跟元组形式)
ordering_fields -- 通过哪些字段排序
3)
class EstateView(CacheResponseMixin, ListCreateAPIView, RetrieveUpdateDestroyAPIView):
queryset = Estate.objects.all().defer('agents')
filter_backends = (DjangoFilterBackend, OrderingFilter)
filter_fields = ('district',)
ordering_fields = ('hot', 'estateid',)
通过字段范围搜索
1.先申明一个筛选类,可以建一个py文件单独存放
自定义一个类来作为筛选条件
class EstateFilterSet(django_filters.FilterSet):
"""自定义FilterSet"""
minhot = django_filters.NumberFilter(field_name='hot', lookup_expr='gte')
maxhot = django_filters.NumberFilter(field_name='hot', lookup_expr='lte')
keyword = django_filters.CharFilter(method='filter_by_keyword')
@staticmethod
def filter_by_keyword(queryset, key, value):
queryset = queryset.filter(Q(name__contains=value) |
Q(intro__startswith=value))
return queryset
在视图集类中加入
class EstateView(CacheResponseMixin, ListCreateAPIView, RetrieveUpdateDestroyAPIView):
queryset = Estate.objects.all().defer('agents')
# 筛选
filter_backends = (DjangoFilterBackend, OrderingFilter)
# filter_fields = ('district',)
# 通过自定义的筛选类来筛选
filterset_class = EstateFilterSet
# 排序
ordering_fields = ('hot', 'estateid',)
# 加上下面这句就不限流
# throttle_classes = ()
配置限流(全局作用)
在setting中的REST_FRAMWORK配置中加入
# 给REST_FRAMEWORK增加配置,添加分页功能
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 5,
# 限流配置包
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.AnonRateThrottle',
),
'DEFAULT_THROTTLE_RATES': {
'anon': '5/min',
},
}
在视图类中加入throttle_classes = ()就不会限流
查询的时候在输入select_related/prefetch_related 1对多/多对多 的方法来解决查询数据库1+N的问题
写序列化方法满足单独的序列需求
view中:
serializer中:
搜索引擎
倒排索引:Whoosh + jieba
ElasticSearch / Solr + ik-analysis/smartcn/pinyin - 可以支持中文分词的搜索引擎
模糊查询:
需要导入drf中的OrderingFilter
from rest_framework.filters import OrderingFilter
然后再视图类中加入
filter_backends = (DjangoFilterBackend, OrderingFilter) - 筛选类/排序类
filterset_class = 你定制的筛选类
定制的筛选类需要先导入django_filters
视图中:
class HouseInfoViewSet(ReadOnlyModelViewSet):
"""房源只读视图集"""
# 查询集
queryset = HouseInfo.objects.all()\
.defer('detail', 'hassubway', 'isshared',
'hasagentfees', 'userid', 'distid2', 'agent')\
.select_related('type', 'estate')\
.prefetch_related('tags')
# 指定你的序列器
serializer_class = HouseInfoSerializer
# 指定你的筛选类
filter_backends = (DjangoFilterBackend, OrderingFilter)
# 自定义你的筛选方法
filterset_class = HouseInfoFilterSet
# 指定你的排序字段
ordering = ('-pubdate', 'houseid')
ordering_fields = ('price', 'area')
声明的筛选器
# 声明筛选类
class HouseInfoFilterSet(django_filters.FilterSet):
# 通过title来模糊查询
title = django_filters.CharFilter(lookup_expr='contains')
# 最低价格小于等于(如果表中没有title字段需要用field_name重新指定)
minprice = django_filters.NumberFilter(field_name='price', lookup_expr='gte')
maxprice = django_filters.NumberFilter(field_name='price', lookup_expr='lte')
# 通过Q对象自己写筛选方法,method=方法名
district = django_filters.NumberFilter(method='filter_by_district')
#自定义筛选静态方法
@staticmethod
def filter_by_district(queryset, key, value):
return queryset.filter(Q(distid2=value) |
Q(distid3=value))
重写序列器的get方法返回需求的序列数据
class HouseInfoSerializer(serializers.ModelSerializer):
"""房源序列器"""
# 声明要重写的序列字段
pubdate = serializers.SerializerMethodField()
type = serializers.SerializerMethodField()
estate = serializers.SerializerMethodField()
tags = serializers.SerializerMethodField()
district = serializers.SerializerMethodField()
# 利用静态方法重新处理字段并返回
@staticmethod
def get_pubdate(obj):
return obj.pubdate.strftime('%Y--%m--%d')
# 如果是ForeignKey的字段obj.属性取到的是关联对象,需要用.方法调用属性
@staticmethod
def get_type(obj):
return obj.type.name
@staticmethod
def get_estate(obj):
return obj.estate.name
# 如果是多对多,manytomany需要用obj.属性.查询方法()返回queryset对象,然后再作处理
@staticmethod
def get_tags(obj):
return [i.content for i in obj.tags.all()]
# 如果没有关联属性又想取关联的对象需要先用指定参数查询一次返回queryset对象然后再作处理
@staticmethod
def get_district(obj):
data = District.objects.filter(distid=obj.distid3).only('name').first()
return data.name
富文本编辑器 - 可以排版、可以插入图片音频视频美化代码
Markdown编辑器
国内比较好用的富文本编辑器
wangEditor
kindEditor
软件开发过程模型
1.传统过程模型:大型、超大型项目
-瀑布模型(经典模型):
1)可行性分析(研究做还是不做)=> 可行性分析报告
2)需求分析(研究做什么)=> {
头脑风暴 => 思维导图 => 需求规格说明书
产品原型图 => Azure RP => 线框图/高保真原型
}
3)概要设计和详细设计
数据库设计 => E-R图 => 物理模型图 <= PowerDesigner
物理模型图
面向对象设计(OOAD)=> UML => 类图
is-a:继承
has-a:关联=>聚合/合成
use-a:依赖
类图
4)编码
5)测试(单元->系统->集成->验收)
6)交付(上线)+运维
缺点:周期慢,不能拥抱需求变化
2.敏捷模型:迅速推出产品占领市场
核心理念:增量迭代式开发
SCRUM(将软件开发分为若干个冲刺周期) - Spring Cycle
1)建立或更新需求池 -> 用户故事(User Story / Epic) --> 看板
- 计划会议(评估需求)
- 日常开发(站立会议、番茄工作法)
4)版本发布
5)评审会议(show case) - 回顾会议 (总结得失)
时长:2-4周
敏捷闭环工具 - JIRA/禅道
团队协作工具 - 钉钉/TeamBition
版本控制 - 团队开发模式下如何使用Git
Git私服 => GitLab
Git便准工作流程:
git-flow
github-flow(PR流程)
流程
1.克隆或者更新项目
git clone URL
git pull
2.基于master分支创建自己的分支
git branch Name
git checkout Name / git swich Name
git checkout -b Name /git switch -C Name (同时完成建立和切换分支)
3.在自己的分支上做开发并实施版本控制
4.把自己的分支推到服务器上
git push -u origin Name
5.在线发起合并请求(线上操作),请求将工作成果合并到master分支
git branch -d/-D 分支名 (删除分支,-D强删除,删除之前切换到master分支)
git diff - 版本比较
gitlab-flow
调用三方服务的两种方式:
1.API调用 => 通过Http协议请求URL的方式获得服务(数据)
短信、地图、天气 、个人认证、企业认证、物流
短信网关:云片、SendCloud、螺丝帽
2.SDK调用 => 安装对应的库文件,使用封装好的类、函数来调用服务
pip install alipay-sdk-python 阿里云支付
pip install qiuniu 青牛存储
pip install oss2 阿里云对象存储
JWT - Json Web Token - 生成用户身份令牌的方式
优点:JWT无法伪造、也无法篡改令牌中包含的用户信息
保存在用户浏览器端,服务器没有任何储存开销,便于水平扩展
事务上下文环境,要么全成功,要么全失败
@atomic() => 事务装饰器
命名关键字参数
在传参数的时候必须传关键字参数
函数的参数前面加*
def function(*,x,y):
pass
解决富文本在html中源码显示问题
在vue中,将html的标签加上v-html='内容' 可以告诉内容为html格式
将项目部署上线
1.在项目的setting.py中加入下面代码:
# 保持HTTPS连接的时间
# SECURE_HSTS_SECONDS = 3600
# SECURE_HSTS_INCLUDE_SUBDOMAINS = True
# SECURE_HSTS_PRELOAD = True
# 自动重定向到安全连接
# SECURE_SSL_REDIRECT = True
# 避免浏览器自作聪明推断内容类型
# 防范跨站脚本攻击,浏览器会自己分辨文件内容,不管后缀名,就算是恶意代码也会执行
SECURE_CONTENT_TYPE_NOSNIFF = True
# 避免跨站脚本攻击
SECURE_BROWSER_XSS_FILTER = True
# COOKIE只能通过HTTPS进行传输
# SESSION_COOKIE_SECURE = True
# CSRF_COOKIE_SECURE = True
# 防止点击劫持攻击手段 - 修改HTTP协议响应头
# 当前网站是不允许使用
2.检查项目能否正常运行
python manage.py check --deploy
3.连接服务器,创建文件夹
mkdir -p ~/project/{code,conf,logs,stat}
cd project
3.克隆项目到code文件夹
cd code
git clone 项目地址
4.创建虚拟环境重建依赖项
cd ..
pip3 install virtualenv
virtualenv --python=/usr/bin/python3 venv
source venv/bin/activate
pip install -r code/项目名/requirements.txt
pip install uwsgi (处理django 动态文件)
5.编写uWSGI的配置文件 -- conf/uwsgi.ini
1 [uwsgi]
2 # 守护进程
3 master=true
4 # 进程个数
5 processes=4
6 # 虚拟环境
7 pythonhome=/root/project/venv
8 # 项目地址
9 chdir=/root/project/code/test (最后一个是文件名字)
10 # 指定python解释器
11 pythonpath=%(pythonhome)/bin/python
12 # 指定wsgi文件
13 module=gkd.wsgi (这里是写项目名字)
# 用ifconfig eth0 查看内网ip
14 # 通信的地址和端口(自己服务器的IP地址和端口)=172.18.61.250:8000
15 socket=172.31.106.24:8000
16 # 日志文件地址
17 logto=/root/project/logs/uwsgi.log
6.启动uWSGI
uwsgi --ini conf/uwsgi.ini & (后台运行加最后的&)
7.动静分离部署
yum install -y nginx (如果没有nginx需要先安装)
vim /etc/nginx/nginx.conf
全局Nginx配置文件------/etc/nginx/nginx.conf下面的配置
5 user root;
6 worker_processes auto;
7 error_log /var/log/nginx/error.log;
8 pid /run/nginx.pid;
9
10 # Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
11 include /usr/share/nginx/modules/*.conf;
12
13 events {
14 use epoll;
15 worker_connections 1024;
16 }
17 http {
18 log_format main '$remote_addr - $remote_user [$time_local] "$request" '
19 '$status $body_bytes_sent "$http_referer" '
20 '"$http_user_agent" "$http_x_forwarded_for"';
21
22 access_log /var/log/nginx/access.log main;
23
24 sendfile on;
25 tcp_nopush on;
26 tcp_nodelay on;
27 keepalive_timeout 30;
28 types_hash_max_size 2048;
29
30 include /etc/nginx/mime.types;
31 default_type application/octet-stream;
32
33 # Load modular configuration files from the /etc/nginx/conf.d directory.
34 # See http://nginx.org/en/docs/ngx_core_module.html#include
35 # for more information.
36 include /etc/nginx/conf.d/*.conf;
37 include /root/project/conf/nginx.conf;
38 }
局部配置 -- conf/nginx.conf
1 server {
2 listen 80;
3 server_name _;
4 # access_log /root/project/logs/access.log;
5 # error_log /root/project/logs/error.log;
6 location / {
7 include uwsgi_params;
8 uwsgi_pass 172.31.106.24:8000;
9 }
10 location /static/ {
11 alias /root/project/stat/;
12 expires 30d; }
13 }
修改配置文件 - code/项目文件夹/项目名/settings.py
STATIC_ROOT = '/root/project/stat/'
收集静态资源 - code/项目名
python manage.py collectstatic
启动Nginx:
systemctl start nginx
8.部署HTTPS
-
部署HTTPS
server {
listen 443 ssl;
server_name _;ssl_certificate /root/project/cert/jackfrued.top.pem;
ssl_certificate_key /root/project/cert/jackfrued.top.key;
ssl_session_timeout 5m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;root /root/project/stat/html;
location /api/ {
include uwsgi_params;
uwsgi_pass [图片上传失败...(image-a1de84-1579252109800)]
172.18.61.250:8000;
}
location /upload/ {
include uwsgi_params;
uwsgi_pass [图片上传失败...(image-ca2e63-1579252109800)]
172.18.61.250:8000;
}
location /media/ {
alias /root/project/code/blog/media/;
expires 30d;
}
location /static/ {
alias /root/project/stat/;
expires 30d;
}
}
Session的配置
# 浏览器关闭自动清除cookie,但是服务器不会删除session会话,还留在数据库中
# 如果想要删除,需要在生命周期过后执行
# python manage.py clearsessions
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
# 设置session的缓存时间,单位/秒
SESSION_COOKIE_AGE = 86400
base64编码
在前端中:
btoa(字符串) - 编码
atob(base64) - 解码
计算事件差值问题
没有做时区矫正和矫正之后的不能做减法
如果非要计算必须用django内置的timezone模块
django中间件(新版写法)
在django中指定的app中新建一个middlewares.py的文件
def simple_middleware(get_response):
# One-time configuration and initialization.
def middleware(request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = get_response(request)
# Code to be executed for each request/response after
# the view is called.
return response
return middleware
或者写成一个类
class SimpleMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = self.get_response(request)
# Code to be executed for each request/response after
# the view is called.
return response
写好了之后在settings中加入配置
项目.middlewares.中间件名字
如果需要捕获异常可以利用返回的请求编号判断
配置日志,记录错误信息
logger = logging.getLogger('django_request')
生成一条日志记录这个错误以便通过追踪日志定位错误解决问题
写日志的时候需要根据日志的级别调用不同的方法
debug --- 调试信息
info --- 普通(正常信息)
warning --- 警告信息
error --- 错误信息
critical --- 致命错误
logger.error(f'{request.path}:{view_func.__name__}:{resp.status_code}')
例子:
中间件的本质就是装饰器,只不过被中间件装饰的是django项目中的视图函数
中间件实现了对请求和响应的拦截和过滤,可以对请求和响应做额外的处理
中间件的依赖关系
各种响应状态码的原因
读写Excel
Office 2010 ---> Excel 2010 ---> openpyxl
Office 2004 ---> Excel 2003 ---> xlwt / xlrd
例子:
浏览器导出或下载数据
excel导出的例子
def export_excel(request):
carinfo = request.GET.get('carinfo', '')
queryset = Record.objects.filter(is_deleted=False) \
.defer('deleted_time', 'is_deleted', 'updated_time') \
.select_related('car').order_by('-makedate')
if carinfo:
queryset = queryset.filter(
Q(car__carno__iexact=carinfo) | Q(car__owner__contains=carinfo)
)
wb = Workbook()
sheet = wb.active
col_names = ('车牌号', '车主姓名', '违章原因', '违章时间', '处罚方式', '是否受理')
for index, col in enumerate('ABCDEF'):
sheet[f'{col}1'] = col_names[index]
row = 2
for record in queryset:
sheet[f'A{row}'] = record.car.carno
sheet[f'B{row}'] = record.car.owner
sheet[f'C{row}'] = record.reason
sheet[f'D{row}'] = record.makedate
sheet[f'E{row}'] = record.punish
sheet[f'F{row}'] = '已受理' if record.dealt else '未受理'
row += 1
buffer = BytesIO()
wb.save(buffer)
resp = HttpResponse(buffer.getvalue())
# 设置响应头指定MIME类型(告诉浏览器服务器返回的内容类型)
resp['content-type'] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
filename = quote('违章记录表.xlsx')
# 设置响应头指定内容处置方式(inline-内联打开;attachment-下载)
# 如果要设置中文文件名,需要将中文处理成百分号编码,否则不能写到响应头
resp['content-disposition'] = f'attachment; filename="{filename}"'
return resp
调用三方服务(自己无法搞定的事情)
SDK --->
- pip install alipay-python-sdk
- pip install qiniu
API调用 ---> URL统一资源定位符 ---> HTTP ---> JSON - pip install requests
发送短信验证码(逻辑)
# 发送短信验证码
def get_mobile_code(request):
# 获取redis的原生链接
# redis_cli = get_redis_connection()
# 从前端获取手机号
tel = request.GET.get('tel')
# 正则匹配手机号是否合法
if TEL_PATTERN.fullmatch(tel):
# 检查缓存中是否存在手机号
if caches['default'].get(f'voteapp:polls:block:{tel}'):
data = {'code': 20004, 'msg': '请不要重复发送!'}
else:
code = gen_mobile_code()
result = send_mobile_code(tel, code)
if result['code'] == 0:
caches['default'].set(f'voteapp:polls:block:{tel}', code, 60)
caches['default'].set(f'voteapp:polls:valid{tel}', code, 600)
data = {'code': 20000, 'msg': '短信发送成功!'}
else:
data = {'code': 20001, 'msg': '请重新发送!'}
else:
data = {'code': 20003, 'msg': '请输入有效的手机号码!'}
return JsonResponse(data)
从请求中获取信息
HttpRequest
- 获取请求参数:GET/POST/FILES/data
- 获取请求路径: path / get_full_path_info()
- 获取请求方法: method
- 获取请求头: META / COOKIES
- 判断是不是异步请求:is_ajax()
从响应中获取信息
HttpResponse
- 响应状态码:status_code
- 响应字符集:charset
- 响应头:
- response['content-type']
- response['content-disposition']
set_cookie / delete_cookie
用户跟踪:记住用户提供更好的服务
---> 浏览器本地储存
- cookie
- localStorage / sessionStorage
- IndexDB
读取可写入cookie信息
def show_subjects(request):
queryset = Subject.objects.all()
resp = render(request, 'subjects.html', {'subjects': queryset})
# 读取浏览器中的cookie
# request.COOKIES.get('friends')
# 在浏览器中写入cookie
# resp.set_cookie('username', 'wangdachui')
# friends = ['cang', 'xiaoze', 'maria']
# resp.set_cookie('friends', json.dumps(friends))
# resp.set_signed_cookie('') - 存入带签名的cookie
# request.get_signed_cookie('') - 取出带签名的cookie
return resp
Vue中的延迟渲染
在前端标签中加上v-cloak,然后利用选择器选择所有带v-cloak的标签,display标记成none
利用三方库序列化模型对象
安装rest_framework
pip install djangorestframework
安装之后新建一个serializers.py
在里面定义需要序列化的模型
from rest_framework import serializers
from polls.models import Subject, Teacher
class SubjectSimpleSerializer(serializers.ModelSerializer):
class Meta:
model = Subject
# 指定只序列化的属性
fields = ('no', 'name')
class SubjectSerializer(serializers.ModelSerializer):
class Meta:
model = Subject
# 指定哪些不序列化
exclude = ('create_data',)
class TeacherSerializer(serializers.ModelSerializer):
sex = serializers.SerializerMethodField()
# 需要单独处理的属性
@staticmethod
def get_sex(teacher):
return '男' if teacher.sex else '女'
class Meta:
model = Teacher
exclude = ('subject',)
直接在view试图函数中调用自己定义的序列化器
# 利用rest_framwork序列化模型
@api_view(('GET',))
def show_subjects(request):
queryset = Subject.objects.all()
serializer = SubjectSerializer(queryset, many=True)
return Response(serializer.data)
# 序列化
@api_view(('GET',))
def show_teachers(request):
sno = int(request.GET.get('sno', '0'))
teachers = []
subject = Subject.objects.only('name').filter(no=sno).first()
if subject:
queryset = Teacher.objects.defer('subject').filter(subject__no=sno)
teachers = TeacherSerializer(queryset, many=True).data
serializer = SubjectSimpleSerializer(subject)
return Response({'teachers': teachers, 'subject': serializer.data})
分页
全局分页:
# 配置全局分页
REST_FRAMEWORK = {
# 配置默认页面大小
'PAGE_SIZE': 10,
# 配置默认的分页类
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
}
指定分页:
在工具文件中定义一个分页器
# 定制分页
# 两种用法:
# 可以直接加到settings中的默认分页器,也可以在视图集中用pagination_class=分页器名字,指定
class CustomizePagination(PageNumberPagination):
page_size_query_param = 'size'
max_page_size = 50
如果不想被全局分页影响的接口就在类视图集中添加一句:
pagination_class = None
SQL查询效率
explain + sql 语句可以看见详细的查询参数
从参数中的type中可以看出sql的查询效率怎么样
如果查询效率垃圾就需要为字段添加索引
需要用什么字段查询就为什么字段添加索引,
比如说姓名查询:
-- 查询
explain select * from tb_emp where ename='张三丰';
-- 创建索引(为指定字段添加索引,效率最高,但是占空间)
create index idx_emp_ename on tb_emp(ename);
-- 前缀索引,为指定字段的第一个字添加索引(效率较高,但是会找所有姓张的)
create index idx_emp_ename on tb_emp (ename(1));
-- 添加复合索引,(利用第一和第二个字段查效率很高,只有第一个也可以,但是只用第二个查就用不到索引,查询会优先根据第一个查询)
create index idx_emp_ename on tb_emp(ename,job);
-- 删除指定字段的索引
drop index idx_emp_ename on tb_emp;
总而言之要避免不带前缀的模糊查询,类似%张%,这样的,性能极差
视图集定制筛选条件:
前端的弹窗确认,从对象层面删除标签(前端删除)
利用对象获取该标签在列表中的位置
缓存页面(静态页面一般不需要缓存)
settings:
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://120.27.93.188:54321/0",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"PASSWORD": "ikaros123",
}
}
}
views:
# 缓存页面
# timeout - 缓存时间(秒)
# cache - 指定缓存到哪个缓存服务器
@cache_page(timeout=1800, cache='default')
def show_index(request):
return redirect('/static/html/index.html')
装饰类的装饰器:
给类加上缓存装饰器
# 装饰类的装饰器
@method_decorator(decorator=cache_page(timeout=300, cache='default'), name='list')
@method_decorator(decorator=cache_page(timeout=1800, cache='default'), name='retrieve')
class RecordViewSet(ReadOnlyModelViewSet):
queryset = Record.objects.all()
serializer_class = RecordSerializer
def get_queryset(self):
queryset = Record.objects.filter(is_deleted=False) \
.defer('is_deleted', 'deleted_time', 'updated_time') \
.select_related('car').order_by('-makedate')
keyword = self.request.GET.get('keyword')
if keyword:
queryset = queryset.filter(
# iexact 忽略大小写 contains 模糊查询(性能很差)
Q(car__carno__iexact=keyword) | Q(car__owner__contains=keyword)
)
return queryset
但是因为这种给类指定装饰器的函数不能解决需求,比如如果数据库已经更改但是缓存还是以前的,给用户显示的数据就会有出入,所以一般采用重写方法自己定义缓存比较好
例子:
class RecordViewSet(ReadOnlyModelViewSet):
queryset = Record.objects.all()
serializer_class = RecordSerializer
# 重写list方法,自己配置缓存
def list(self, request, *args, **kwargs):
# 获取完整的请求路径并做md5处理
path = gen_md5_hex(request.get_full_path())
# 生成独一无二的键
key = f'carsys:search:views:{path}'
redis_cli = get_redis_connection()
data = redis_cli.get(key)
if data:
resp = Response(json.loads(data))
if not data:
resp = super().list(request, *args, **kwargs)
redis_cli.set(key, json.dumps(resp.data), ex=300)
return resp
用普通的类视图ListAPIView实现接口
跟ModelViewSet的区别是轻量化,可以自己指定请求方法,筛选条件,不象增删改查全套那么冗余
S
实现缓存的通常做法
在视图类中重写list方法,利用用户的请求路径做md5摘要处理,并且加上前缀(前缀一般用工程名+app名+请求的什么+名字+摘要)
因为response对象不好做序列化,所以一般就把里面的数据做数据化处理
什么时候用高速缓存服务 ---> Redis
数据:体量不大,访问频率极高,不怎么变化 ---> 就是热数据
冷数据一般采用 ---> 关系型数据库
如果关系型数据某张表更新特别频繁,那么可以把表直接放到redis,这种情况下需要配置好
项目中的缓存在redis的redis.conf 文件中需要绑定内网ip,(查看方式ifconfig eth0)
异步化处理
项目中一旦遇到耗时间的任务
不要因为一个任务执行时间很长而阻塞了 其他任务或者请求
- 1把任务派发到线程中
- 2把任务派发到消息队列中,通过其他程序处理任务
多线程下载:
起进程就是把Thread改成Process
from multiprocessing.dummy import Process
多线程利于密集型io操作
多进程利于大量运算型操作
进程通信,数据传输
IPC机制(Inter-Procession Communication)
~ 1. 管道 (multiprocession.Queue)
~ 2. 套接字(socket)
空间换时间 ---> 池化技术 ---> 线程池/连接池
线程池!!!
import json
import time
from concurrent.futures.thread import ThreadPoolExecutor
from threading import Thread
import requests
# 定义美女对象
class Beauty:
def __init__(self, ctime, title, description, picUrl, url):
self.ctime = ctime
self.title = title
self.description = description
self.pic_Url = picUrl
self.url = url
def download_image(image_url):
resp = requests.get(image_url)
filename = image_url.split('/')[-1]
with open(f'imgs/{filename}', 'wb') as file:
file.write(resp.content)
return '100'
def main():
resp = requests.get(
'http://api.tianapi.com/meinv/index?key=9643fd6c91335ed5e5759775dbcd2541&num=50'
)
data = json.loads(resp.content)
# 创建线程池,定义最大的线程数量
pool = ThreadPoolExecutor(max_workers=16)
start = time.time()
futures = []
for beauty_dict in data['newslist']:
# 直接将字典解包生成美女对象
model = Beauty(**beauty_dict)
# 将指定的函数提交到线程池中
# submit会返回一个将来时对象,将将来时对象添加到列表中
futures.append(pool.submit(download_image, model.pic_Url))
for future in futures:
# result可以获取任务的返回值
a = future.result()
print(a)
end = time.time()
print(f'执行时间:{end - start:.1f}秒')
if __name__ == '__main__':
main()
Celery
Celery从任务中获取任务执行 ---> 消费者(消息)
也可以将任务派发到队列中 ---> 生产者(消息)
市场上比较好的消息队列
Erlang ---> RabbitMQ / RocketMQ / ActiveMQ / Kafka
Redis ---> list ---> rpush / blpop
Web应用优化两大定律:
1.缓存
2.消息队列 - 上下游节点解耦合
- 削除流量洪峰(突发流量)
安装celery
pip install celery
windows 要安装,不要报错
pip install eventlet
在django中使用
在项目的init.py配置如下
import os
import celery
# 将Django的配置文件注册到环境变量中
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'carsys.settings')
app = celery.Celery('carsys', broker='redis://:ikaros123@120.27.93.188:54321/1/')
# 从项目的配置文件读取Celery配置信息
app.config_from_object('django.conf:settings')
# 让Celery自动从参数指定的应用中发现异步任务/定时任务
app.autodiscover_tasks(['search', ])
在需要加入消息队列的函数头上打上@app.task装饰器
打上装饰器之后才会被许可加入队列
调用的方式
需要加入消息队列的函数直接.delay(参数,redis数据库编号)就搞定
启动消费者:
celery -A carsys worker -l debug
windows下:
celery -A carsys worker -l info -P eventlet
例子:
补充:
检查三方库有没有冲突
pip check
读取大文件的操作
![image.png](https://upload-images.jianshu.io/upload_images/20013041-
7cf385bd9d22a569.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
更优雅的写法
项目的运用,下载PDF文件
UML物理模型
可以根据自己画的图纸自动生成框架代码
免费的软件:
StarUML
团队开发辅助工具(CASE工具)
CASE - Computer-Aided Software Engineering
~ 版本控制工具 : Git、Subversion(SVN)
- Git标准化流程(分支管理策略)
~git-flow
~github-flow
~gitlab-flow
Github-flow
~ 克隆或者更新代码到本地
git clone
git pull
~ 创建自己的分支(基于master),在自己的分支做开发
查看分支: git branch -a
创建分支: git branch hellokitty 名字一般使用自己的代号,或者功能代号
切换分支: git checkout hellokitty / git switch hellokitty
创建并切换: git checkout -b hellokitty
git switch -C hellokitty (新版)
~ 把自己的分支推到服务器上
git push -u origin hellokitty
~ 在线发起合并请求(Pull Request / Merge Request)
- 新建Pull Request
- 选择源分支(自己的分支)和目标分支(master)
- 写一个合并请求的申请
- 选择代码审查者、代码测试人员、标签、优先级、... ...
5.合并请求被接受,hellokitty就会合并到master分支
每次拉代码的时候要加上master分支名
git pull origin master
git diff 检查冲突在什么地方
- 当面协商解决冲突,重新提交代码,并将自己的分支推到服务器
pycharm新建分支
vcs -> git -> branches -> new -> 分支名
安装禅道
解压缩解归档并指定路径
z - 解压缩
x - 解归档
f - 指定文件名
C - 指定解压的路径
tar -zxf ZenTaoPMS.12.3.zbox_64.tar.gz -C /opt
git分支合并:
~ 合并:git merge
- git checkout baiyuanfang
- git merge --no-ff new-cool-function
~ 变基:git rebase ---> 分支比较干净不混乱
- git rebase baiyuanfang
- git checkout baiyuanfang
- git merge new-cool-function
Epic(大的功能) / Story(用户故事) / Task(任务)
私单报价:
根据需求细分功能到不可再分,估算项目完成的总时长
计算公式:
自己的时薪 * (1.25,2.5) * 估算总时长 = 报价
线程锁:
例子
"""
被多个线程竞争的资源称之为临界资源
临界资源通常需要锁进行保护
Rlock ---> Reentrant Lock (重入锁)
current_thread() - 获得线程的名字
"""
import time
from threading import Thread, RLock, Condition
class Account:
def __init__(self):
self.balance = 0
self.lock = RLock()
# 线程调度
self.condition = Condition(self.lock)
def deposit(self, money):
# # 获得锁
# self.lock.acquire()
# # 因为无论如何获取锁之后都需要释放,不然会导致其他线程无法获取,造成死锁现象
# # 所以必须把释放锁卸载finally中无论如何必须执行
# try:
# new_balance = self.balance + money
# time.sleep(0.01)
# self.balance = new_balance
# finally:
# # 释放锁
# self.lock.release()
# 因为锁对象也支持作用域写法,所以可以写成如下:
# 源码中在作用域进入会自动调用acquire()
# 离开自动调用release()
with self.lock:
new_balance = self.balance + money
time.sleep(0.01)
self.balance = new_balance
# 唤醒等待中的线程
self.condition.notify_all()
def withdraw(self, money):
if money <= self.balance:
# 原理同上
with self.lock:
# 根据条件暂停线程,释放掉获得的锁
while money > self.balance:
self.condition.wait()
new_balance = self.balance - money
time.sleep(0.01)
self.balance = new_balance
"""
原理:
模拟存钱和取钱的过程:
起100个线程,每次给账户存入1元
如果不加线程锁,会导致线程竞争,每次都只会存入1元,需要通过线程锁保护临界资源
线程锁一般用from threading import RLock 重入锁
创建锁对象 lock = RLock()
获得锁 lock.acquire()
释放锁 lock.release()
因为获得锁之后线程结束必须释放锁,不然其他线程没法获取,造成死锁现象
所以一般都用try-finally或者with语法,保证锁一定释放
如果有存钱和取钱的情况同时发生,并且是多线程,那就需要保证余额足够的情况下才能取钱
这个时候就必须暂停线程和唤醒线程
from threading import Thread, RLock, Condition
参数是创建的锁对象
线程调度对象 condition = Condition(lock)
暂停线程 condition.wait()
唤醒线程 condition.notify_all()
根据条件暂停线程,不断的判断,当条件满足的时候唤醒线程
"""
def main():
account = Account()
threads = []
for _ in range(100):
# 100个线程竞争一个资源,最后只会在原始的基础上+1
t = Thread(target=account.deposit, args=(1,))
threads.append(t)
t.start()
for t in threads:
t.join()
print(account.balance)
if __name__ == '__main__':
main()
django模型补充:
在外键中加上下面参数,在迁移的时候可以不加外键
db_constraint=False
外键中加上related_name='+',表示不允许反查,就是不允许通过主表对象.从表_set查询,如果想查的话,就写成related_name='a',想查的时候直接用主表对象.a就行
Distributed Deny of Service
TCP Flood
DDoS攻击 ---> 高仿DDoS
四层模型
python中时间并发的三种方式:
- 多线程:I/O密集型应用
- 多进程:计算密集型应用
-
异步编程:I/O密集型应用(异步I/O --> 多个并发的I/O操作)
~ 多个子程序相互协作,让CPU保持较高的利用率,达到协作式并发效果
~ 协程(coroutine)、微线程、线程
override ---> 重写,覆盖,覆写,重载(overload)
偏函数:
partial的用法 --->
异步编程,协程
例子:
import asyncio
# 在函数前面加async的函数叫异步函数,
# 调用不能直接执行,而是返回一个协程对象
async def output(num):
# 如果异步程序执行到某一步需要很长的等待就用await将之挂起,让出cpu给其他异步程序
await asyncio.sleep(1)
print(num)
# 生成10个协程对象
cos = [output(i) for i in range(1, 11)]
# 创建一个异步事件循环对象
loop = asyncio.get_event_loop()
# 调用对象的run_until_complete()方法,运行事件循环
# 因为该方法需要一个将来对象,所以用wait方法将之转换成future对象
loop.run_until_complete(asyncio.wait(cos))
你没见过的yield骚操作,生成器变协程
"""
协程:生成器对象执行预激活操作就可以将生成器升级为协程
"""
# 定义一个生成器函数
def calc_avg():
total, count = 0, 0
avg_value = None
while True:
# 将yield关键字写到右边,表示等待数据
value = yield avg_value
total += value
count += 1
avg_value = total / count
def main():
# 创建生成器对象
gen = calc_avg()
# 将对象预激活 用next(gen)也可以实现预激活
gen.send(None)
print(gen.send(10))
print(gen.send(20))
print(gen.send(30))
print(gen.send(40))
print(gen.send(10))
if __name__ == '__main__':
main()
查看自己的系统编码:
import sys
print(sys.getdefaultencoding())
python利用三方库查看域名信息
pip install python-whois
import whois
whois.whois(域名)
获取一个对象不存在的属性容易报错
这个时候可以使用getattr()函数
value = getattr(对象,'属性',默认值)
如果能获取对象的属性就直接获取,获取不到就赋默认值
还有一个setattr可以赋值
datetime模块简单使用
datetime.date(年,月,日) - 生成时间对象
datetime.date.today() - 返回当前时间
两个时间可以直接相减做差,返回的对象可以调用方法
差值对象.days - 直接返回相差的天数
时间对象.strftime('%Y-%m-%d') - 格式化时间格式
rest自带限流配置
基于redis,必须加上redis缓存
# 配置限流,必须要加上redis缓存配置
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle'
),
'DEFAULT_THROTTLE_RATES': {
# 未登录用户每分钟可以请求5次
'anon': '5/min',
# 登陆过的用户每分钟可以请求50次
'user': '50/min'
}
}
绕过代理获取请求的ip地址,方便设置黑白名单
# 获取正确的IP地址,可以绕过代理
def get_ip_address(request):
"""获得请求的IP地址"""
ip = request.META.get('HTTP_X_FORWARDED_FOR', None)
return ip or request.META['REMOTE_ADDR']
模型相关:
模型
django的查询:
模型.objects.get/all
查询单个数据:
模型.objects.get(pk=主键)
查询所有数据:
模型.objects.all() --- 返回值是一个queryset,直接遍历即可
删除数据(删除要基于查询的基础之上):
查询出对象之后直接对象.delete() 即可
修改数据(修改数据也要基于查询的基础之上):
查询出来之后直接修改值,然后save()即可
一对多查询
模型设计:
直接在从表中写
外键名 = models.ForeignKey(主表名)
其实外键就是从表的一个字段取关联主表的主键
根据主表查询从表数据
def getStudents(request):
# 查询python班级有哪些学生
grade = Grade.objects.get(pk=1)
student_list = grade.student_set
for student in student_list.all():
print(student.name, student.id)
return HttpResponse('查询成功!')
如果2张表有主从关系 而且还是一对多 那么主表对象有一个属性就是 ---》
从表模型小写_set 那么这个属性就是隐形属性
根据从表查主表数据:
def getGrade(request):
student = Student.objects.get(pk=1)
name = student.s_grade.name
print(name)
return HttpResponse('查询成功!')
元信息
在模型中写
class Meta:
db_table = '表名'
常用的字段属性以及约束
AutoField
一个根据实际ID自动增长的IntegerField
CharField(max_length=字符长度)
TextField
大文本字段,一般超过4000字以上,默认的表单控件是Textarea文本域
IntegerField
整数
DecimalField(max_digits=None,decimal_places=None)
max_digits位数总数
decimal_places小数点后的数字位数
FloatField
用Python的float表示的浮点数
BooleanField
true/false,默认表单控件式CheckboxInput
NullBooleanField
支持true/false/null三种值
DateField([auto_now=False,auto_now_add=False])
DateField.auto_now 每次保存对象时,自动设置该字段为当前时间
DateField.auto_now_add当对象第一次被创建的时候设置当前时间
TimeField
使用python的datatime.time实例表示时间,参数同DateField
DateTimeField
使用python的datetime.datetime实例表示时间和日期,参数同上
FileField
一个上传文件的字段
ImageField
存图片的字段,继承了FileField的所有属性和方法
约束
null
如果为True,Django会将空值以Null存入,默认为False
db_column
字段的名称
blank
如果为True,允许字段为空白,默认False
db_index
如果为True,则在表中会为字段创建索引
default
默认值
primary_key
为True则该字段会成为模型的主键
unique
如果为True,字段唯一
ForeignKey 一对多,将字段定义在多的模型中
ManyToManyField 多对多,将字段定义在两个模型中
OneToOneField 一对一,将字段定义在任意模型中
模型过滤查询
def testFilter(request):
# filter和exclude返回都是queryset,所以可以链式调用
# food_list = Food.objects.filter(id=3)
# 大于
# food_list = Food.objects.filter(id__gt=3)
# 大于等于
# food_list = Food.objects.filter(id__gte=3)
# 小于是lt,lte
# 排除
# food_list = Food.objects.exclude(id=3)
# 大于3但不能等于4
# food_list = Food.objects.exclude(id=4).filter(id__gt=3)
food_list = Food.objects.filter(id__in=[1, 2, 3])
for food in food_list:
print(food.id, food.name)
return HttpResponse('查询成功!')
补充:
startswith() 以什么开头
endswith() 以什么结尾
以上两种类似于模糊查询like
contains() 是否包含,大小写敏感
isnull,isnotnull 判断是否为空
获取前端传过来的参数
url(r'^get_clerk_list/', views.get_clerk_list)
def get_clerk_list(request):
id = request.GET.get('id')
print(id)
return HttpResponse('123')
查询集
四种查询方法
all()
filter()
exclude()
order_by() 默认是升序,在字符串前面加'-'就是降序
values() 返回的是一个列表,里面嵌套的是字典
queryset可以直接切片,用法等同于列表
缓存集和懒查询
缓存集
每次查询都会将数据做一个缓存,以后的查询直接走缓存,不会去查询数据库
懒查询/懒加载
只有我们在迭代结果集,或者获取单个对象的属性的时候,才会去查询数据
获取单个对象
get() 如果找不到的时候会报错
如果存在多个值,也会报错
first() 获取查询集的第一个
last() 获取查询集的最后一个
count() 获取查询集中有几个对象
exists() 判断queryset中是否有数据
查询时间
filter(时间字段__year=int)
year
month 会出现时区问题 需要在settings中USE-TZ设置为False
day
week_day
hour
minute
second
聚合函数:
使用aggregate()函数返回聚合函数的值
Avg
Count
Max
Min
Sum
eg:Student.objects.aggregate(Max('age'))
跨关系查询:
def getRelation(request):
# 查询小米公司的人
clerks = Clerk.objects.filter(company_id__name='小米')
for i in clerks:
print(i.name, i.age, i.salary)
# 查询女王的公司
company = Company.objects.filter(clerk__name='女王')[0]
print(company.name)
return HttpResponse('查询成功')
获取请求中的数据
如果不知道是get还是post请求可以直接
request.data.get('参数名')
linux添加环境变量
用户目录下
vim .bash_profile
# .bash_profile
2
3 # Get the aliases and functions
4 if [ -f ~/.bashrc ]; then
5 . ~/.bashrc
6 fi
7
8 # User specific environment and startup programs
9
10 export lbw='卢本伟牛逼!'
11 PATH=$PATH:$HOME/bin
12 export PATH=$PATH:/usr/local/python37/bin
保存之后
source .bash_profile
搞定
python中调用环境变量中的值
import os
print(os.environ['lbw'])
身份令牌:jwt的使用
例子:
token里面还可以添加其他的参数
接口认证,必须登陆之后才能调用接口
在视图集中加上
authentication_classes = (自己的认证类,)
授权:
自定义授权类
登陆操作:
验证操作
知识补充:
从请求头拿数据 - request.META.get()
拿参数 - request.GET/POST.get()
拿数据 - request.FILED.get()
拿cookie - request.COOKIE.get()
通过JWT完成身份认证
如果想拿取请求头headers中的信息需要加HTTP_大写参数
比如:
request.META.get('HTTP_USER_AGENT')
创建认证类:
FBV实现身份验证
在需要认证的函数上打赏装饰器
@authentication_classes()
里面加上你自己创建的认证类,元组形式
如果不成功记得清除缓存
类的装饰器写法
类的对象一般不能调用
但是如果加上了call方法,就可以调用了
用对象作为装饰器和用函数作为装饰器
例子:
class SayHello:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print('say hello 之前!')
result = self.func(*args, **kwargs)
print('say hello 之后!')
return result
@SayHello
def hello(name):
print(f'hello,{name}')
def main():
hello('张三')
if __name__ == '__main__':
main()
上传文件,转存文件操作
表单上传文件
异步上传文件
上传文件到七牛云
上传文件之前对文件大小做判断
为什么使用关系型数据库?
~ 持久化 / 管理数据(方便查询)
~ 一致性 ---> 事务 ---> 锁
并发数据访问 ---> 五类问题
- 第一类丢失更新
- 第二类丢失更新
- 脏读(读脏数据):一个事务读到另一个事务还没有提交的数据。
- 不可重复读:一个事务读取之前获取的数据,发现原来的数据已经被另一个已提交的事务修改。
- 幻读:一个事务读取数据,读到了另一个已提交事务插入的新数据。
锁:共享锁、排他锁、表锁、行锁
- MyISAM ---> 读数据
- InnoDB ---> 行锁,事务 ---> 互联网高并发
- Memory
设置合理的事务隔离级别让MySQL自动选择合适的锁
- 查看事务隔离级别:select @@tx_isolation;
- 修改事务隔离级别:set session transaction isolation level read committed;
~ READ UNCOMMITTED --> 脏读
~ READ COMMITTED --> 不可重复读
~ REPEATABLE READ --> 幻读
~ SERIALIZABLE --> 没任何毛病(性能最差)
事务隔离级别越高,数据越安全,但是性能也越差
~ 完整性
- 实体完整性(每个实体都是独一无二的,没有冗余)- 主键/唯一索引
- 参照完整性(父表没有的记录子表也不能出现)- 外键
~ 注意:实际项目中出于性能的考虑会去掉外键约束
- 域完整性(录入的数据都是有效的)
~ 数据类型和数据长度
~ 非空约束
~ 默认值约束
~ 检查约束范式理论(Normal Form)---> 设计表
~ 数据库表的规范程度
- 1NF:字段不可分割
- 2NF:非主键字段都依赖主键
- 3NF:消除传递依赖
~ 实际项目中可能为了提升性能使用反范式设计
SOA
Service-Oriented Architecture
SasS:Software as Service
PasS:Platform as Service
IasS:Infrastructure as Service
怎么把软件做成服务
RMI / RPC: 远程方法调用(远程过程调用)- nameko
WebService -> SOAP
RESTful -> HTTP -> JSONF
django项目相关,(100天,好好看!)
https://gitee.com/ikaros274556330/luo_shen_python100_days/blob/master/Day91-100/Django%E7%9F%A5%E8%AF%86%E7%82%B9%E6%A6%82%E8%BF%B0.md
自定义过滤类
新建一个filters.py,单独管理
from django.db.models import Q
from django_filters import rest_framework as df
from common.models import Company
class CompanyFilterSet(df.FilterSet):
"""自定义过滤类"""
icate = df.NumberFilter(field_name='industry_category')
fstage = df.NumberFilter(field_name='financing_stage')
ssize = df.NumberFilter(field_name='staff_size')
name = df.CharFilter(method='filter_by_name')
@staticmethod
def filter_by_name(queryset, name, value):
return queryset.filter(
Q(short_name__contains=value) | Q(full_name__contains=value)
)
class Meta:
model = Company
fields = ('icate', 'fstage', 'ssize', 'name')
解决javascript跨域获取数据的问题django-cors-headers
pip install django-cors-headers
然后在setting中配置一下代码
跨域请求数据只针对浏览器,就是其他网站请求本网站的数据,如果是源码请求不受这个限制
curl命令
在Linux终端直接输入curl + 网址 可以直接获取接口数据
加上 -I 可以看见响应头参数
加上 -v 可以看见最详细的信息
安装httpie,跟curl差不多
pip install httpie
http + 网址 请求接口
更新pip
pip3 install -U pip
linux中
复制并处理文件中的注释行
Docker
安装docker
先更新你的yum库
yum update
如果有旧的docker可以先删除
yum erase -y docker docker-common docker-engine
先装一个工具包
yum install -y yum-utils
通过yum工具包添加yum源(安装docker-ce的源)
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
安装一个管理工具
yum install -y device-mapper-persistent-data lvm2
安装docker-ce
yum install -y docker-ce
启动docker
systemctl start docker
查看镜像
docker images
删除镜像
docker rmi nginx/Image ID
下载镜像对应的镜像(nginx为例)
docker pull nginx:latest 下载最新版nginx,不加:latest默认也是最新的
配置国内源
cd /etc/docker
touch daemon.json
vim daemon.json
添加如下代码
{
"registry-mirrors": [
"http://hub-mirror.c.163.com",
"https://registry.docker-cn.com"
]
}
保存之后重启docker
systemctl restart docker
搞定
创建和运行容器
-p 暴露端口 阿里云端口/容器端口
-d 后台运行
--name 你的容器名字
--rm 加上--rm参数容器会在停止时被移除
docker run -d -p 80:80 --name myws nginx:latest
停止和启动容器
docker stop 容器名
docker start 容器名
如果创建的时候使用了--rm stop之后就应该看不见容器了
docker container ls -a 查看所有容器的详情
如果没有指定--rm可以使用下面的命令删除
docker rm 容器名
删除正在运行的容器需要加上-f强删
docker rm -f 容器名
docker ps 查看运行中的容器
docker logs 容器名 查看指定容器的日志
添加自己的页面到容器中的nginx
-v 数据卷操作,将自己的页面映射到nginx主页
html路径/nginx默认页路径
docker run -d -p 80:80 -v /root/code/html:/usr/share/nginx/html --name myws nginx
下载Mysql镜像
docker pull mysql:5.7.30
可以改:不能改
自己建数据库数据地址 - /root/mysql/data
自己建mysql日志地址 - /root/mysql/log
容器中数据 - /var/lib/mysql
容器中日志 - /var/log
第一个mysql
docker run -d -p 3306:3306 --name mysql571 -v /root/mysql57/data1:/var/lib/mysql -v /root/mysql/log1:/var/log -e "MYSQL_ROOT_PASSWORD=123456" mysql:5.7.30
第二个mysql
docker run -d -p 3308:3306 --name mysql572 -v /root/mysql57/data2:/var/lib/mysql -v /root/mysql/log2:/var/log -e "MYSQL_ROOT_PASSWORD=123456" mysql:5.7.30
Linux重新执行命令
!history编号
docker起redis
docker run -d -p 54321:6379 --name redis-master redis:5.0.5 redis-server --requirepass ikaros111
进入容器中
docker exec -it redis-master /bin/bash
redis-cli
操作跟Linux差不多
退出直接exit
查看容器的ip地址
docker respect 容器名
添加从机操作
--link 冒号左右都写主人名字就对了 (就是给主人起别名)
--slaveof 指定主容器ip,可用别名
端口号要写默认的
--masterauth 主人容器口令
从机只能读,不能写
在从机中输入info可以看见各种信息,包括主人信息
docker run -d --name redis-slave-1 --link redis-master:redis-master redis:5.0.5 redis-server --slaveof redis-master 6379 --masterauth ikaros111
docker run -d --name redis-slave-2 --link redis-master:redis-master redis:5.0.5 redis-server --slaveof redis-master 6379 --masterauth ikaros111
docker run -d --name redis-slave-3 --link redis-master:redis-master redis:5.0.5 redis-server --slaveof redis-master 6379 --masterauth ikaros111
docker多容器管理 - docker-compose
安装
pip3 install docker-compose
升级
pip3 install -U docker-compose
django配置多数据库
第一步,除了default,增加新的数据库配置
第二步,添加setting中的一个配置
DATABASE_ROUTERS = []
第三步,新建routers.py文件创建数据库路由类
见官方文档
第四步,在需要的模型中Meta中添加标记
接口文档生成神器
django-rest-swagger
装好之后在INSTALL_APP中加入
加入之后在url下面配置
doc_view = get_swagger_view(title="标题")
urlpatterns = [
....
path('docs/',doc_view),
]
配置好之后直接起项目,访问docs/路径就可以看见文档了
如果报错在settings下面加入
国内比较好用的文档生成
YAPI
RAP2(阿里啊,推荐)
github地址
用Docker搭建RAP2私服
pip3 install docker-comose
git clone --depth=1 https://github.com/thx/rap2-delos.git
cd rap2-delos
docker-compose up -d
docker-compose exec delos node scripts/init
http://服务器公网IP地址:3000
docker-compose down (关闭整个容器)
普通程序使用celery
安装
pip install celery
win下安装
pip install eventlet
例子:
import celery
# 创建对象,配置redis,broker是队列池
# backend是后台,如果要取结果就需要配置
app = celery.Celery(
main='celery_test',
broker='redis://:ikaros111@120.27.93.188:54321/5',
backend='redis://:ikaros111@120.27.93.188:54321/6'
)
# 在函数上打上装饰器
@app.task
def hello(x: int):
print('hello!')
return x > 10
# 直接调用函数.delay() 括号中直接传参就行了
result1 = hello.delay(5)
result2 = hello.delay(12)
# 结果直接result,没有括号
print(result1.result)
print(result2.result)
如果有多个任务,可以链式执行
在django项目中,如果想在将celery的执行结果放到数据库中而不是redis
需要安装一个三方库
django-celery-results
然后做一次迁移,会生成几张新的表
安装好之后字celery的配置中backend后面驱动写成django-db,会默认去找default数据库保存结果数据
在django中
让celery自己取找定时任务或者异步任务
django中celery的定时任务
django-celery-beat
消息代理(broker):Redis / RabbitMQ
运营指标
PV UV IP
必看文档
敏捷开发
https://blog.51cto.com/newthink/1775427
最佳日志实践
https://zhuanlan.zhihu.com/p/27363484
RBAC权限模型
HTTP协议是请求响应式协议,服务器无法主动发数据给浏览器
pull模式:拉模式 / push模式: 推模式(服务器主动发数据给浏览器)
不停的轮询(每隔一段时间就向服务器法请求问是否有新消息)
建立长连接:
- Ajax长轮询
- iframe长轮询
- webSocket(全双工)
django中使用websocket
安装
pip install channels
配置
先在INSTALLED_APPS中加入
然后加上一个配置
ASGI_APPLICATION = 'webapp.routing.application'
在需要的app下新建一个routing.py文件
from django.urls import path
from chatroom.views import ChatConsumer
websocket_urlpatterns = [
path('ws/chat/', ChatConsumer),
]
在项目的(与settings同级)目录下新建一个routing.py
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from chatroom.routing import websocket_urlpatterns
# 固定写法
# 创建application对象,协议路由
application = ProtocolTypeRouter({
# 如果是websocket走哪
'websocket': AuthMiddlewareStack(
URLRouter(
# 指定从哪个路由
websocket_urlpatterns
)
),
})
在视图函数或者新建一个consumer.py中新建一个类
如果视图函数中代码比较多,最好还是新建一个文件
from channels.generic.websocket import WebsocketConsumer
# Create your views here.
# 定义类
class ChatConsumer(WebsocketConsumer):
# 这三个方法都是回调方法
# 建连接要干嘛
def connect(self):
# 接受连接,这句必加
self.accept()
print('跟浏览器建立了长连接')
# 断连接要干嘛
def disconnect(self, code):
print('连接已经断开')
# 发消息要干嘛
def receive(self, text_data=None, bytes_data=None):
print(text_data)
# 如果浏览器发送的二进制消息:bytes_data
# self.send就是主动给服务器发消息
self.send('收到了你的消息!')
最后是前端代码
websocket测试
websocket测试
主动发消息
先装三方库
pip install channels_redis
待更新
需要配置反向代理
自动化运维服务管理
Supervisor
Circus
数据结构和算法
数据结构:数据再计算中如何储存
离散存储
连续存储
算法:解决问题的步骤
渐近时间复杂度
渐进空间复杂度
python自带小根堆结构
heapq模块
解决top_k问题
In [1]: import heapq
In [2]: items = [64,23,51,64,7,5,86,34,2,34,98]
In [3]: heapq.nlargest(3,items)
Out[3]: [98, 86, 64]
In [4]: heapq.nsmallest(5,items)
Out[4]: [2, 5, 7, 23, 34]
cookbook需要看的章节
1,2,4,7,8,9,12