1.通过组id查询用户
qs = self.get_queryset().filter(is_staff=0, groups=keyword).order_by(“-create_time”)
2.前台打印
前台通过consle.log打印的日志是引用日志,如果后面的代码逻辑中修改了数据,则在浏览器种看到的是修改后的数据,若要查看当时的日志,可以用深拷贝Json.Stringfy().
3.设置联合主键
class TestCaseModel(BaseModel):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=50, verbose_name="该名称", help_text="测试用例名称")
# config = models.IntegerField(verbose_name="公共配置id", help_text="公共配置id", blank=True, null=True)
config = models.ForeignKey(to=ConfigModel, on_delete=models.CASCADE, verbose_name="公共配置id",
help_text="公共配置id", related_name="testcase", blank=True, null=True)
fixture = models.TextField(verbose_name="前置用例", help_text="前置用例", blank=True, null=True)
content = models.TextField(verbose_name="测试用例内容", help_text="测试用例内容", blank=True)
interface = models.ForeignKey(to=InterfaceModel, on_delete=models.CASCADE, verbose_name="所属接口id",
help_text="所属接口id", related_name="testcase")
switch = models.BooleanField(verbose_name='选择开关', help_text='选择开关', default=True)
running_state = models.BooleanField(verbose_name='运行状态', help_text='运行状态', default=False)
group = models.ForeignKey(to=Group, on_delete=models.CASCADE, verbose_name="所属组id", help_text="所属组id",
related_name="inter_case_group")
is_delete = models.IntegerField(verbose_name="逻辑删除(0-未删除、1-删除)", help_text="逻辑删除(0-未删除、1-删除)",
default=0)
class Meta:
db_table = "dtp_testcase"
verbose_name = "测试用例"
verbose_name_plural = verbose_name
unique_together = ("name", "interface")
def __str__(self):
return self.name
4.标签中动态加载的数据
<el-button type="info" size="mini" icon="el-icon-video-play" @click="openRunBatchTestCaseDialog" plain>{{ batchData }}</el-button>
5.sessionStorage
设置sessionStorage
window.sessionStorage.setItem('id', response.data.data.id)
window.sessionStorage.setItem('group', response.data.data.group)
window.sessionStorage.setItem('token', response.data.data.token)
window.sessionStorage.setItem('username', response.data.data.username)
获取sessionStorage
window.sessionStorage.getItem('group')
6.数组解包
[...this.testcaseList]
this.editTestCaseForm = { ...caseInfo }
7.路由跳转
this.$router.push('/login')
8.设置定时器
created () {
this.getProjectList()
this.timer = window.setInterval(() => {
window.setTimeout(this.getProjectList(), 0)
}, 2000)
},
9.bool是int的子类
print(isinstance(True, int))
print(True + True)
print(True - True)
print(3 * True + True)
print(3 * True - False)
print(True << 10)
True
2
0
4
3
1024
10.异常捕获中的else
try:
print('third element:', a_list[2])
except IndexError:
print('raised IndexError')
else:
print('no error in try-block') # 只有在try里面没有异常的时候才会执行else里面的表达式
11.协程
目前主流语言基本上都选择了多线程作为并发设施,与线程相关的概念是抢占式多任务(Preemptive multitasking),而与协程相关的是协作式多任务。不管是进程还是线程,每次阻塞、切换都需要陷入系统调用(system call),先让CPU跑操作系统的调度程序,然后再由调度程序决定该跑哪一个进程(线程)。而且由于抢占式调度执行顺序无法确定的特点,使用线程时需要非常小心地处理同步问题,而协程完全不存在这个问题(事件驱动和异步程序也有同样的优点)。
因为协程是用户自己来编写调度逻辑的,对CPU来说,协程其实是单线程,所以CPU不用去考虑怎么调度、切换上下文,这就省去了CPU的切换开销,所以协程在一定程度上又好于多线程。对cpu来说让cpu感觉像是只有一个线程在执行工作,与进程中只有一个线程是不一样的,可以在一个进程中开启多个协程工作,但是对cpu而言没有了cpu的切换,感觉是一个线程在工作。
使用协程,可以不受线程开销的限制,我尝试过一次把20W条url放在单进程的协程里执行,完全没问题。所以最推荐的方法,是多进程+协程(可以看作是每个进程里都是单线程,而这个单线程是协程化的)多进程+协程下,避开了CPU切换的开销,又能把多个CPU充分利用起来,这种方式对于数据量较大的爬虫还有文件读写之类的效率提升是巨大的。
12.cpu和核
一个cpu只有一个核的叫单核cpu,目前市场上的cpu多数为多核cpu,一个cpu上有多个核,常见电脑一般都只有一个cpu,超大型计算机可能有多个cpu,“systeminfo”可以查看到cpu个数。
13.python的GIL、多线程、多进程
pthon的多线程,经常听到老手说:“python下多线程是鸡肋,推荐使用多进程!”,但是为什么这么说呢?
-
**GIL是什么?**GIL的全称是Global Interpreter Lock(全局解释器锁),来源是python设计之初的考虑,为了数据安全所做的决定。
-
每个CPU在同一时间只能执行一个线程(在单核CPU下的多线程其实都只是并发,不是并行,并发和并行从宏观上来讲都是同时处理多路请求的概念。但并发和并行又有区别,并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔内发生。)
-
在Python多线程下,每个线程的执行方式:
1.获取GIL
2.执行代码直到sleep或者是python虚拟机将其挂起。
3.释放GIL
-
可见,某个线程想要执行,必须先拿到GIL,我们可以把GIL看作是“通行证”,并且在一个python进程中,GIL只有一个。拿不到通行证的线程,就不允许进入CPU执行。
在python2.x里,GIL的释放逻辑是当前线程遇见IO操作或者ticks计数达到100(ticks可以看作是python自身的一个计数器,专门做用于GIL,每次释放后归零,这个计数可以通过 sys.setcheckinterval 来调整),进行释放。
而每次释放GIL锁,线程进行锁竞争、切换线程,会消耗资源。并且由于GIL锁存在,python里一个进程永远只能同时执行一个线程(拿到GIL的线程才能执行),这就是为什么在多核CPU上,python的多线程效率并不高
-
那么是不是python的多线程就完全没用了呢?
在这里我们进行分类讨论:
1、CPU密集型代码(各种循环处理、计数等等),在这种情况下,ticks计数很快就会达到阈值,然后触发GIL的释放与再竞争(多个线程来回切换当然是需要消耗资源的),所以python下的多线程对CPU密集型代码并不友好。
2、IO密集型代码(文件处理、网络爬虫等),多线程能够有效提升效率(单线程下有IO操作会进行IO等待,造成不必要的时间浪费,而开启多线程能在线程A等待时,自动切换到线程B,可以不浪费CPU的资源,从而能提升程序执行效率)。所以python的多线程对IO密集型代码比较友好。
而在python3.x中,GIL不使用ticks计数,改为使用计时器(执行时间达到阈值后,当前线程释放GIL),这样对CPU密集型程序更加友好,但依然没有解决GIL导致的同一时间只能执行一个线程的问题,所以效率依然不尽如人意。
多核多线程比单核多线程更差,原因是单核下多线程,每次释放GIL,唤醒的那个线程都能获取到GIL锁,所以能够无缝执行,但多核下,CPU0释放GIL后,其他CPU上的线程都会进行竞争,但GIL可能会马上又被CPU0拿到,导致其他几个CPU上被唤醒后的线程会醒着等待到切换时间后又进入待调度状态,这样会造成线程颠簸(thrashing),导致效率更低
-
回到最开始的问题:经常我们会听到老手说:“python下想要充分利用多核CPU,就用多进程”,原因是什么呢?
原因是:每个进程有各自独立的GIL,互不干扰,这样就可以真正意义上的并行执行,所以在python中,多进程的执行效率优于多线程(仅仅针对多核CPU而言)。
所以我们能够得出结论:多核下,想做并行提升效率,比较通用的方法是使用多进程,能够有效提高执行效率
14.django中间件
django中间件在settings中,有系统中间件和三方中间件,还可以自定义中间件(继承MiddlewareMixin)
中间件有执行有顺序要求,如果有依赖,必须放在依赖的后面。可以在工程任意位置自定义中间件,注册在MIDDLEWARE中即刻生效。中间件中有process_request,process_response,process_view,process_exception这几个钩子函数
15.codecs专门用作编码转换
codecs专门用作编码转换,用codecs提供的open方法来指定打开的文件的语言编码,它会在读 取的时候自动转换为内部unicode
{"商家名称": "珍滋味港式粥火锅(工体店)", "评分": 27.0, "地址": "火锅工人体育场东路丙2号中国红街3号楼2层里", "人均消费": 174, "评论数量": 2307}
{"商家名称": "井格老灶火锅(望京新世界店)", "评分": 26.2, "地址": "火锅望京广顺南大街路16号", "人均消费": 105, "评论数量": 1387}
{"商家名称": "脸谱港式火锅(酒仙桥丽都店)", "评分": 24.5, "地址": "火锅芳园西路6号一层", "人均消费": 218, "评论数量": 39}
当json数据格式是上面这种,每个字典之间以回车分割时,可以采用如下方法将json编码的字符串转换为python数据结构dict:
# -*- coding: utf-8 -*-
import json
import codecs
data = []
with codecs.open("hg.json", "r", "utf-8") as f:
for line in f:
dic = json.loads(line)
data.append(dic)
print json.dumps(dic, indent=4, ensure_ascii=False, encoding='utf-8')
字符的编码是按照某种规则在单字节字符和多字节字符之间进行转换的某种方法。从单字节到多字节叫做decoding,从多字节到单字节叫做 encoding。在这些规则中经常用到的无非是UTF-8和GB2312两种。在Python中,codecs模块提供了实现这些规则的方法,通过模块公开的方法我们能够方便地获取某种编码方式的Encoder和 Decoder工厂函数(Factory function),以及StreamReader、StreamWriter和StreamReaderWriter类。总结一下,codecs模块为我们解决的字符编码的处理提供了lookup方法,它接受一个字符编码名称的参数,并返回指定字符编码对应的 encoder、decoder、StreamReader和StreamWriter的函数对象和类对象的引用。为了简化对lookup方法的调用, codecs还提供了getencoder(encoding)、getdecoder(encoding)、getreader(encoding)和 getwriter(encoding)方法;进一步,简化对特定字符编码的StreamReader、StreamWriter和 StreamReaderWriter的访问,codecs更直接地提供了open方法,通过encoding参数传递字符编码名称,即可获得对 encoder和decoder的双向服务。
18.drf序列化器
-
drf serilizers()参数:在序列化时参数为serilizers(instance=obj),如果模型为queryset参数为serilizers(instance=obj,many=True);在反序列化创建数据时参数为serilizer(data=data),在反序列化更新数据时参数为serilizers(instance=obj, data=data)
-
序列化器,自定义的序列化器如果继承自serilizers.Serlizers,在反序列化保存和更新数据时需要重写create和updata方法,因为父类的Serlizers中只定义了函数抛出了异常,使用时需要重写方法。如果序列化器时继承自serilizers.ModelSerilizers,则不需要重写,ModelSerilizers,则不需要重写,ModelSerlizer中已经有create和update方法。
-
django和drf请求参数差异:django中get请求获取参数方式为:request.GET,post请求获取参数方式为:request.POST(表单数据)或者request.body(Json数据)drf中get请求获取参数是request.query_params,post请求获取参数是request.data。
-
django和drf响应差异:django中响应值为from django import http http.HttpResponse或者http.JsonResponse等等,不同数据类型对应不同响应,drf中响应值为from rest_framework.response import Response Response不同类型的数据都可以用Response,包括文本,字典,列表。
-
drf中列表视图是获取所有和创建单个(get,post),详情视图是获取单个、修改单个和删除单个(get、put、delete)
-
drf二级视图GenericAPIView特点:
GenericAPIView,继承自APIView类,为列表视图, 和详情视图,添加了常用的行为和属性。-
行为(方法)
get_queryset: 获取queryset的数据集
get_serializer: 获取serializer_class序列化器对象
get_object: 根据lookup_field获取单个对象
-
属性
queryset: 通用的数据集
serializer_class: 通用的序列化器
lookup_field: 默认是pk,可以手动修改id
-
-
drf中使用mixin类时需要注意:mixin类提供的方法名称和原始请求方法名称不相同。原始标准【get、post、put、delete】,mixin【list、create、retrieve、update、destroy】
-
通用视图(三级视图)特点:
- 特点: 如果没有大量自定义的行为, 可以使用通用视图(三级视图)解决
- 提供的类:CreateAPIView、ListAPIView、RetrieveAPIView、DestroyAPIView、UpdateAPIView
- 提供的方法:原始标准【get、post、put、delete】
-
视图集中ViewSetMixin的作用是可以做路由映射
url(r'^viewset/$',views.BooksViewSet.as_view({'get': 'list'})), url(r'^viewset/(?P<pk>\d+)/$',views.BooksViewSet.as_view({'get': 'retrieve'}))
-
路由中的SimpleRouter,DefaultRouter区别:DefaultRouter默认路由关联的视图集会生成5个路由,路由可以带.json和root根路由;SimpleRouter只生成列表视图路由和详情视图路由
-
序列化器中的外键关联
-
场景:模型中外键关联其他模型主键,如果序列化器继承自ModelSerializer中外键字段会自动生成一个序列化器字段,类似serializers.PrimaryKeyRelatedField,但是该字段会将外键id在反序列化的时候将前端传过来的id变成模型对象,所以需要在序列化器中重新create和updata方法,将模型对象重新转成id。djngo外键字段在orm模型迁移数据库时外键字段会自动加_id,所以模型中的project在数据库中字段变成了project_id。当遇到这种场景:前端Post请求提交的表单数据是id,但是List查询请求希望显示的是id对应的name,这种场景就需要前后端设置合理字段,前端传过来的字段名就按数据库中的字段名带id的那种,后端在序列化器中重写外键字段让该字段只参与序列化而不参与反序列化,新增"该字段_id"的字段。这样在保存数反序列化时,通过新增的project_id字段反反序列化,在List查询时,project字段参与序列化,将project_id对应的name也反回前端,返回给前端的字段"data":{“id”:5,“project”:“项目01”,“project_id”:1,“create_time”:“2022-04-18 12:02:51”,“name”:“接口06”}
-
模型:
class InterfaceModel(BaseModel): id = models.AutoField(primary_key=True) name = models.CharField(max_length=50, verbose_name="该名称", help_text="接口名称", unique=True) project = models.ForeignKey(to=ProjectModel, on_delete=models.CASCADE, verbose_name="所属项目id", help_text="所属项目id", related_name="interface")
-
序列化器:
project = serializers.StringRelatedField(label='所属项目名称', help_text='所属项目名称') # read_only=True project_id = serializers.PrimaryKeyRelatedField(queryset=ProjectModel.objects.all(), label='所属项目ID', help_text='所属项目ID') def create(self, validated_data): project = validated_data.pop("project_id") validated_data["project_id"] = project.id return super().create(validated_data)
-
前端Post请求体:
this.addInterfaceForm = { name: '接口06', project_id: 1 }
-
-
序列化器中的多级关联
-
场景:项目-模块-用例,一个项目下有多个模块,一个模块下有多个用例,在前端创建模块和用例时会选择该模块或者该用例属于那个项目和模块
-
数据结构:
[ { "id": 1, "name": "项目01", "interface": [ { "id": 1, "name": "接口01", "config": [ { "id": 1, "name": "配置01" } ] }, { "id": 2, "name": "接口02", "config": [] }, { "id": 3, "name": "接口03", "config": [] }, { "id": 4, "name": "接口04", "config": [] }, { "id": 5, "name": "接口06", "config": [] } ] }, { "id": 2, "name": "项目02", "interface": [] } ]
-
接口及序列化器实现序列化过程:
-
在项目视图中实现接口
@action(methods=["GET"], detail=False) def cases(self, request, *args, **kwargs): """获取项目测试用例列表名称""" serializer = self.get_serializer(instance=self.get_queryset().filter(is_delete=0), many=True) result = data_response(data=serializer.data) return response.Response(result)
-
实现序列化器:
# 项目Serializer: def get_serializer_class(self): if self.action == "cases": return GetTestCaseNameModelSerializer elif self.action == "configs": return GetConfigNameModelSerializer elif self.action == "run": return RunProjectModelSerializer return self.serializer_class class GetTestCaseNameModelSerializer(serializers.ModelSerializer): interface = InterfaceAndTestCaseNameModelSerializer(label="项目接口", help_text="项目接口", many=True, read_only=True) class Meta: model = ProjectModel fields = ["id", "name", "interface"] # 接口Serializer: class InterfaceAndTestCaseNameModelSerializer(serializers.ModelSerializer): testcase = TestCaseNameModelSerializer(label="接口用例", help_text="接口用例", many=True, read_only=True) class Meta: model = InterfaceModel fields = ["id", "name", "testcase"] # 用例Serializer: class TestCaseNameModelSerializer(serializers.ModelSerializer): class Meta: model = TestCaseModel fields = ["id", "name"]
-
-
19.多任务
-
在多线程中t.start(),是当前线程准备就绪等待cpu调度,具体时间由cpu决定
-
t.join(),在多线程中threading创建的多线程都是子线程,执行程序的为主线程。正常情况为主线程创建完子线程后代码继续向下执行,当有t.join()时,主线程则阻塞到此处等待子线程的结束,才继续往下执行,那个子线程join的,主线程才等待那个子线程结束了继续执行。
-
t.setDaemon(False),默认是False设置在t.start()之前,当主线程执行完所有代码后会等待子线程,等子线程结束后才终止程序。当设置t.setDaemon(True)时,主线程执行结束时则程序终止,子线程也同时终止。
-
线程锁Lock和RLock:
-
二者区别:RLock支持递归锁,当Lock遇到这种递归锁,会导致死锁线程卡死。但Lock执行效率高于RLock
-
线程锁对象给予线程安全可使用上下文写法简写:
with Look_object:
passeg: Look_object.acquire() Look_object.acquire() print(.........) Look_object.release() Look_object.release()
-
-
线程池pool.shutdown(True):等待线程池中的任务执行完毕再继续执行
-
多进程创建子进程模式有3种(fork/spawn/forkserver)
- fork:多数linux系统(完全拷贝主进程中的所有资源)
- spawn:windows系统和linux系统 (通过传参的模式,传了那些参数子进程就拷贝那些参数资源)
- forkserver:mac系统(模板机制,通过传参的模式,传了那些参数子进程就拷贝那些参数资源)
注意:有些特定资源(文件对象和锁对象),fork机制可以完成资源拷贝,但是spawn和forkserver不能通过传参的形式进行拷贝资源,需要手动重新在子进程中创建一份
-
进程池:pool.shutdown(True) 等待进程池中的任务执行完,再继续往下执行
20.单例模式
- 装饰器方式:
def singleton(cls):
_instance = {}
def inner():
if cls not in _instance:
_instance[cls] = cls()
return _instance[cls]
return inner
@singleton
class Cls(object):
def __init__(self):
pass
cls1 = Cls()
cls2 = Cls()
print(id(cls1) == id(cls2))
class Singleton(object):
def __init__(self, cls):
self._cls = cls
self._instance = {}
def __call__(self):
if self._cls not in self._instance:
self._instance[self._cls] = self._cls()
return self._instance[self._cls]
@Singleton
class Cls2(object):
def __init__(self):
pass
cls1 = Cls2()
cls2 = Cls2()
print(id(cls1) == id(cls2))
- 直接在当前类中通过重写__new__方法
class Singleton:
instance = None
lock = threading.RLock()
def __init__(self,name):
self.name = name
def __new__(cls, *args, **kwargs):
if cls.instance: # 性能优化,加锁和释放锁会消耗资源
return cls.instance
with cls.lock:
if cls.instance:
return cls.instance
cls.instance = object.__new__(cls) # 创建一个空对象
return cls.instance
21.python项目打包
创建bat文件
package.bat
pyinstaller -D -w sql_comp.py --add-data C:\Home\001_app\python38\tcl\tkdnd2.8;tkdnd
pause
22.vue中的select多选框
<el-form-item label="触发模式" prop="pattern">
<el-select v-model="addTaskForm.pattern" placeholder="请选择所触发模式" @change="selectTriggerChange(addTaskForm.pattern)">
<el-option
v-for="triggerItem in triggerNames"
:key="triggerItem.id"
:label="triggerItem.name"
:value="triggerItem.id">
</el-option>
</el-select>
</el-form-item>
多选框一般按顺序显示数据,:key显示顺序,:label页面显示内容,:value上面v-model绑定的值。所以发给给后端保存的该字段值是v-model绑定的id,但是在编辑的时候希望显示的是label文本内容,做法分2种情况:
1)数据是固定数据直接写死在页面上,在编辑的时候从后端拿到id后,通过id和triggerNames列表下标对齐的方式正确显示label.
triggerNames: [{ id: 1, name: '单次任务' }, { id: 2, name: '周期任务' }],
this.editTaskForm.pattern = this.triggerNames[this.editTaskForm.pattern - 1].id
2)该字段保存的是其它模型的主键,通过外键关联到该字段,数据库保存的是id,但是在编辑的时候希望前端显示的是name文本信息。这种情况需要在返回给前端返回数据的时候在序列化器中进行数据返回做序列化的时候增加个只参与序列化的字段(StringRelatedField),将id转成文本name返回给前端,详情见18.drf序列化器。
23.drf序列化器的字段校验功能
校验功能的三种方式
-
字段自己的校验规格(max_length…)
-
validators的校验
serializers.py自定义序列化器
from rest_framework import serializers from rest_framework.exceptions import ValidationError from app01 import models def check(data): # validators字段校验 if len(data) > 10: raise ValidationError('你太长了') else: return data # 序列化器类(序列化Book表) class BookSerializer(serializers.Serializer): # 需要序列化的字段 id = serializers.IntegerField(required=False) # required设置后表示改字段可以不传 title = serializers.CharField(max_length=32, min_length=2) price = serializers.DecimalField(max_digits=6, decimal_places=2) publish = serializers.CharField(max_length=32, validators=[check, ]) # validators传列表,会执行列表的函数进行字段校验 def create(self, validated_data): # 调用Serializer必须重写create方法 res = models.Book.objects.create(**validated_data) return res # 注意点:# 自定义的序列化器在试图函数中,数据往哪个表存识别不了,所以在序列化类里需要重写create方法
-
反序列换的局部钩子和全局钩子
from rest_framework import serializers from rest_framework.exceptions import ValidationError from app01 import models class BookSerializer(serializers.Serializer): # 需要序列化的字段 id = serializers.IntegerField(required=False) # required设置后表示改字段可以不传 title = serializers.CharField(max_length=32, min_length=2) price = serializers.DecimalField(max_digits=6, decimal_places=2) publish = serializers.CharField(max_length=32, validators=[check, ]) def create(self, validated_data): # 调用Serializer必须重写create方法 res = models.Book.objects.create(**validated_data) return res def validate_title(self, data): # 局部钩子,带一个data,data就是改字段的数据 if data.startswith('sb'): raise ValidationError('不能以sb开头') else: return data def validate(self, attrs): # 全局钩子,attrs是全部的数据 title = attrs.get('title') publish = attrs.get('publish') if title == publish: raise ValidationError('书名不能同出版社相同') else: return attrs
24.全双工与单双工的区别
1.单工数据传输只支持数据在一个方向上传输;在同一时间只有一方能接受或发送信息,不能实现双向通信,举例:电视,广播。
2.半双工数据传输允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信;在同一时间只可以有一方接受或发送信息,可以实现双向通信。举例:对讲机。
3.全双工数据通信允许数据同时在两个方向上传输,因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力;在同一时间可以同时接受和发送信息,实现双向通信,举例:电话通信。
25.websocket
启动django项目:daphne base.asgi:application -p 8000 -b 127.0.0.1
26.数据库导入其它版本数据库sql报错解决
高版本mysql 导入低版本导致的保存,需要修改编码。
解决方法:
打开sql文件,将文件中的所有
utf8mb4_0900_ai_ci
替换为utf8_general_ci
utf8mb4
替换为utf8
保存后再次运行sql文件,运行成功
27.QueryDict
QueryDict实例不可变类型,若要改值用copy()
data = request.data.copy()
28.谷歌扩展程序地址
chrome://extensions
29.robot默认编码
如果未指定编码,则Robot Framework默认使用ISO-8859-1