啦啦啦啦啦啦
10:主要课程? 答:高等数序;离散数学;大学英语;马克思主义基本原理, 近代史纲要,线性代数;计算机英语;C/C++程序设计; JAVA面向对象的程序设计;软件工程导论 ;计算机组成结构; 计算机原理;数据库原理;操作系统;软件工程经济学;算法与数据结构等 11:你的学术方向? 答:嵌入式,但不喜欢,后面改学python了。 12:你最常上的三个网站是,关注的微信公众号? 答: http://www.github.com ----------------找开源项目, loghttp://www.stackoverflow.com-------碰到问题怎么办? http://www.codeproject.com------------需要设计某个复杂的gui,又不想自己写怎么办? http://www.google.com/ncr--------------为什么是ncr?因为效率高 http://www.v2ex.com--------------------痴汉乐园,为什么是痴汉? http://www.oschina.net-----------------一些技术的八卦可以看看. http://www.zhihu.com-------------------我现在给你回帖的网站 http://www.douban.com------------------书,找电影,找音乐,听音乐 http://www.xiami.com-------------------找单曲,听音乐. python程序员,会发放一些学习资料。一个程序员的日常,有挺有意思的段子。 13:你最近在读的书? 答:最近在读沈从文的《边城》,主要讲述了一个摆渡者和他的孙女翠翠的故事,才读了二十来页。 14:你有没有什么想了解的? 答: 1:公司主要用那个框架? 2:涉及到那些技术? 3: 公司的主要业务有那些? 4: 用的是那个数据库? 5: 公司的团队大概是一个怎样的状态? 6: 公司的主要发展方向? 15:除了会python对其他语言有了解吗? 答:上大学的时候学过java和c,但都忘的差不多了。 java和c都是编译型语言,在程序执行中会把所有的代码编译一遍,编译的时候会生成一个.class为后缀的文件,它的执行速度快 Python 是解释型语言,在执行的过程中执行一行编译一行,相对速度慢一点,但是它的开发效率高. 16:你对未来一到两年有怎样的打算? 答:我希望在技术上有一个大的提升,我想多研究些底层的东西,因为上层的东西变化太快,而且我发现越学越多,而底层的东西轻易不 会改变。五年之内,希望成为一个部门主管,而且我也会往架构师的方向努力。 b.自我介绍: 在"慧聪"主要做了两个项目,k12的在线学习平台慧聪学城和我们公司内部的一个管理系统。 慧聪学城我们是2017年9月份开始立项实施的,主要用于为中小学生提供在线学习和作业管理的在线视频学习平台,并有有望成为西安第一家, 针对中小学生的编程教育,系统使用前后端分离的模式,前端使用vue框架,后端使用restframeworl实现。 并创建一对一辅导,并整合用户支付和微信消息的推送和提醒。使用Git进行协同开发。 项目三大模块: 1:导师后台是:基于Thanos组件,给导师"管理学员作业"、"学习进度"、"跟进记录"等。 2:管理后台:基于Thanos组件,给运营使用:"上传视频"、"写文章"、"优惠券的发放"、办理"退款"、"休学"等业务。 3:主站(基于Vue+Django restful framework框架): (2)我主要负责主站后端的开发。主站主要有分为五大模块: 第一个模块是课程:主要包括:课程大类和子类两种,分别有学位课程和普通课程两种, 区别在于学位课程除了包含普通课程的优惠卷和价格策略 还包含学位奖学金和分配相应的导师进行一对一的跟进辅导。 除此之外还有: "课程列表","课程详细","课程章节","课程目录","看视频","评论"。 第二个模块是资讯(重复于论坛功能):是基于restful将数据序列化,基于restful api规范的不同请求方式 来实现响应的功能, 如使用get方法查看”文章",post方法实现"点赞"、"收藏"、"评论"等功能。使用option实现复杂请求 解决跨域问题。 第三个是购物车:提交请求获取当前课程信息,价格策略等信息点击加入购物车,写入redis实现加入购物车。 也可以点击收藏,获取课程id,添加到收藏表。 有两种支付方式:跳转到支付页面立即支付,用户进入购物车点击去结算, 从redis获取相应信息,点击去结算,获取商品价格策略, 获取当前用户的优惠卷,贝里,判断是否过期,判断优惠卷类型,进行相应的折扣, 点击立即支付,生成订单,判断状态是否支付成功,支付成功清空购物车相应信息, 也就redis中的相应商品信息,最后是基于关注公司微信公众号实现的微信消息推送。 然后是个人中心: 基于restful获取数据,将数据序列化。主要包括"我的账户","我的订单", "我的收藏","个人资料","账号安全"。 最后一个模块就是一些:”关于我们“,”联系我们“,”商务合作等“。 c. 工作经历 1. 公司 : 西安慧聪教育科技有限公司 - 公司业务:-----------教育教学软件的研发,多媒体教学软件的研发及技术咨询;针对中小学生的教育和作业辅导。 - 规模:我们的团队有14人左右,前端1个,后端4人,运维1人,UI1个,测试1个,还有产品经理1人,运营和销售各两人。 还有其他负责其他业务的,大概100多人吧。 - 地址:西安市雁塔区雁翔路99号博源科技广场C座513室老板:陈昌民 - 薪资待遇:工资多少钱?到手多少钱? --:8500,到手8000,没有交五险一金。 2:为什么出来面试? - 辞职主要是想来大城市发展,好多同学都在这边发展的都挺好的,我也想来试试。 d. 个人技能 一:熟悉使用 Python编程语言并了解PEP8规范 1:问题:PEP8规范6条? 答:PEP8规范说白了就是一种规范,可以遵守,也可以不遵守,遵守PEP8可以让代码的可读性更高。 代码编排:-------------缩进,4个空格(编辑器都此功能),每行最大长度79,换行使用反斜杠...... 字符串引用-------------python中双引号与单引号字符串是相同的,而且尽量避免在字符串中写反斜杠\'\' 文档编排---------------不要在一句import多个库, 空格的使用-------------避免不必要的空格,各种左右括号前不要加空格,逗号,冒号,分号前不要加空格, if/for/while语句中-----if/for/while语句中即使执行语句只有一句,也必须另起一行....... 注释:------------------块注释,在一段代码前增加的注释。行注释,在一句代码后加注释。 总体原则,错误的注释不如没有注释。所以当一段代码发生变化时,第一件事就是要修改注释! 命名规范:--------------尽量单独使用小写字母‘l’,大写字母‘O’等容易混淆的字母。 类的属性若与关键字名字冲突,后缀一下划线,尽量不要使用缩略等其他方式。 命名避免与python内置方法冲突,命名要有实际意义,不能随性所欲。 异常--------------------异常中不要使用裸露的except,except后跟具体的exceptions。异常中try的代码尽可能少。 2:问题:那年开始写Python?14年,自学;看视频+看书? 3: 问题:那些比较重要? 答:迭代器、生成器、装饰器。 迭代:就是一个一个的取值。 可迭代对象:-------内部实现__iter__方法。 字符串,列表,元祖,集合,字典都是可迭代的。 迭代器:-----------有__next__方法的就是迭代器,迭代器是往前一个一个的取值,但是不能生成值。 生成器:-----------具有yeild方法,生成器是基于迭代器的,不仅取值还能生成值。被执行后返回的是一个生成器。生成器的本质就是一个迭代器。 装饰器:-----------装饰器的本质:就是一个闭包函数,在不修改原函数及其调用方式的情况下对原函数功能进行扩展。 闭包函数:---------在一个外函数中定义了一个函数,内函数里运用了外函数的临时变量,将数据封装到函数中。 并且外函数的返回值是内函数的引用。这样就构成了一个闭包。 装饰器的固定格式: def timer(func): @functools.wrapper(func) def inner(*args,**kwargs): \'\'\'执行函数之前要做的\'\'\' re = func(*args,**kwargs) \'\'\'执行函数之后要做的\'\'\' return re return inner 什么情况下使用装饰器? 小范围的需要给类增加方法的就可以使用装饰器 中间件是比如验证 session,scrf验证用户是否登录基本上需要为 绝大部分视图使用的时候就可以使用中间件来完成 From django.views.decorators.csrf import csrf_exempt @csrf_exempt 不用中间件的情况下 使用这个直接使用装饰器 带参数的装饰器? 带参数的装饰器,就是给装饰器传参。语法糖也要带个括号,在语法糖的括号里传参。 用处:当我们加了很多装饰器的时候,现在又不想加装饰器了,想把装饰器去掉,但一一去掉很麻烦,那么我们可以利用带参数的装饰器 去装饰它,这就像一个开关一样,用的时候就调用了,不用的时候去掉。 def outer(flag): def warp(func): def inner(*args,**kwargs): return func(*args,**kwargs) return inner return warp @outer("hollo") def foo(a,b) return a+b foo(3,3) 哪里用过装饰器? 用户登录 flask的路由系统 哪里用过带参数的装饰器? flask的路由系统,flask中的特殊装饰器: defor_request after_request berfor_first_request template_global template_filter errorhandler 开放封闭原则? 对扩展开放,对修改封闭。 可迭代和迭代器的相同点:都可以用for循环。 ......................: 迭代器内部多实现了一个__next__方法。 判断迭代器和可迭代的方法? 答:1:判断内部是不是实现了__next__方法 2:lterable:判断是不是可迭代对象 lterable:判断是不是迭代器 isinstance("123",Iterable) 判断迭代器 hasattr(i, \'__iter__\') hasattr(i, \'__next__\') 迭代器的好处1.节约内存 2.不依赖索引取值 3.实现惰性计算(什么时候需要,在取值出来计算) 生成器的好处 2.实现了延迟计算,省内存啊 3.生成器本质和其他的数据类型一样,都是实现了迭代器协议,只不过生成器附加了一个延迟计算省内存的好处,其余的可迭代对象可没有这点好处! range函数-----------------------------------是一个可迭代的,但不是迭代器。 map函数-------------------------------------map方法自带迭代器 哪里用过? ............. 4:问题:函数?作用域、闭包、参数; 答:函数:组织好的,可重复使用的,用来实现单一或相关联功能的代码段。def 开头,函数名加括号,要传入的参数放在括号中,最后冒号。 避免代码重复使用,提高代码的可读性。 函数名可以做参数,也可以做函数的返回值,函数名就是函数的内存地址。 函数的调用:函数内部可以调用与该函数处相同命名空间的函数。 函数的嵌套:函数内部可以定义函数,定义的函数还可以定义其他函数。 作用域:依赖导致原则 谈到作用域,就得说说命名空间,它俩是分不开的: 全局命名空间:创建存储"变量名与值的关系"的空间。 局部命名空间:在函数的运行中开辟临时的空间。 内置命名空间:内置命名空间中存放了python解释器为我们提供的名字:input,print,......我们熟悉的方法。 加载顺序:首先是内置命名空间(程序运行前执行)---->全局命名空间(从上到下顺序加载)---->局部(调用时加载) ---------全局的不能使用局部的,局部的可以使用全局的。 作用域:就是作用的范围。 主要分两种: 全局作用域:全局命名空间与内置命名空间都属于全局范围,在整个文件的任意位置都能被引用,全局有效。 局部作用域:局部命名空间,只能在局部范围有效。 如果站在全局看:如果全局有,用全局的,如果全局没有,用内置的。 为什么要有作用域? 答:为了函数内的变量不会影响到全局。 globals:查看全局作用域的名字。 locals:查看局部作用域的名字。 nonlocal让内部函数中的变量在上一层函数中生效,外部必须有 作用链域:更大范围的作用域嵌套小范围的作用域 闭包: 闭包:"闭"指的是内部的函数,"包"包含了对外部函数作用域中变量的引用。 判断闭包的方法:__closure__,返回cell是闭包函数,返回None不是闭包函数。 闭包就是 以for 循环为例就是将不同的参数分别撞到不同的包里 session的query方法就是通过循环的方式将不同的方法撞到了scopy-sesison中 以前他是没有的 参数: 函数的参数有: 1:形参:定义函数时定义的参数 2:实参:函数调用的时候传进来的参数。 可以传递多个参数,多个参数间用逗号隔开。 站在传参角度上,调用函数传参有两种方式: 1:按照位置传参 2:按照关键字传参。 位置传参必须在关键字传参前面,对于一个参数只能赋值一次。 还有默认参数:将变化比较小的参数设置为默认参数,参数可以不传,不传使用默认值,传会覆盖默认值。 默认参数是一个可变数据类型。 动态参数: args:按位置传参,多余的参数都由args统一接收,保存成一个元祖。 kwargs:按关键字传参,接收多个关键字参数,由kwargs接收,保存成一个字典的形式。 5:问题:列表生成式、生成器表达式? 答:列表生成式:就是用一对中括号将生成列表的语句放入中括号内生成一个列表。 ---------l = [\'egg%s\'%i for i in range(100)] 生成器表达式:生成器表达式通过结合列表解析和生成器来一次性生成数据。 就是将列表推导式的中括号换成小括号。生成器 = (返回结构 执行对象 if判断条件) --------chicken=(\'鸡蛋%s\' %i for i in range(5)) --------------------------------------------------------------------------------------------- 生成器表达式----较----列表生成式比较剩内存,因为是惰性计算的。 6:问题:一行代码实现 9*9乘法表? 答:print ("\n".join("\t".join(["%s*%s=%s" %(x,y,x*y) for y in range(1, x+1)]) for x in range(1, 10))) 7:问题:面向对象? - 谈谈你认识面向对象? - 万物皆对象 - 人们都喜欢说它的三大特性,那我们就从封装,继承,多态开始。 1:先说下类和对象? 类:具有相同属性和方法的一类事物 类内置的特殊属性 类名.__name__类的名字(字符串) 类名.__doc__类的文档字符串 类名.__base__类的第一个父类 类名.__bases__类所有父类构成的元组 类名.__dict__类的字典属性 类名.__module__类定义所在的模块 类名.__class__实例对应的类(仅仅在新式类中) 对象:也就是实例化 2:啥叫抽象? 抽象:不存在的,抽取类似的或比较像的。是一个过程(抽象到具体),是从小到大的过程。 3:如果要在子类中调用父类的方法:----------super().类名() c是本类super(C, self).meth(arg) 4:接口类:------------在python中,默认是没有接口类的,接口类不能被实例化(如果实例化会报错),接口类中的方法不能被实现,本质就是用来做规范的,我们一般使用继承加抛异常 5:抽象类: ------------在python中,默认是有的,父类的方法,子类必须实现,抽象类的方法也可以被实现。 6:抽象类和接口类的区别:-------------接口类不能实现方法,抽象类可以实现方法里面的内容。 7:抽象类和接口类的相同点:-----------都是用来做约束的,不能被实例化。 8:抽象类和接口类的使用: 当几个子类的父类有相同的功能需要被实现的时候用抽象类。 当几个子类有相同的功能,但实现各不相同的时候就用接口类。 9:python中的抽象类和接口类在Java里面的区别? 答:接口类支持多继承 抽象类只支持单继承 5:封装? 答:隐藏对象的属性和实现细节,仅对外提供公共访问方式。 将同一类的方法和属性封装到类中, 将数据封装到对象中。 好处:将变化隔离,便于使用,提高复用性,提高安全性。 封装原则: 1. 将不需要对外提供的内容都隐藏起来; 2. 把属性都隐藏,提供公共方法对其访问。 6: 继承? 答:类与类之间的关系,子类继承父类的方法和属性,解决代码重用问题。 派生:子类在父类的方法和属性的基础上产生自己的方法和属性。 查看继承:类名.__bases__ 多继承:在继承抽象类的过程中,我们应该尽量避免多继承。 在继承接口类的时候,可以多继承接口类。 钻石继承: 新式类:广度优先。 新式类:深度优先。 组合:将一个类的对象当做另一个类的属性,是什么有什么的关系。 7:多态? 答:多态指的是一类事物有多种形态。python里面处处都是多态。 只制定了方法 可以传任何形式的参数 class Wechat(object): def send(): ... class Email(object): def send(): ... def func(arg): arg.send() // obj = Wechat() obj = Email() func(obj) 8:经典类和新式类: 1.只有在Python2中才分新式类与经典类,Python3中统一都是新式类 2.在Python3中,无论是否继承object,都默认继承object,即Python3中所有类均为新式类。 区别: 在多继承中,新式类采用广度优先搜索,而旧式是采用深度优先搜索。 新式类更符合OOP编程思想,统一了python的类型机制。 注意:如果没有指定基类,Python的类会默认继承object类, object是所有Python类的基类,它提供了一些常见方法(如__str__)的实现。 -------------------------------------------------------------------------------------------------- 9:广度优先/深度优先 答:广度优先: 广度优先的算法的实现是通过 分层次的进行遍历 先遍历第一层,然后第二层,第三层 (队列实现) 深度优先:将一个子节点的所有内容全部遍历完毕之后再去遍历其他节点实现的 (递归实现) 10:递归? 答:998 sys设置大小 11:鸭子模型? 答:如果想编写现有对象的自定义版本,可以继承该对象,也可以创建一个外观与行为像,但与他无任何关系的全新对象。 例如:序列类型有多种形态:字符串,列表,元祖,但他们之间没有直接的继承关系。 12:私有变量和私有方法? 私有变量: 在python中用双下划线开头的方式将属性隐藏起来(设置成私有的) 私有方法: 在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的 子类不能继承父类的私有方法 13:自省也称作反射, 对象可以获取自身信息。也就是传一个对象给你,你可以查出它有什么能力,这是一项强大的特性。 还有通过字符串的形式取执行类或则对象的方法 如果Python不支持某种形式的自省功能,dir和type内建函数,将很难正常工作。还有那些特殊属性,像__dict__,__name__及__doc__ 答:在调用一个函数的过程中,直接或间接地调用了函数本身这个就叫递归 dir 查看对象或则类的所有双下属性以及方法 ----------------------------------------------------------------------------------------------------- 哦,还有一些; isinstance(obj,cls)-----------------------检查obj是否是类cls的对象 issubclass(sub,super)---------------------检查super类的派生类 __call__----------------------------------对象后面加括号,触发执行,构造方法的执行是由创建对象触发的。即:对象 = 类名() __calll__方法的执行是由对象后加括号触发的。即:对象() 或 类()() - 单例 - 用类做装饰器 - Flask源码 __new__----------------------------------- - 单例 - 自定义Form验证 - wtforms源码 - django rest-framework中 序列化源码 __init__---------------------------------- __del__-----------------------------------析构方法,当对象在内存中被释放时,自动触发执行。此方法无需定义。 __iter__---------------------------------- - 对象要被for循环,stark组件组合搜索 反射(自省):以字符串的形式操作类中对象的属性。 四个可以实现自省的函数 __getattr__------------------------------- - Flask源码,自定义的“本地线程” Local - rest-framework request中取值 __getitem__------------------------------- - Session源码 setattr ------------------------------- delattr ------------------------------- __next__---------------------------------- from collections import Iterator class Item(Iterator): def __init__(self,arg): self.arg = arg def __next__(self): self.arg -= 1 if self.arg >1: return self.arg else: raise StopIteration() # 可迭代对象 class Foo(object): def __iter__(self): # 迭代器 return Item(10) obj = Foo() for item in obj: print(item) __dict__------------------------- - 自定义Form __add__ ------------------------- - data = obj1 + obj2 # obj1.__add__(obj2) __str__和__repr__--------------改变对象的字符串显示 __for__mat---------------------自定制格式字符串 item系列: __getitem__--------------- __setitem__--------------- __deltiem__--------------- metaclass------------------------可以自由的,动态的修改,增加,删除,类或者实例中的方法或者属性。 当引入第三方库的时候,如果该库某些需要path的时候可以用metaclass. 批量的对某些方面使用decorator,而不需要都在上面加入@decorator_func metaclass的实例化结果是类,而class实例化的结果instance.我们可以这样理解: metaclass是类似创建类的模板,所有的类都是通过它来create的,(调用了__new__). 一般情况下,如果你要用类来实现metaclass的话,该类需要继承type,而且通常会重写type的__new__方法来控制创建过程。 在metaclass里面定义的方法会称为类的方法,可以直接通过类名来调用。 - 流程 metaclass的原理其实是这样的:当定义好类之后,创建类的时候其实是调用了type的__new__方法为这个类分配内存空间,创建 好了之后再调用type的__init__方法初始化(做一些赋值等)。所以metaclass的所有magic其实就在于这个__new__方法里面了。 说说这个方法:__new__(cls, name, bases, attrs) cls: 将要创建的类,类似与self,但是self指向的是instance,而这里cls指向的是class name: 类的名字,也就是我们通常用类名.__name__获取的。 bases: 基类 attrs: 属性的dict。dict的内容可以是变量(类属性),也可以是函数(类方法)。 所以在创建类的过程,我们可以在这个函数里面修改name,bases,attrs的值来自由的达到我们的功能。这里常用的配合方法是getattr和setattr(just an advice) - 在wtforms源码中看到过? ----wtforms源码: 8:GIL全局解释器锁? 答:GIL全局解释器锁,它是基于Cpython实现的。由于全局解释器锁的存在,所以在同一时间内,python解释器只能运行一个线程的代码。 线程运行在进程里,共享内存地址空间,有共享就有竞争。所以那个线程抢到了GIL锁,谁就先执行。如果遇到IO阻塞,就会把锁释放掉。 下一个线程抢到,继续执行。 9:GC垃圾回收机制? 答:Python的GC模块主要运用了“引用计数”来跟踪和回收垃圾。 在引用计数的基础上,还可以通过“标记-清除”解决容器对象可能产生的循环引用的问题。 通过“分代回收”以空间换取时间来进一步提高垃圾回收的效率。 10:题:python2和Python3的区别? 答:1. print不再是语句,而是函数,比如原来是 print \'abc\' 现在是 print(\'abc\') 2. 在Python 3中,没有旧式类,只有新式类,也就是说不用再像这样 class Foobar(object): 3.python2中有两种字符串类型:Unicode字符串和非Unicode字符串。Python3中只有一种类型:Unicode字符串。 4.Python2支持<>作为!=的同义词, python3只支持!=, 不再支持<> 5.在python2里,许多字典类方法的返回值是列表。最常用方法有keys, items和values。python3,所有以上方法的返回值改为动态试图。 几乎所有的python2程序都需要一些修改才能正常的运行在python3的环境下。-----------2转3--------2to3脚本 11:python的内置方法有那些? __init__:构造方法,创建对象时默认执行__init__方法。 __new__:创建类实例的时候调用此方法。 __call__:对象加括号执行__call__方法。 __del__:构造方法。 __setitem__:给对象创建属性的时候执行。 __getattr__:获取对象属性的执行。 __str__:打印的是一个对象,如果有__str__,就去执行__str__里打印的东西。 __repr__:改变对象的字符串显示 __add__:两个对象相加 __dict__:查看类成员 __len__:查看长度的时候执行 __eq__:两个对象相等的时候执行。 12:repr和str的区别? 答:它俩的不同之处在于,在打印一个对象的时候,即执行了__str__,也执行了__repr__. 会首先打印__str__的返回值,如果没有__str__,就会执行__repr__. 所以在字符串格式化的时候,还是在打印的时候,__repr__都可以当__str__的替补。反之不行 当字符串格式化的时候%s和%r分别执行了__str__和__repr__. 13:cookie和session? 答:由于Http协议是无状态协议,无法保持状态,但我们又有保持状态的需求。就有了cookie. 有服务端产生内容,客户端收到响应保存在浏览器。当客户端再次请求的时候会自动带上cookie. 这样服务端可以通过cookie的内容来判断状态了。但由于保存在浏览器和很容易被查看和窃取,不是很安全。 由于cookie虽然解决了一部分保存状态的需求,但存在与客户端浏览器,不安全,而且字节有限。就有了session和cookie配合使用。 sesion保存在客户端有较高的安全性。当客户端访问服务端,服务端根据需求设置session。 将会话信息保存在服务器端,同时将标志session的session_id发送到客户端,客户端收到sessio_id保存在内存中, 以后发送请求都会带上这个session_id,通过sessio_id找到session,就能取得客户端的数据状态。 session本身存在哪里?!!!! 二:熟练使用Django、Tornado、Flask等Web框架 问题:简单说一下Django、Tornado、Flask的关系?(bottle/webpy)?谈谈你个人对框架的认识: 答: 公司项目都是用Django实现,写过一点Flask(问卷/会议室),自学Tornado,我了解到它的(异步非阻塞)了解比较牛逼。 Django:大而全,包含了很多的web开发组件,比如ORM,Form,middlewear ,session ,Admin ,信号,django-redis缓存等 Flask :小而精,可扩展性非常强,比如:它是没有ORM的,借助的SQLAlchemy,WTForms ,flask中的session很弱, 借助的是Flask-Session 组件,还可以借助Falsk-script生成Python manage.py runserver 的命令, Flask-Migrate 数据库迁移等组件。 Tornado:我觉得tornado和django的语法类似,django和flask都是同步阻塞框架, 而tornado的异步非阻塞很厉害,比django,flask的性能高。 一:Django,大而全重武器。几乎包含所有Web开发组件,如:ORM、Session、Form、ModelForm、Admin、中间件、缓存等 分页、信号、缓存等... 1:ORM:-----------------------对象关系映射,表名对应类名,字段对应字段,表记录对应类实例化对象,自动生成sql语句。 2: COOKIE---------------------由于http协议无法保持状态,而我们却有需要保持状态,因此就产生了cookie. cookie工作原理:由服务器产生内容,浏览器收到请求保存在本地, 当浏览器再次访问的时,浏览器会自动带上cookie, 这样服务器就能通过cookie的内容来判断了。但安全性并不高。 在客户端很容易被查看或破解用户会话信息 3:SESSION--------------------cookie虽然在一定程度上解决了保持状态的需求, 但由于cookie本身字节有限(4096),以及cookie本身保存在客户端, 很容易被拦截或窃取,就产生了session和cookie配合使用, session保存在服务端,有较高的安全性。 工作原理:当客户端访问服务器时,服务器根据需求设置session, 将会话信息保存在服务器上,同时将标示session的session_id传递 给客户端浏览器,浏览器将这个session_id保存在内存中 (还有其他的存储方式,例如写在url中),我们称之为无过期时间的cookie。 浏览器关闭后,这个cookie就清掉了,它不会存在用户的cookie临时文件。 以后浏览器每次请求都会加上session_id, 服务器根据这个session_id,就能取得客户端的数据状态。 3: Form-----------------------用于Form验证:用户请求数据验证,自动生成错误信息 打包用户提交正确信息,错误:保留上次输入内容, 定制页面上显示的HTML标签 1:Form组件可以做几件事情? 用户请求数据验证 自动生成错误信息 打包用户提交的正确信息 保留上次输入内容 自动创建input标签并且可以设置样式 2: Form组件的使用 写一个类 字段(参数) - require - max_length - min_length - error_message - validate=[] #自定制的正则表达式 方法 - clean_字段名称 使用: - GET请求实例化直接render回去 POST 实例化(request.POST) is_valid() clean_data errors 注意:ChoiceField数据源时时更新 4: MdelFORM-------------------ModelForm是将model和form二者合一, 5: Admin---------------------Django能够根据定义的模型类自动地生成管理模块。 源码流程:运行程序,找打每个app中的admin.py并加载。 创建admin.site中的对象,将注册类添加到_registry中。 _registry传进来的是一个键值对:k是modelv是modeladmin对象 admin.site是一个对象,用单例模式创建,其中封装了_registry. 再次调用admin.site对象的urls动态生成url,每个url对应一个视图 函数。 6: 信号-----------------------Django中提供了“信号调度”,用于在框架执行操作时解耦。 就是一些动作发生的时候,信号允许特定的发送者去提醒一些接受者。 Django内置的信号有:pre_init,post_init,post_save,post_delete, 也可以自定义信号。 pre_init---Django的modal执行其构造方法前,自动触发 post_init---django的modal执行其构造方法后,自动触发 pre_save---django的modal对象保存前,自动触发 post_save---django的modal对象保存后,自动触发 pre_delete---django的modal对象删除前,自动触发 post_delete---django的modal对象删除后,自动触发 request_started---请求到来前,自动触发||sdadande request_finished---请求结束后,自动触发||finshelld 7:缓存-----------------------由于Django是动态网站,所有每次请求均会去数据库进行相应的操作, 当程序访问量大时,耗时必然会更加明显,最简单解决方式是使用: 缓存,缓存将某个views的返回值保存至内存或者memcache中, 再有人来访问时,则不再去执行view中的操作, 而是直接从内存或者Redis中之前缓存的内容拿到,并返回。 Django中提供了6种缓存方式: 开发调试 内存 文件 数据库 Memcache缓存(python-memcached模块) Memcache缓存(pylibmc模块) 8:中间件---------------------是介于request与response处理之间的一道处理过程, 相对比较轻量级,并且在全局上改变django的输入与输出。 其实就是一个类,在请求到来之前和响应结束之后, django会根据自己的规则在合适的时机执行中间件的相应方法。 因为改变的是全局,所以需要谨慎实用,用不好会影响到性能 主要有五个方法:1:process_request 2: process_view : 3: process_template_response 4: process_exception 5:process_request 执行流程? 执行完所有的request方法,到达视图函数,执行中间件的其他方法, 经过所有的response方法,返回给客户端。 注意:如果在其中一个中间件里requeat方法里返回了值, 就会执行当前中间件的responses方法,返回给用户, 然后报错,不会再执行下一个中间件。 哪里用过? - 用户认证 - 权限 - CORS - csrf------process_view - session - 缓存 9:分页-----------------------Django提供了一个新的类来帮助你管理分页数据, 这个类存放在django/core/paginator.py.它可以接收列表、 元组或其它可迭代的对象。 10:缓存 Django的缓存机制? Django默认有缓存机制,它能为我们做什么事情? 6中缓存方式 1、 内存缓存 2、 文件缓存 3、 开发调试缓存 4、 数据库缓存 5、 Memcache缓存 经常使用的有文件缓存和mecache缓存 二:Flask,短小精悍的框架。可扩展性非常强,如:、Flask-session,DBUtils,信号blinker,wtform组件,SQLAlchemy组件, Flask Script组件 三:Tornado, 异步非阻塞方面比较厉害。 问题:说一下你了解到的Tornado异步非阻塞? 答: 请求过来,但有个条件是"服务端处理客户端请求时是IO请求才能提高并发"。 全部接收,向别的地方发送IO请求,减少了自己服务器的压力, 相当于找了个代理让别人去干了,自己只接受,让别的去处理。 Tornado异步非阻塞,Django和flask都是同步的,也就是一个一个的排队处理。 异步:成功之后做一个回调函数 非阻塞:非阻塞主要体现在不等待。socket.seblocking(False) 1:tronado有两种模式? 同步方式 :直接response就行 异步方式::加上装饰器,yield一个future对象。执行成功之后走回调函数,finash关闭连接 2:tronado异步非阻塞的本质,原理: 内部是基于IO多路复用实现的。 问题:django、flask、trnado之间的比较 1:请求参数的接收 Django:request.GET.get()/request.POST.get() Flask:request.args.get()/request.form.get() Tronado:self.get_querystring.get()/self.get_body_argument.get() 2:请求方式的区别 Django:request.method ==”Get” Flask:@app.route(‘/’,mehod=[“get”,”post”]) Tornado:重写父类的get/post方法 3:静态文件和网页模板的处理 Django:templates文件,static保存静态资源 Flask:默认templates保存,static保存静态资源 Tornado:通过配置文件中的template_path配置或者static_path配置 4:模板语法的操作 Django:默认使用自己的模板语法 Flask:使用jinjia2模板 Tornado:使用第三发的jinjia2模板语法 问题:Django (1)生命周期 请求进来先走wsgi,然后走中间件,再到路由,视图,ORM,返回数据进行模板渲染。 1:wsgi是干什么的? 答:web协议,网管接口 2:Django中使用的是? 答:Django中实现wsgi的是:wsgiref和uwsgi,wsgiref是开发测试用的,uwsgi是线上用的。 Flask中实现wsgi的是:werkzurg Tornado中实现wsgi的是:tornado和gevent (2)中间件: Django的中间件就是一个类,是介于请求和响应之间的一道处理过程, 相对比较轻量级,并且在全局上改变Django的输入和输出。 1:中间件有几个方法? 答:5个。 1:process_request 2: process_view : 3: process_template_response 4: process_exception 5:process_request 2: 中间件的执行流程? 当用户请求的时候会依次经过所有的中间件,这个时候请求的是所有process_request, 当最后一个process_request到达路由关系映射之后,返回到中间件的process_view, 到达视图函数中,视图函数处理后,如果有模板渲染,执行process_template_response, 如果在以上执行的过程中出错了,执行process_exception, 最后通过process_response返回给请求者。 3:用它做过什么? 1:登录认证 2:权限 3:CORS跨域(由于好多的请求都可能存在跨域问题,所以直接写一个中间件,就不用每次都在响应的时候设置响应头了) 4:csrf (执行process_view,在这view里面去拿csrf_token,再看有没有加特别的装饰器, 特殊的装饰器,如果那个函数不需要认证,则可以取设置一个特殊的装饰器) 5:session 缓存(请求进来,先去缓存获取,再往后执行) 5: 中间件的差异? (3)路由 - namespace-----------------反向生成要加冒号。 1:反向解析? 答:找到别名是xxx的url,也可在视图函数中使用:reverse。 2:路由分发,---------------incoulde. 答: 项目里面有多个APP目录大家共有一个容易造成混淆,于是路由分发让每个APP的拥有了自己单独的url (4):视图 视图分为CBV和FBV响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片,是任何东西都可以 1:CBV 原理: 执行路由.as_view(),as_view又调用了view函数 ,view执行dispatch通过反射,执行get/post/delete等请求方法。 - 装饰器 from django.utils.decorators import method_decorator class AuthView(View): @method_decorator(func) def post(self, request, *args, **kwargs): pass @method_decorator(func,name=\'post\') class AuthView(View): def post(self, request, *args, **kwargs): pass csrf装饰器,必须加到dispatch上。 class AuthView(View): @method_decorator(func) def dispatch(self, request, *args, **kwargs): pass @method_decorator(func,name=\'dispatch\') class AuthView(View): def post(self, request, *args, **kwargs): pass 2:FBV 3:请求? 答;request里包含哪些数据 request.GET: ---------------------------------------------------GET请求的数据 {} request.POST:---------------------------------------------------POST请求的数据 {} request.method:-------------------------------------------------请求方式:GET 或 POST request.POST.getlist("hobby") -----------------------------------请求某个键下多个值时 request.path:/index.html/23--------------------------------------获取url路径,不包括参数 request.get_full_path():------------------------------------------R获取url路径,包括参数 Request.cookies Request.session Request.files Request.user 4:响应? render:----------------------------------------只是返回页面内容,但是未发送第二次请求 返回一个页面,模板语法:将变量如何巧妙的嵌入到HTML页面中 redirect(ruideruankt):-------------------------发送了第二次请求,url更新 HttpResponse------------------------------------自定义返回 (5):ORM ORM是对象关系映射,数据库中的表名对应类名,字段字段,表记录类实例化对象,自动生成sql语句。 1:谈谈你对QuerySet的理解,以及它有哪些方法? 答:Queryset可切片,可迭代,缓存机制、惰性查询(不用不求)Update, create , delete。 2:原生SQL和ORM区别? 答:原生SQL查询速度快,ORM使用方便。 3:原生SQL:SQL注入 4:手动创建表(肯定毙掉); class Book(models.Model): title = models.CharField(max_length=32) publish=models.ForeignKey("Publish")------------------------------创建一对多的外键字段 authorList=models.ManyToManyField("Author")-----------------------多对多的关系,自动创建关系表 class Publish(models.Model): name = models.CharField(max_length=32) addr = models.CharField(max_length=32) class Author(models.Model): name=models.CharField(max_length=32) age=models.IntegerField() class AuthorDetail(models.Model): tel=models.IntegerField() 添加数据的两种方式 方式一:实例化对象就是一条表记录 Frank_obj = models.Student(name ="海东",course="python",birth="2000-9-9",fenshu=80) Frank_obj.save() 方式二: models.Student.objects.create(name ="海燕",course="python",birth="1995-5-9",fenshu=88) 5:基本操作(毙掉): API操作: <1> all(): -------------------------查询所有结果--------------------------------------------QuerySet <2> filter(**kwargs): --------------它包含了与所给筛选条件相匹配的对象----------------------QuerySet <3> get(**kwargs): -----------------返回与所给筛选条件相匹配的对象,返回结果有且只有一个, 如果符合筛选条件的对象超过一个或者没有都会抛出错误------model对象 <5> exclude(**kwargs):--------------它包含了与所给筛选条件不匹配的对象 ---------------------QuerySet <4> values(*field):-----------------返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列 model的实例化对象,而是一个可迭代的字典序列-------------QuerySet <9> values_list(*field):------------它与values()非常相似,它返回的是一个元组序列, values返回的是一个字典序列 -----------------------------QuerySet <6> order_by(*field):---------------对查询结果排序------------------------------------------QuerySet <7> reverse():----------------------对查询结果反向排序 -------------------------------------QuerySet <8> distinct():---------------------从返回结果中剔除重复纪录 -------------------------------QuerySet <10> count():-----------------------返回数据库中匹配查询(QuerySet)的对象数量。--------------int <11> first():-----------------------返回第一条记录------------------------------------------model对象 <12> last(): -----------------------返回最后一条记录----------------------------------------model对象 <13> exists():----------------------如果QuerySet包含数据,就返回True,否则返回False---------True,Flase <14>only --------------------------- <15>defer--------------------------- all :打印的是一个QuerySet集合,一个列表里面放的对象 values:是一个字典的形式 values_list : 是一个元组的形式 基本操作:增,删,改 添加:create updata 删除: delete 改: save 6:查询 一对多查询(Book--Publish): 正向查询,按字段: 反向查询,按表名_set 一对一查询(Author-- -AuthorDetail): 正向查询,按字段: 反向查询:按表名: 多对多(Book----Author): 正向查询,按字段: 反向查询,按表名_set: 基于双下滑线查询: id__lt=10,-------------------大于10的值 id__gt=1 ,-------------------获取id小于 id__in=[11, 22, 33])---------获取id等于11、22、33的数据 id__in=[11, 22, 33])---------not in name__contains="ven")--------包括ven的 name__icontains="ven")-------icontains大小写不敏感 id__range=[1, 2])------------范围bettwen and startswith,istartswith, endswith, iendswith 聚合查询: aggregate(*args, **kwargs),只对一个组进行聚合 from django.db.models import Avg,Sum,Count,Max,Min,Sum 1、查询所有图书的平均价格,Avg,min,max,count print(models.Book.objects.all().aggregate(Avg("price"))) 分组查询: 分组查询 :annotate():为QuerySet中每一个对象都生成一个独立的汇总值。是对分组完之后的结果进行的聚合。 统计每一本以py开头的书籍的作者个数: models.Book.objects.filter(title__startswith="py").annotate(authNum = Count("authorlist__name")). values("authNum")) F查询与Q查询: Q:--------------用于复杂查询,你可以使用Q对象。 它产生一个新的Q 对象。 print(models.Book.objects.filter(Q(commentNum__gt=100)|Q(readNum__lt=200))) 查询年份等于2017年或者价格大于200的书 print(models.Book.objects.filter(Q(publishDdata__year=2017)|Q(price__gt=200))) 查询年份不是2017年或者价格大于200的书 print(models.Book.objects.filter(~Q(publishDdata__year=2017)&Q(price__gt=200))) F:--------------如果我们要对两个字段的值做比较,F() 来做这样的比较。 F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。 查看评论数大于阅读数的书 from django.db.models import F,Q print(models.Book.objects.filter(commentNum__gt=F("readNum"))) 修改操作也可以使用F函数,比如将id大于1的所有的书的价格涨价100元 print(models.Book.objects.filter(nid__gt=1).update(price=F("price")+100)) Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。 查询评论数大于收藏数2倍的书籍 models.Book.objects.filter(commnetNum__lt=F(\'keepNum\')*2) 7:以使用select_related 来对QuerySet进行优化 select_related主要针一对一和多对一关系进行优化。 select_related使用SQL的JOIN语句进行优化,通过减少SQL查询的次数来进行优化、提高性能。 可以通过可变长参数指定需要select_related的字段名。也可以通过使用双下划线“__”连接字段名来实现指定的递归查询。 没有指定的字段不会缓存,没有指定的深度不会缓存,如果要访问的话Django会再次进行SQL查询。 也可以通过depth参数指定递归的深度,Django会自动缓存指定深度内所有的字段。如果要访问指定深度外的字段, Django会再次进行SQL查询。 也接受无参数的调用,Django会尽可能深的递归查询所有的字段。但注意有Django递归的限制和性能的浪费。 Django >= 1.7,链式调用的select_related相当于使用可变长参数。Django < 1.7, 链式调用会导致前边的select_related失效,只保留最后一个。 prefetch_related: 对于多对多字段(ManyToManyField)和一对多字段,可以使用prefetch_related()来进行优化。 prefetch_related()和select_related()的设计目的很相似,都是为了减少SQL查询的数量, 但是实现的方式不一样。后者是通过JOIN语句,在SQL查询内解决问题。 但是对于多对多关系,使用SQL语句解决就显得有些不太明智,因为JOIN得到的表将会很长, 会导致SQL语句运行时间的增加和内存占用的增加。若有n个对象,每个对象的多对多字段对应Mi条 ,就会生成Σ(n)Mi 行的结果表。 prefetch_related()的解决方法是,分别查询每个表,然后用Python处理他们之间的关系。 因为select_related()总是在单次SQL查询中解决问题,而prefetch_related()会对每个相关表进行SQL查询, 因此select_related()的效率通常比后者高。 鉴于第一条,尽可能的用select_related()解决问题。 只有在select_related()不能解决问题的时候再去想prefetch_related()。 你可以在一个QuerySet中同时使用select_related()和prefetch_related(),从而减少SQL查询的次数。 只有prefetch_related()之前的select_related()是有效的,之后的将会被无视掉 (6):原生SQL(即将毙掉) - extra - raw - connection - models.User.objects.all().using(\'default\') (7):模板: 深度查询: 通过句点符号 .---------------句点符也可以用来引用对象的方法(无参数方法) 1:模板语法的目的是什么? 将变量如何巧妙的嵌入到HTML页面中 2:模板语法之变量 {{ }} 3:模板语法之标签{% tag %} 创建的有 for 标签 for….empty (给出的组是空的或者没有找到时,可有所操作) if 标签 {% csrf_token %}+ 4:模板语法之过滤器{{obj|filter_name:param }} 1:Defaule:如果一个变量是false或者为空,使用给定的默认值。否则,使用变量的值。 2:{{ value |length}} :返回值的长度。它对字符串和列表都起作用 3:{{ value|filesizeformat}} :将值格式化成我们人能看懂的文件尺寸 4:{{ vaue|date:’Y-m-d’}} : 转换时间格式 5:{{value |slice:”2:-1”}} :切片 6:{{ safe }} :确保你的数据是安全的,才被当做标签 7;slice------------------------如果 value="hello world",去掉前两个和后面一个 ------------------------{{ value|slice:"2:-1" }} 5:自定义标签和过滤器 a) 1、在settings中的INSTALLED_APPS配置当前app,不然django无法找到自定义的simple_tag. b) 2、在app中创建templatetags模块(模块名只能是templatetags) c) 3、在templatetags里面创建任意 .py 文件, from django import template from django.utils.safestring import mark_safe register = template.Library() #register的名字是固定的,不可改变 @register.filter 过滤器 def multi(x,y): return x*y @register.simple_tag 标签 def multitag(x,y,z): return x*y*z0000000000 @register.simple_tag 标签 def my_input(id,arg): result = "<input type=\'text\' id=\'%s\' class=\'%s\' />" %(id,arg,) return mark_safe(result) 6:手写模板语法循环字典 Views.py 假设 d = {‘a’:’ddd’,’b’:’xxx’} return render(request,’index.html’,{‘d’:d}) {% for item in d%} {{ item }} {{ v }} {% endfor % 7:查找顺序 8:simple_tag/inclusion_tag 9:模板继承(extend):-------Django模版引擎中最强大也是最复杂的部分就是模版继承了。 模版继承可以让您创建一个基本的“骨架”模版,它包含您站点中的全部元素,并且可以定义能够被子模版覆盖的 blocks 。 1:在模板中键一个盒子: {% block classIfo%} {% endblock%} 2:继承: {% extends "studentIfo.html"%} {%%} {% block classIfo %} <h>学生信息</h> <h3>{{ class_info }}</h3> {% endblock %} 问题:令你记忆深刻: - 参考djangoadmin源码:编辑、添加、搜索成功后,跳转会页面,保留原来搜索条件。 - 参考django admin源码:yield完成在后台无需处理完成后,再在页面上循环展示。 - 设计权限时,利用中间件? - csrf,中间件的process_request/后来装饰器 process_view 问题:Flask和Django的比较? Django大而全:无socket,依赖于第三方wsgiref模块实现了wsigi协议,中间件 ,视图(CBV,FBV) 模板、cookie、session 、ORM、Admin、Form、缓存、信号、序列化 Flask小而精:无socket,依赖于是第三方werkzeug模块实现wsgi协议,中间件(扩展), 视图(CBV,FBV) 无模板、用的是第三方JinJa2模板,无ORM 、cookie、session(比较弱) 问题:Flask路由参数 redirect_to:直接重定向,原url有参数时,跳转时也得传参,注意:不用加类型 @app.route(\'/test\',strict_slashes=True) #当为True时,url后面必须不加斜杠 # =============== 子域名访问============ @app.route("/static_index", subdomain="admin") 扩展Flask的路由系统,让他支持正则,这个类必须这样写,必须去继承BaseConverter @app.route(\'/index/<regex("\d+"):nid>\',endpoint=\'xx\') 问题:请求响应相关 request - request.form--------------------POST请求 - request.args--------------------GET请求 字典形式的 - request.querystring-------------GET请求,bytes形式的 - response - return render_tempalte() - return redirect() - return "" v = make_response(返回值)--------返回的值包在了这个函数里面 - session - 存在浏览器上,并且是加密的 - 依赖于:secret_key 问题:闪现的本质是什么? flash是基于session创建的,flash支持往里边放值, 只要你取一下就没有了,相当于pop了一下。不仅吧值取走,而且吧session里的东西去掉 问题:上下文 flask的上下文是一个类似于本地线程的东西, 本地线程:线程与线程之间的数据隔离。 a、类似于本地线程 创建Local类: { 线程或协程唯一标识: { \'stack\':[request],\'xxx\':[session,] }, 线程或协程唯一标识: { \'stack\':[] }, 线程或协程唯一标识: { \'stack\':[] }, 线程或协程唯一标识: { \'stack\':[] }, } b、上下文管理的本质 每一个线程都会创建一个上面那样的结构, 当请求进来之后,将请求相关数据添加到列表里面[request,],以后如果使用时,就去读取 列表中的数据,请求完成之后,将request从列表中移除 问题:flask中的扩展是什么? flask里面的扩展,相当于django中的中间件,是用装饰器实现的。 @app.before_request,@app.after_request 问题:flask蓝图是什么? 蓝图也就是对flask的目录结构进行分配(应用于小,中型的程序) 问题:flask是怎么操作数据库的? flask中是没有ORM的,如果在flask里面连接数据库有两种方式 一:pymysql 二:SQLAlchemy 是python 操作数据库的一个库。能够进行 orm 映射官方文档 sqlchemy SQLAlchemy“采用简单的Python语言,为高效和高性能的数据库访问设计,实现了完整的企业级持久模型”。SQLAlchemy的理念是,SQL数据库的量级和性能重要于对象集合;而对象集合的抽象又重要于表和行。 问题:Flask—session:--------------------flask-session是flask框架的session组件, 由于原来flask内置session使用签名cookie保存, 该组件则将支持session保存到多个地方,如:redis,memcached等。 源码流程: 1:引用from flask_session import Session 2:首先走session类,执行__init__方法,若app不为空,执行init_app方法。 3:init_app有执行了_get_interface,首先是一大堆的配置。 4:然后就是Ssession可以保存的地方,如if config[\'SESSION_TYPE\'] == \'redis\', 当然也可以是:memcached,mongodb,filesystem(),sqlalchmey()等。 5:实例化对象 RedisSessionInterface,赋值给session_interface。 6:实例化的这几个isSessionInterface,有open和save方法。-------------RedisSessionInterface, MemcachedSessionInterface, FileSystemSessionInterface, MongoDBSessionInterface, SqlAlchemySessionInterface. 7:每个实例化的对象都添加了四个参数:1:用于连接的配置 2:保存session中值的前缀. 3: 是否持久化存储。----------如果设置为True,则关闭浏览器session就失效。 4:是否对发送到浏览器上 session:cookie值进行加密。 8: 然后到RedisSessionInterface,执行执行open_session, 9: 去cookie中获取session做为key对应的值。-----------最开始是空的 10:如果是空的,自动生成id。 11:最后返回一个空字典。 12:执行save_session,拿到域名和路径 如果没有session,就没有值 如果有,获取日期,序列化字典 判断是否加密;执行_get_signer进行加密,通过hmac实现,在open_session进行解密。 不加密,session_id = 随机字符串,写到用户浏览器的cokkie中,session=随机字符串 13:执行完以后返回session_interface 问题:DBUtils:------------------------是flask的数据库连接池组件。 连接数据库----------------------python3pymysql,python2用mysqldb 问题:为什么使用数据库连接池? 答:解决多次连接,解决多线程,并发操作。 1:解决多次链接--------------------多次链接,数据库压力过大 解决方法:--链接写全局变量(全局变量要大写)。 2:解决多线程----------------------pymysql同一时间只能有一个线程。 解决方法:--加锁,每次只能有一个线程。这个线程执行完,下个线程才能进来。 3:不能并发操作。 解决方法:--引入第三方模块DBUtils,python默认没有。--------pip install dbutils 问题:基于DButils实现的数据库连接池有两种模式: 模式一:为每一个线程创建一个链接(是基于本地线程来实现的。thread.local), 每个线程独立使用自己的数据库链接,该线程关闭不是真正的关闭, 本线程再次调用时,还是使用的最开始创建的链接,直到线程终止,数据库链接才关闭 模式二:创建一个链接池,为所有线程提供连接,使用时来进行获取,使用完毕后在放回到连接池。 问题:本地线程? 答:线程和线程之间的数据隔离。-------------------创建数据库链接池的第一种模式。 问题:信号blinker-----------------------达到某个状态,发生一个状态的转化。在代码中预留了几个位置,也可看做是列表。 例如:手机信号,打电话触发信号,执行函数,响铃。 内部主要实现了八个信号: 1.请求app上下文push时执行 2. 请求到来前执行 3. 模板渲染前执行 4. 模板渲染后执行 5. 请求结束后执行 6. 请求执行完毕后自动执行(无论成功与否) 7. 请求上下文执行完毕后自动执行(无论成功与否) 8. 请求上下文pop时执行 2/3/4/5或不执行 请求执行出现异常时执行 如果使用闪现flash()-走flash,触发信号,调用flask在其中添加数据时,自动触发。独立存在 问题: 1:特殊的装饰器和信号有什么区别? 答:装饰器返回值有意义 2:信号用于做什么呢? 答:降低代码之间的耦合 源码流程: 1:定义函数,将函数添加到信号中,信号可以看做是个列表,循环列表,执行函数。 2:触发信号就是执行信号的set方法。 3:执行app,run()的__call__方法。 4:执行wsgi_app() 5:走push方法。-------触发第一个信号。------请求app上下文push时执行. 6:执行app_context,返回AppContext(self),self是当前app。 7:执行AppContext的push方法。执行send方法触发信号。(请求app上下文push时执行)----第一个执行的信号。 8:执行视图函数,触发full_dispatch_request方法,触发信号。(请求到来前执行)-----第二个执行的信号。 9:执行finalize_requestreturn,返回时执行finalize_request触发信号,(请求结束后执行)--第五个执行的信号。 10:在执行视图函数的时候执行render_template(),返回的时候执行_render方法。 11:执行before_render_template.send()触发信号(模板渲染前执行)----------------------第三个执行的信号。 12:template_rendered.send()----------触发信号(模板渲染后执行)----------------------第四个执行的信号。 13:执行handle_exception,got_request_exception.send(self, exception=e). 触发信号-----请求执行出现异常时执行-------若2/3/4/5出错或不执行。 14: 执行wsgi_app,finally(就是retuen也执行),request_tearing_down.send(self, exc=exc) 触发信号-----请求执行完毕后自动执行-------第六个执行的信号 15: 执行wsgi_app,finally(就是retuen也执行),a就是AppContex中的pop. 执行self.app.do_teardown_appcontext(exc) appcontext_tearing_down.send- 触发信号-----请求上下文执行完毕后自动执行-----------第七个执行的信号 16: 执行wsgi_app,finally(就是retuen也执行),app_ctx.pop(exc)----------就是AppContex中的pop appcontext_popped.send(self.app)-触发信号-----请求上下文pop时执行--------第八个执行的信号 17: 如果使用闪现flash()--------走flash 执行message_flashed.send()--触发信号,调用flask在其中添加数据时,自动触发。独立存在 问题:wtform组件-----------------------WTForms是一个支持多个web框架的form组件, 主要用于对用户请求数据进行验证。相当于Django的form. 问题:SQLAlchemy组件-------------------SQLAlchemy是一个基于Python实现的ORM框架。 该框架建立在 DB API之上,使用关系对象映射进行数据库操作, 简言之便是:将类和对象转换成SQL,然后使用数据API执行SQL并获取执行结果。 问题:Flask Script组件-----------------扩展提供向Flask插入外部脚本的功能,包括运行一个开发用的服务器, 一个定制的Python shell,设置数据库的脚本,cronjobs, 及其他运行在web应用之外的命令行任务;使得脚本和系统分开; Flask Script和Flask本身的工作方式类似,只需定义和添加从命令行中被Manager实例调用的命令; 有三种方法创建命令,即创建Command子类、使用@command修饰符、使用@option修饰符; 问题:ThreadLocal 问题:组件 问题:HTTP? 答:http是超文本传输协议,是一个基于TCP/IP通信协议来传送数据,属于应用层的面向对象协议。 http工作于客户端,浏览器作为HTTP客户端通过URL向http服务端(web服务器)发送请求,创建一个TCP连接,指定端口号,默认是80。 然后连接到服务器工作。在那个端口监听浏览器请求。一旦监听到客户端请求,分析请求类型后, 服务器会向客户端返回一个响应状态,比如"HTTP/1.0 404 OK",同时会返回特定的数据内容。-------如请求的资源,错误代码,其它状态信息等等。 请求包括:请求首行,请求头信息,空行,请求体。 响应包括:响应行,响应头,响应体,状态码等 问题:咱们公司主要用什么框架?-----------------------自己提问 - Django - Flask - Tornado - webpy(听说过,但是没有去研究过去,这个框架哪方面比较出众)---------提问 三:熟悉 Restful API 接口规范、django-rest-framework框架。 问题:什么是API? 答:API是接口,提供url. 接口有两个用途: 为别人提供服务,前后端分离。 问题:为什么使用前后端分离? 答:主要为了数据的解耦,提高开发效率。 如果更新了数据,web页面需要更改,而且网站的前端页面变动很大,后端也可能更改,会非常麻烦。 使用前端框架VUE.js,能快速搭建前端项目,不需要依赖后端。 后端只需做序列化,返回json数据即可,并不涉及更多操作。 问题:Restful API规范? (1):API与用户的通信协议,使用的是http协议 (2):域名尽量部署在专有域名之下,若API很简单,不会进一步扩展,可以考虑放在主域名下。 也可以放在子域名上(放在子域名上会存在跨域的问题,) (3):应将api的版本号放入url,还可以将版本号放入Http请求头信息中,但不如放在url中方便。 (4):在RESTful架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词, 而且所用的名词往往与数据库的表格名对应。一般来说, 数据库中的表都是同种记录的"集合",所以API中的名词也应该使用复数。 (5):如果记录数量很多,服务器不可能都将它们返回给用户。API应该提供参数,过滤返回结果。 (6)根据请求方式的不同,来区分不同的行为。post----get----put----patch----delete。 (7):服务器向用户返回的状态码和提示信息。 (8):返回json数据。 问题:常用的请求方式有那些? 答:Get :获取数据,Post:创建数据,Patch:部分修改 Put :全部修改,DELETE:删除数据,Options:如果有跨域,会先发一个option请求,先做预检,完了在执行自己发的请求 问题:常见的状态码有那些? 答:200:响应成功,301:永久重定向,302:重定向,403:Forbidden 404:找不到页面,500:服务端代码有误 问题:谈谈你对restful framework的认识? 答:我们一开始是没有用restful framework, 用django写一些装饰器或中间件也能实现。 但是它为我们事先提供了一部分接口, 常用的有:获取版本,认证,权限,分流 , 我们只需要按照restful的规范,只需要写几个类就好了,或者在配置文件里面配置一下就可以使用。 尤其是在用户访问频率限制的时候,restful里面都已经封装好了,我们直接调用它的方法就行了。 我们写的时候是基于CBV模式写的,这个和django中的CBV是一样的, 请求先进来会走dispatch方法,根据请求方法不同反射执行不同的方法。 我们写的类是继承APIView的View,去执行它的dispatch先找自己的,自己没有就找父类的。 restful framework请求进来到达路由.as_view(),在as_view里面return了一个view函数,请求进来先执行一个view函数。 as_views调用了dispath方法根据请求方式的不同,触发不同的方法。 initialize_request这个方法接收客户端的request请求,再重新封装成新的request。请求一些相关的东西放到request中 然后进行初始化,获取版本,认证调用request.user方法,检查权限,检查限制访问。 执行对应视图函数。 对返回结果再次进行加工。重要的功能在APIviews的dispath中实现。 问题:Django Rest Framework 的的请求生命周期: TTP请求 —> wsgi —> 中间件 —> 路由分发 —> 执行对应类的dispatch方法 —> 视图函数 —>返回 采用CBV的请求方式。 问题:restful framework中包括那些? 答:有版本,认证,权限,访问频率的限制,路由系统,视图,渲染器,序列化,分页,解析器。 问题:认证? 答:执行认证功能,确认进行后续操作的用户是被允许的,perform_authentication方法返回经过认证的用户对象, 传入的request是重新封装过的。 问题:版本? 功能的更新迭代。 可以放在url,也可以放在请求头,但不是很方便,也可以放在子域名上,但存在跨域的问题。 可以用三种方法实现:get传参方式,基于url正则传参 如果url中有版本信息,就获取发送到服务端的版本,返回一个元组. 问题:权限? 答:如果用户通过认证,检查用户是否有权限访问url中所传的路径,如用用户访问的是没有没有权限的路径,则会抛出异常。 问题:访问频率的限制? 答:作用是检查用户是否被限制了访问主机的次数。 问题:解析器 答:对请求数据进行解析,对请求体进行解析。 问题:如何进行序列化的? 答:有两种方法实现 一:从数据去取出数据,return response 二:从数据库求出数据,赋值给instance,设置是否是单对象,再进行赋值,然后return response(.data)返回。 ser = UsersSerializer(instance=user_list,many=True) return Response(ser.data) 问题:序列化? 答:解决QuerySet序列化问题。 序列化: 基本序列化:单对象,多对象。instance=user_list,many=True 跨表序列化: 复杂序列化: 基于Model实现序列化: 生成url: 全部生成url: 请求数据验证: 四:熟悉 html,css,JavaScript、jQuery、Ajax、BootStrap、VUE、跨域等前端技术。 问题:跨域? 答:浏览器的同源策略会导致跨域。 同源策略:相同的IP,相同的端口,相同协议视为同一个域,一个域内的脚本仅仅具有本域内的权限, 可以理解为本域脚本只能读写本域内资源,无法访问其他域的资源。这种安全机制称为同源策略。 AJAX同源策略主要用来防止CSRF攻击。 问题:跨域的解决方法:jsonp,cors? jsonp: 基本原理就是通过动态创建script标签,然后利用src属性进行跨域。 jsonp由两部分组成: 回调函数:会调函数是当响应到来的时候应该在网页中调用的函数。 数据:出入会调函数的json数据。 对于经常用jQuery的开发者来说,能注意到jQuery封装的$.ajax中有一个dataType属性, 如果将该属性设置成dataType:"jsonp",就能实现JSONP跨域了。需要了解的一点是, 虽然jQuery将JSONP封装在$.ajax中,但是其本质与$.ajax不一样。 cors: cors是一个w3c标准,全称"跨域资源共享"。它允许浏览器向跨源服务器,发出XMLHttpRequest请求,而克服了AJAX只能同源使用的限制。 cors需要浏览器和服务器同时支持。整个cors通信过程,都是浏览器自动完成的,浏览器一旦发现AJAX请求跨域,就会自动添加一些 附加头信息,有时还会多出一次附加的请求,但用户不会有感觉,所以实现cors通信的关键是服务器,一旦服务器实现了cors接口, 就可以跨源通信。 cors请求分成两类: 简单请求: 对于简单请求,浏览器直接发出CORS请求。----------就是在头信息中增加一个Origin字段。 流程: 1:Origin字段来说明,本次请求来自与那个源,服务器根据这个值决定是否同意这次请求。 2:如果指定的源,不在许可范围内,服务器会返回一个正常的htttp回应,这个回应的的头信息 没有包含Access-Control-Allow-Origin字段,就知道出错了,而抛出个异常。 3:抛出的异常被XMLHttpResponse的onerror回调函数捕获。 4:如果Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。 非简单请求:是对服务器有特殊要求的请求(put,deleter等等),会在正式通信之前,增加一次HTTP查询请求,进行预检。 浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单中,只有得到肯定回答,浏览器才会发出 正式的XMLHttpResponse请求,否则就会报错。 流程: 1:若HTTP请求的方式是put,并且发送一个自定义头信息。 浏览器发现这是一个非简单请求,就会自动发送一个"预检"请求,要求服务器确认。 预检请求的方法是:OPTIONS,表示请求是来询问的。 2:服务器收到预检请求以后,检查origin字段以后,确认允许跨源,就可以做出回应。 3:如果服务器否定了预检的请求,会返回一个HTTP回应,但没有任何cors相关的头信息字段。 4:这时浏览器就会认定,服务器不同意预检请求,因此触发一个错误,被XMLHttpRequest对象的onerror回调函数捕获。 CORS与JSOP的使用目的相同,但比JSONP更强大。 jsonp只支持get请求,cors支持所有类型的HTTP请求 问题:js作用域 答:在JS当中一个变量的作用域(scope)是程序中定义这个变量的区域。 变量分为两类:全局(global)的和局部的。 其中全局变量的作用域是全局性的,即在JavaScript代码中,它处处都有定义。 而在函数之内声明的变量,就只在函数体内部有定义。它们是局部变量,作用域是局部性的。函数的参数也是局部变量,它们只在函数体内部有定义。 问题:ajax ajax主要是实现异步发送和局部刷新。 同步交互:客户端发出一个请求后,需要等待服务器响应结束后,才能发出第二个请求; 异步交互:客户端发出一个请求后,无需等待服务器响应结束,就可以发出第二个请求。 json-------------------------序列化和反序列化(lodes,dumps)-----------------编程语言之间交互的一种语言。 json数据类型-----------------数字类型,字符串,布尔,数组,对象,null. 参数: url typr------------------请求方式 data------------------ajax请求携带的数据 processData-----------声明当前的data数据是否进行转码或预处理,默认为true. contentType-----------发送数据的格式,"urlencoded"(默认)-------------客户端给服务端发送数据的格式。 success:--------------function回调函数。 error:----------------function回调函数(错误的情况下)拿数据: 问题:@media 使用 @media 查询,你可以针对不同的媒体类型定义不同的样式。 @media 可以针对不同的屏幕尺寸设置不同的样式,特别是如果你需要设置设计响应式的页面,@media 是非常有用的。 当你重置浏览器大小的过程中,页面也会根据浏览器的宽度和高度重新渲染页面。 问题:VUE如何搭建脚手架? 做项目的时候用到了vue就学了vue的基本操作,会把脚手架搭起来, vue init webpack 项目名称 cd 项目名称 npm run dev #吧程序运行起来 使用的插件有:$axios发送ajax请求, vue-cookies操作cookie 常用指令有? 1:v-text:------------------------在元素当中插入值 2: v-html:------------------------在元素不中不仅可以插入文本,还可以插入标签 3: v-if:--------------------------根据表达式的真假值来动态插入和移除元素,若状态为flase,将标签注释。 4:v-show:------------------------根据表达式的真假值来隐藏和显示元素 5:v-for:-------------------------根据变量的值来循环渲染元素 6: v-on:--------------------------监听元素事件,并执行相应的操作<!-- 完整语法 --><a v-on:click="doSomething">...</a><!-- 缩写 --><a @click="doSomething">...</a> 7: v-bind:------------------------绑定元素的属性来执行相应的操作---完整语法<a v-bind:href="url">...</a> --缩写<a :href="url">...</a> 8: v-model:-----------------------实现了数据和视图的双向绑定:--把元素的值和数据相绑定,当输入内容时,数据同步发生变化,视图是---------数据的驱动当改变数据时,输入内容也会发生变化,数据是------视图的驱动 9: 取反---------------------------!,通过不停改变状态,实现闪烁 10:自定义指令: 问题:JQ常用选择器? 元素选择器 属性选择器 css选择器 问题:Ajax的特点: 异步交互:当请求发出后,浏览器还可以进行其他操作,无需等待服务器的响应! 局部刷新: 整个过程中页面没有刷新,只是刷新页面中的局部位置而已! 问题:csrf跨站请求的三种方式? 1:自己组装一组键值对发过去 2:自己设置头信息 3: $.ajaxSetup({ 问题:怎么上传文件? 有两种方式: 1、 Form表单上传文件。需要加一个enctype=’multipart/form-data’ 2、 Ajax上传文件 利用FormData,既可以处理二进制,也可以处理字典,列表等 ajax,使用FormData的最大优点就是我们可以异步上传一个二进制文件. 要是使用FormData一定要加上: 一定要加上: contentType:false processDate:false #不做预处理 问题:JS选择器? getElementById(通过ID获取元素),getElementsByName(通过name属性获取元素) getElementsByTagName(通过元素名称获取元素),getElementsByClassName(通过CSS类来获取元素) 问题:什么响应式布局? 根据浏览器页面大小控制布局 是怎么实现的呢? 是CSS中的@media实现的,页面宽度小于多少是生效,页面宽度大于多少时生效 问题:JavaScriptVar 和let 的区别 Var: Let:局部变量 问题:Vue.js的优点以及你对于这个框架的简单认识 Vue框架可以用一句话概括,它是构建用户界面的JavaScript框架, 用它可以快速的搭建网站,不用我们自己手写代码,提高了开发效率,自动生成css和js。 五:熟悉 MySQL以及存储过程、触发器、索引,事物。 问题:给需求设计表? 问题:获取数据? 问题:引擎 ? 答:Mysiam :支持全文索引,支持表锁 Ennodb :支持事务,支持行锁 问题:什么是行锁什么是表锁? 行锁:根据主键找到数据 Select * from tb2 where nid=3 for update; 表锁: 根据主键,未找到数据 Select * from tb2 where nid=30 for update; 根据主键,数据不明确 Select * from tb2 where nid<2 for update; Select * from tb2 where nid!=3 for update; 根据非主键查找 Select * from tb2 where name=’alex’ for update; 问题:mysql怎么提高性能? 当数据量越来越大的时候,查询速度会越来越慢, 这就可以用加锁引的方式提高查询速度 问题:索引分类? 1、 普通索引 index 2、主键索引 primary key 3、唯一索引 unique 4、联合索引 (遵循最左匹配原则:mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配) Primary key (id ,name) 联合主键索引 Unique(id , name) 联合唯一索引 Index(id , name)联合普通索引 5、覆盖索引:在索引表里面吧所有的值拿到 6、合并索引:两个单独的索引合起来一起用 问题:如何索引命中?: 创建索引,未命中 命中:相当于精确查询。。。in,like就不命中 排序条件为索引,则select字段必须也是索引字段,否则无法命中 索引的种类? Hash类型的索引:查询单条快,范围查询慢。 Btree类型的索引 :b+树,层次越多,数据量指数级增长(ennodb默认的就是Btree类型) 问题:在django的model操作,怎么提高性能? Select_related:一对多使用,查询主动做连表操作 Prefetch_related:多对多或者一对多的时候使用,不做连表,做多次查询 问题:数据库怎么创建索引? create index account_count on table_name 字段名 怎么创建组合索引? create index account_count on table_name (字段名, 字段名) 问题:mysql有那些数据类型? 数字 字符 日期 枚举和集合 问题:你对数据库优化的方案有哪些? 减少FK 允许数据冗余 不经常更改的数据放入内存 models中的choice Select * 问题:redis和memcache非关系型数据库和mysql关系型数据库的不同? Redis和memcache都是在内存中存放的 Redis是支持持久化的,memcache是不支持的, 相对于memchache慢一点。但是比mysql关系型数据库快的多。 因为mysql的数据是存在硬盘的,redis是存在内存中的 问题:Memchache和redis的相同点和不同点? Redis:Key:value键值对 Name:’yaa’, Name:[] #列表 Name:{“”:””} #散列表(哈希) Name:{} #集合 、/有序集合 Name :”” #字符串 Memchache:key:value Name:”” #只支持字符串 Redis是支持持久化的,memcache是不支持的,相对于memchache慢一点 Memchache机器出现故障数据没有了,redis支持持久化可以存一点数据,所以安全性不高 问题:redis和mysql的优缺点? Redis :优点是存在内存、速度快;缺点是不安全 Mysql:优点是安全,是存在硬盘的;缺点是查询速度慢。 问题:简单的说下你对于redis好memache的认识? 他们的数据类型不同,redis支持的数据类型更加丰富,memache只支持字符串格式的 Redis支持持久化,memache不支持,相对于memache慢一点 问题:索引? 1:为何要有索引? 答:加速查询。 2: 什么是索引? 答:索引在MySQL中也叫做“键”,是存储引擎用于快速找到记录的一种数据结构。相当与字典的音序表。 3: 索引的优缺点? 答:优点,加速查询。 缺点,加太多的索引,应用程序的性能可能会受到影响,占用磁盘。 解决方法:去掉没必要的索引,只给需要快速查询的字段加索引 4: 索引的原理? 答:索引的目的在于提高查询效率,与我们查阅图书所用的目录是一个道理:先定位到章, 然后定位到该章下的一个小节,然后找到页数。 5:索引的本质? 答:本质都是:通过不断地缩小想要获取数据的范围来筛选出最终想要的结果, 同时把随机的事件变成顺序的事件,也就是说,有了这种索引机制, 我们可以总是用同一种查找方式来锁定数据。 6:索引的数据结构? 答:b+树,---------------------------B+树是通过二叉查找树,再由平衡二叉树,B树演化而来。 7:如何命中索引? 1:明确字段 2:缩小范围 3:尽量选择区分度高的列作为索引 4:索引列不能参与计算,保持列“干净” 5:最左前缀匹配原则。 8:mysql的其他索引? 答:mysql中的primary key,unique,联合唯一也都是索引,这些索引除了加速查找以外,还有约束的功能。 9:mysql常用的索引? 普通索引:index------------------------------------------------加速查找 唯一索引: 主键索引:primary key----------------------------------加速查找+约束(不为空、不能重复) 唯一索引:unique---------------------------------------加速查找+约束(不为空) 联合索引: 联合住建索引:--------------------------------primary key(id,name) 联合唯一索引:--------------------------------unique(id,name) 联合普通索引:--------------------------------index(id,name) 10:索引两大类型? hash类型的索引:------------------------------------------查询单条快,范围查询慢 btree类型的索引:-----------------------------------------b+树,层数越多,数据量指数级增长(我们就用它,因为innodb默认支持它) 11:mysql如何做分页 答:mysql数据库做分页用limit关键字,它后面跟两个参数startIndex和pageSize 12:数据库怎么建立索引: 答:create index account_index on `table name `(`字段名`(length) 13:一张表多个字段,怎么创建组合索引 答:create index account_index on `table name `(`字段名`,\'字段名\') 14:如何应对数据的高并发,大量的数据计算 答: 1.创建索引 2.数据库读写分离,两个数据库,一个作为写,一个作为读 3. 外键去掉 问题:视图(view) 答:sql查询的虚拟表,非真实存在的,用户使用时只需要名称即可获取结果集,可以将结果当做表来使用,存到硬盘中。----------简化查询。 创建视图:creat view 视图 as select * form teacher. 使用视图:select 问题:存储过程(procedure)? 答:存储过程是一系列可执行的sql语句,存储过程存储在mysql中,通过调用它的名字在内部执行一些sql。 创建存储过程:delimiter caeate procedure 存储过程名() begin 一系列sql语句 end deliniter; 三个参数:in 仅用于传入参数用,out仅用于返回值用,inout既可以传入又可以当作返回 使用存储过程:call proc_name(),有参数的在括号中传入参数 问题:分页(limt)?mysql如何做分页,mysql数据库做分页用limit关键字,它后面跟两个参数startIndex和pageSize 答:查询数据量大时使用,就是根据sql语句,加上限制条件,显示从第几条数据,到第几条数据。 创建分页:sql查询+limt限制条件。limt, select * from sjdr_product_detail limit 0,5; 问题:触发器(trrgger)? 答:限定一个条件,一旦此条件为真,则触发一个动作,执行一条sql语句 创建触发器:delimiter create trigger 触发器名 after insert on cmd for each row begin 限定条件if new.success = \'no\' THEN sql语句 insert into errlog(err_cmd,err_time) values(new.cmd,sub_time) end deliniter; 使用触发器:触发器无法由用户直接调用 问题:事物(transaction)? 答:将某些操作多个sql作为原子操作,一旦某一个出现错误,就回滚到原始状态,从而保证数据的完整性,安全性。 创建事物:start transaction; sql的原子操作;commit; start transaction; update user set balance=900 where name=\'wsb\'; #买支付100元 update user set balance=1010 where name=\'egon\'; #中介拿走10元 update user set balance=1090 where name=\'ysb\'; #卖家拿到90元 commit; 问题:函数(function)? Mysql中提供了许多的内置函数: 如:聚合函数:AVG,min,max,sum等 字符串函数:concat字符串拼接,conv进制装换,lower变小写,upper变小写等 时间和日期函数:year,week,等 加密函数,控制流函数, 还可以自定制函数。 创建函数: delimiter create function 函数名(i1 int,i2 int) returns int begin 封装一段功能 end delimiter ; 执行函数? 答:@函数名 问题:pymysql的使用? 引用------------------------------import pymysql 连接------------------------------conn=pymysql.connect(host=\'localhost\',user=\'root\',password=\'123\',database=\'egon\',charset=\'utf8\') 拿到游标--------------------------cursor=conn.cursor() cursor=conn.cursor(cursor=pymysql.cursors.DictCursor) 执行sql语句-----------------------sql=\'select * from userinfo where name="%s" and password="%s"\' %(user,pwd) 执行sql返回查询结果---------------res=cursor.execute(sql) 关闭------------------------------cursor.close() conn.close() 问题:mysql优化查询? 答: 1、选取最适用的字段属性 2、使用连接(JOIN)来代替子查询(Sub-Queries) 3、使用联合(UNION)来代替手动创建的临时表 4、事务 5、锁定表 6、使用外键 7、使用索引 8、优化的查询语句。 问题:数据库怎么建立索引 create index account_index on `table name `(`字段名`(length) 问题:一张表多个字段,怎么创建组合索引 create index account_index on `table name `(`字段名`,\'字段名\') 问题:如何应对数据的高并发,大量的数据计算 1.创建索引 2.数据库读写分离,两个数据库,一个作为写,一个作为读 3. 外键去掉 问题:数据库内连表、左连表、右连表 内连接是根据某个条件连接两个表共有的数据 左连接是根据某个条件以及左边的表连接数据,右边的表没有数据的话则为null 右连接是根据某个条件以及右边的表连接数据,左边的表没有数据的话则为null 问题:视图和表的区别 视图是已经编译好的sql语句,是基于sql语句的结果集的可视化的表,而表不是 视图是窗口,表示内容 视图没有实际的物理记录,而表有视图是虚表,表是实表 视图的建立和删除只影响视图本身,不影响对应的表 问题:关系型数据库的特点 数据集中控制 数据独立性高 数据共享性好 数据冗余度小 数据结构化 问题:mysql数据库都有哪些索引 普通索引:普通索引仅有一个功能:加速查找 唯一索引:唯一索引两个功能:加速查找和唯一约束(可含null) 外键索引:外键索引两个功能:加速查找和唯一约束(不可为null) 联合索引:联合索引是将n个列组合成一个索引,应用场景:同时使用n列来进行查询 问题:sql优化: 选取最适用的字段属性 使用连接(JOIN)来代替子查询(Sub-Queries) select句中避免使用 \'*\' 减少访问数据库的次数 删除重复记录 用where子句替代having子句 减少对表的查询 explain 问题:char和vachar区别: char是固定长度,存储需要空间12个字节,处理速度比vachar快,费内存空间,当存储的值没有达到指定的范围时,会用空格替代 vachar是不固定长度,需要存储空间13个字节,节约存储空间,存储的是真实的值,会在存储的值前面加上1-2个字节,用来表示真实数据的大小 问题:Mechached与redis mechached:只支持字符串,不能持久化,数据仅存在内存中,宕机或重启数据将全部失效 不能进行分布式扩展,文件无法异步法。 优点:mechached进程运行之后,会预申请一块较大的内存空间,自己进行管理。 redis:支持服务器端的数据类型,redis与memcached相比来说,拥有更多的数据结构和并发支持更丰富的数据操作,可持久化。 五大类型数据:string、hash、list、set和有序集合,redis是单进程单线程的。 缺点:数据库的容量受到物理内存的限制。 问题:sql注入 sql注入是比较常见的攻击方式之一,针对编程员编程的疏忽,通过sql语句,实现账号无法登陆,甚至篡改数据库。 防止:凡涉及到执行sql中有变量时,切记不要用拼接字符串的方法 问题:游标是什么? 是对查询出来的结果集作为一个单元来有效的处理,游标可以定在该单元中的特定行, 从结果集的当前行检索一行或多行,可以对结果集当前行做修改, 一般不使用游标,但是需要逐条处理数据的时候,游标显得十分重要 问题:数据库支持多有标准的SQL数据类型,重要分为三类 数值类型(tinyint,int,bigint,浮点数,bit) 字符串类型(char和vachar,enum,text,set) 日期类型(date,datetime,timestamp) 枚举类型与集合类型 问题:mysql慢查询 慢查询对于跟踪有问题的查询很有用,可以分析出当前程序里哪些sql语句比较耗费资源 慢查询定义: 指mysql记录所有执行超过long_query_time参数设定的时间值的sql语句,慢查询日志就是记录这些sql的日志。 mysql在windows系统中的配置文件一般是my.ini找到mysqld log-slow-queries = F:\MySQL\log\mysqlslowquery.log 为慢查询日志存放的位置,一般要有可写权限 long_query_time = 2 2表示查询超过两秒才记录 问题:memcached命中率 命中:可以直接通过缓存获取到需要的数据 不命中:无法直接通过缓存获取到想要的数据,需要再次查询数据库或者执行其他的操作, 原因可能是由于缓存中根本不存在,或者缓存已经过期 缓存的命中率越高则表示使用缓存的收益越高,应额用的性能越好,抗病发能力越强 运行state命令可以查看memcached服务的状态信息,其中cmd—get表示总的get次数,get—hits表示命中次数, 命中率=get—hits / cmd—get 问题:Oracle和MySQL该如何选择,为什么? 他们都有各自的优点和缺点。考虑到时间因素,我倾向于MySQL 选择MySQL而不选Oracle的原因 MySQL开源 MySQL轻便快捷 MySQL对命令行和图形界面的支持都很好 MySQL支持通过Query Browser进行管理 问题:什么情况下适合建立索引? 1.为经常出现在关键字order by、group by、distinct后面的字段,建立索引 2.在union等集合操作的结果集字段上,建立索引,其建立索引的目的同上 3.为经常用作查询选择的字段,建立索引 4.在经常用作表连接的属性上,建立索引 问题:sql语句应该考虑哪些安全性? 1.防止sql注入,对特殊字符进行转义,过滤或者使用预编译的sql语句绑定变量 2.最小权限原则,特别是不要用root账户,为不同的类型的动作或者组建使用不同的账户 3.当sql运行出错时,不要把数据库返回的错误信息全部显示给用户,以防止泄漏服务器和数据库相关信息 问题:数据库事物有哪几种? 隔离性、持续性、一致性、原子性 问题:MySQ数据表在什么情况下容易损坏? 服务器突然断电导致数据文件损坏 强制关机,没有先关闭mysq服务器等 问题:drop、delete与truncate的区别 当表被truncate清空后,这个表和索引所占用的空间会恢复到初始大小, delete操作不会减少表或索引所占用的空间。 drop语句将表所占用的空间全释放掉。 一、delete,每次删除一行,可以进行回滚 1、delete是DML,执行delete操作时,每次从表中删除一行, 并且同时将该行的的删除操作记录在redo和undo表空间中以便进行回滚(rollback)和重做操作, 但要注意表空间要足够大,需要手动提交(commit)操作才能生效,可以通过rollback撤消操作。 2、delete可根据条件删除表中满足条件的数据,如果不指定where子句,那么删除表中所有记录。 3、delete语句不影响表所占用的extent,高水线(high watermark)保持原位置不变。 二、truncate,清空所有记录,不能回滚 1、truncate是DDL,会隐式提交,所以,不能回滚,不会触发触发器。 2、truncate会删除表中所有记录,并且将重新设置高水线和所有的索引,缺省情况下将空间释放到minextents个extent,除非使用reuse storage,。不会记录日志,所以执行速度很快, 但不能通过rollback撤消操作(如果一不小心把一个表truncate掉,也是可以恢复的,只是不能通过rollback来恢复)。 3、对于外键(foreignkey )约束引用的表,不能使用 truncate table,而应使用不带 where 子句的 delete 语句。 4、tr 三、drop,删除后释放内存。 1、drop是DDL,会隐式提交,所以,不能回滚,不会触发触发器。 2、drop语句删除表结构及所有数据,并将表所占用的空间全部释放。 3、drop语句将删除表的结构所依赖的约束,触发器,索引,依赖于该表的存储过程/函数将保留,但是变为invalid状态。 问题:数据库范式 1.第一范式:就是无重复的列 2.第二范式:就是非主属性非部分依赖于主关键字 3.第三范式:就是属性不依赖于其他非主属性(消除冗余) 问题:MySQL锁类型 根据锁的类型分:可以分为共享锁、排他锁、意向共享锁和意向排他锁 根据锁的粒度分:可以分为行锁、表锁 对于mysql而言,事务机制更多是靠底层的存储引擎来实现的,因此,mysql层面只有表锁, 而支持事物的innodb存储引起则实现了行锁(在行相应的索引记录上的锁) 说明:对于更新操作(读不上锁),只有走索引才可能上行锁 MVCC(多版本并发控制)并发控制机制下,任何操作都不会阻塞读取操作, 读取操作也不会阻塞任何操作,只因为读不上锁 共享锁:由读表操作加上的锁,加锁后其他用户只能获取该表或行的共享锁,不能获取排他锁, 也就是说只能读不能写 排他锁:由写表操作加上的锁,加锁后其他用户不能获取该表或该行的任何锁,典型mysql事物中的更新操作 意向共享锁(IS):事物打算给数据行加行共享锁,事物在给一个数据行加共享锁前必须先取得该表的IS锁 意向排他锁(IX):事物打算给数据行加行排他锁,事物在给一个数据行家排他锁前必须先取得该表的IX锁 问题:如何解决MYSQL数据库中文乱码问题? 1.在数据库安装的时候指定字符集 2.如果在安装完了以后可以更改配置文件 3.建立数据库时候:指定字符集类型 4.建表的时候也指定字符集 六:熟悉 Redis、Memcache和常用操作,RabbitMQ 消息队列的使用。 问题:说说你了解的redis。 答:redis可以做缓存也可以做消息队列,支持持久化。支持存放数据的格式比较多。 字符串,链表,集合,有序集合,哈希,这些数据数据类型都支持push----pop----add----remove, 以及取交集----并集----差集,及更丰富的操作,这些操作都是原子型的。本质就是将数据保存在内存中。 问题:使用Redis有哪些好处? 答: (1) 速度快,因为数据存在内存中, (2) 支持丰富数据类型,支持string,list,set,sorted set,hash (3) 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行 (4) 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除 问题:操作? 答: 连接方式,redis-py提供两个类Redis和StrictRedis用于实现Redis的命令,StrictRedis用于实现大部分官方的命令, 并使用官方的语法和命令。 连接池------------redis-py使用connection pool来管理对一个redis server的所有连接, 避免每次建立、释放连接的开销。默认,每个Redis实例都会维护一个自己的连接池。 可以直接建立一个连接池,然后作为参数Redis,这样就可以实现多个Redis实例共享一个连接池。 操作--------------String------Hash------List-----set------Sort Set.(字符串)-(哈希)-(链表)-(集合)-(有序集合) (1):String操作----------redis中的String在在内存中按照一个name对应一个value来存储。 设置值: set(name,value,ex=None,nx=Faals,xx=false)-------------设置值,默认:不存在则创建,存在则修改 获取值: get(name)---------------------------------------------获取值 修改值: setrange(name,offset,value)---------------修改字符串内容, 从指定字符串索引开始向后替换(新值太长时,向后添加) (2):Hash操作--------------------------------------一个name对应多个hash(key:value) 1:hset(name,key,value)--------------在name对应的hash中设置一个键值对(不存在--创建,存在--修改) 2:hget(name,key)--------------------在name对应的hash中获取根据key获取value (3):List操作----------------------------redis中的List在在内存中按照一个name对应一个List来存储 1:lpush(anme,values)----------------在name对应的List中添加元素,每个新的元素都添加到列表的最左边 2:llen(name)------------------------name对应的list元素的个数 (4):Set操作-----------------------------Set集合就是不允许重复的列表。 1:sadd(anme,value)------------------name对应的集合添加元素 2:smove(src,dst,value)--------------将某一个成员从一个集合中移动到另外一个集合 3:spop(name)------------------------从集合的右侧(尾部)移除一个成员,并将其返回。 (5):Sort Set----------------------------有序集合,在集合的基础上,为每个元素排序;元素的排序需要根据另外 一个值进行比较,所以,对于有序集合,每一个元素有两个值 值和分数,分数专门用来做排序。 (1):zadd(name,*args,**kwargs)------------------在name对应的集合中添加元素 (2):zcound(name,min,max)-----------------------获取name对应的有序集合中分数在[min,max]之间的个数 管道--------------redis-py默认在执行每次请求都会创建(连接池申请链接)和断开(归还连接池)一次连接操作, 如果想要在一次请求中指定多个命令,则可以使用pipline实现一次请求指定多个命令, 发布订阅 问题:redis相比memcached有哪些优势? (1) memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型 (2) redis的速度比memcached快很多 (3) redis可以持久化其数据 问题:Memcache与Redis的区别都有哪些? (1):存储方式: Memecache把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。 Redis有部份存在硬盘上,这样能保证数据的持久性。 (2):数据支持类型 Memcache对数据类型支持相对简单。 Redis有复杂的数据类型。 (3):value大小 redis最大可以达到1GB,而memcache只有1MB 问题:说下RabbitMQ? 答:说下RabbitMQ是一个在AMQ基础上完整的,可复用的消息系统。 1:应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,无需专用链接来链接他们。 2:消息传递指的是:程序之间通过在--消息--中发送数据进行通信,而不是直接调用彼此来通信,直接调用通常是用于--:诸如诸如远程过程调用的技术。 3:排队指的是:应用程序通过队列来通信。 4:队列的使用除去了接收和发送同时执行放入要求。 问题:Memchache和redis的相同点和不同点? 答:Memchache和redis都是通过一个键对应一个值的存储方式 Redis:Key:value键值对 Redis是支持持久化的,memcache是不支持的,相对于memchache慢一点 Memchache一断电就数据没有了,redis支持持久化可以存一点数据,所以安全性不高 七:了解websocket。 轮询: 客户端定时向服务器发送AJAX请求。 服务器接到请求后马上返回响应信息并关闭连接。 长轮询:客户端向服务器发送AJAX请求,服务器接到请求后hold住连接 直到有新的信息or新的数据才返回响应信息并关闭连接。客户端处理完再向服务器发送新的请求 。微信,qq都是基于长轮询 websocket :web socket连上就不断开了。 介绍 HTTp:单向的,永远是客户端发送请求服务端返回 客户端:发起(断开) 服务端:响应(断开) websocket :(双工通道)相互发送,谁都可以发,谁都可以收 客户端:发起 服务端:链接 websocket适用于: 实时显示数据(兼容性不是特别好,低版本不支持) 学习websocket 机制:如果双方加密规则不一样就断开链接了 - 先创建链接 xx等着发数据,链接成功发数据 xx拿到数据加密,加密完封装到响应头里面发过去 我拿到数据也进行加密,,两者进行比对。 if 数据不一样就,断开链接 else : 收发消息 ,解包 封包,解包,包含三大部分 比如[[qcode] [payload len] [data]] 对payload len 进行判断 <=125 =126 =127 数据: masking key (占四个字节) 面试题: 是否了解websocket? 简单聊聊你了解的websocket 服务端手写socket,封包解包 http协议本身规定创建完协议就链接,而websocket协议对请求头封装,其实是通过位运算得到真实的数据 你用他做过什么? web聊天室2 tronado 源码里面有一个web聊天室示例,有个demo的目录 还可以做投票 适用于做什么? 页面实时要求比较高的 框架支持: Tornado框架原生就支持 八:了解 Python网络编程,以及多线程、多进程、协程、IO多路复用、异步非阻塞等。 0:问题:http请求与响应过程? 答: http是超文本传输协议,是一个基于TCP/IP通信协议来传送数据,属于应用层的面向对象协议。 http工作于客户端,浏览器作为HTTP客户端通过URL向http服务端(web服务器)发送请求,创建一个TCP连接,指定端口号,默认是80。 然后连接到服务器工作。在那个端口监听浏览器请求。一旦监听到客户端请求,分析请求类型后, 服务器会向客户端返回一个响应状态,比如"HTTP/1.0 404 OK",同时会返回特定的数据内容。-------如请求的资源,错误代码,其它状态信息等等。 主要体现在短连接,无状态。 **:请求头和请求体是通过什么分割的,两个/r/n。 ** ,一个/r/n 请求包括:请求首行,请求头信息,空行,请求体。 响应包括:响应行,响应头,响应体,状态码等 1.域名解析 2. 发起TCP的3次握手 3. 建立TCP连接后发起http请求 4. 服务器端响应http请求,浏览器得到html代码 5. 浏览器解析html代码,并请求html代码中的资源 6. 浏览器对页面进行渲染呈现给用户 1:问题:说下你所理解的socket: 是应用层与TCP,IP协议通信的中间软件,它是一组接口。 它把复杂的TCP/IP协议隐藏在socket接口后面,对用户来说一组简单的接口就是全部让socket去组织数据,以符合指定协议。 TCP服务端: 1 创建套接字,绑定套接字到本地IP与端口---------------------------------socket.socket(socket.AF_INET,socket.SOCK_STREAM) , s.bind() 2 开始监听连接 --------------------------------------------------------s.listen() 3 进入循环,不断接受客户端的连接请求-----------------------------------s.accept() 4 然后接收传来的数据,并发送给对方数据----------------------------------s.recv() , s.sendall() 5 传输完毕后,关闭套接字------------------------------------------------s.close() TCP客户端: 1 创建套接字,连接远端地址--------------------------------------------------socket.socket(socket.AF_INET,socket.SOCK_STREAM) , s.connect() 2 连接后发送数据和接收数据 -------------------------------------------------s.sendall(), s.recv() 3 传输完毕后,关闭套接字----------------------------------------------------s.close() 2:问题:进程,线程,协程? 答: 1:进程:进程就是一个软件运行的过程,进程是对运行程序的一个抽象,只是一个资源单位。 程序仅仅只是一堆代码而已,而进程是程序运行的过程。 并发和并行: 并发:是伪并行,即看起来同时运行。单个CPU + 多道技术就可以实现并发。 并发的本质:切换加状态保存 并行:多个任务同时运行,只有具有多个cpu才能实现并行。 单核下,可以利用多道技术,多个核,每个核也都可以利用多道技术(多道技术是针对单核而言的) 有四个核,六个任务,这样同一时间有四个任务被执行,假设分别被分配给了cpu1,cpu2,cpu3,cpu4, 一旦任务1遇到I/O就被迫中断执行,此时任务5就拿到cpu1的时间片去执行,这就是单核下的多道技术。 而一旦任务1的I/O结束了,操作系统会重新调用它,可能被分配给四个cpu中的任意一个去执行。 多道技术:内存中同时存入多道(多个)程序,cpu从一个进程快速切换到另外一个,使每个进程各自运行几十或几百毫秒, 这样,虽然在某一个瞬间,一个cpu只能执行一个任务,但在1秒内,cpu却可以运行多个进程,这就给人产生了并行的错觉 。 同步异步: 同步执行:一个进程在执行某个任务时,另外一个进程必须等待其执行完毕,才能继续执行 异步执行:一个进程在执行某个任务时,另外一个进程无需等待其执行完毕,就可以继续执行, 当有消息返回时,系统会通知后者进行处理,这样可以提高执行效率 2:线程:就是一条流水线式的过程,线程才是CPU上的执行单位。 多线程:在一个进程中存在多个控制线程,多个控制线程共享该进程的地址空间。(相当与一个车间有多条流水线,都共用一个车间的资源) **一个进程可以开多个线程,共享同一个进程里面的内存空间。 进程里面正真干活的是线程,进程只是用来把资源互相隔离开。 线程和进程的区别? 答:创建线程比进程开销小,(开一个进程,里面就有空间了,而线程在进程里面,就没必要再开一个空间了) 多线程一定是在一个进程里面开启的,共享进程里面的资源, 线程的启动速度快, 同一进程下的多个线程共享进程的资源,而多个进程之间内存空间是隔离的。 线程可以跟它同一进程间的线程相互通信。 为什么要有多线程? 答:多个任务共用一块地址空间,那么就必须在进程内开启多个线程。 3:协程 协程,又称微线程,单线程下实现并发。 即在一个主线程,并且CPU只有一个的情况实现并发(并发的本质:切换+保存状态) 优点: 1:协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因而更轻量级。 2:单线程内就可以实现并发的效果,最大限度地利用cpu 缺点: 1:协程的本质就是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启协程。 2:协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个协程。 协程的特点: 1:必须在只有一个单线程里实现并发。 2:修改共享数据不需要加锁 3:用户程序里自己保存多个控制流的上下文栈。 4:一个协程遇到io操作自动切换到其他协程(如何实现检测io,yiled,greenlet都无法实现,就用到了geven模块(select机制)) 区别: 1:地址空间:线程是进程内的一个执行单元,进程内至少有一个线程,他们共享进程的地址空间,而进程有自己独立的地址空间。 2:资源拥有:进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源。 3:线程是处理调度的基本单位,但进程不是。 4:二者均可并发执行。 5:每个独立的线程有一个程序运行的入口,顺序执行序列和程序的出口,但线程不能够独立执行,必须依赖存在应用程序,由应用程序提供多个线程 执行控制 比较: 1:一个线程可以多个协程,一个进程也可以单独拥有多个协程,这样pytho中则能使用多核CPU. 2:线程进程都是同步机制,而协程则是异步。 3:协程能保留上一次调用的状态,每次过程重入时,就相当于进入上一次调用的状态。 3:软件开发架构: C/S架构:客户端与服务器端的架构,也是从用户层面来划分的 B/S架构:浏览器与服务器端的架构,是从用户层面来划分的。 4:问题:TCP UDP 答:tcp:可靠的、面向连接的协议(eg:打电话)、传输效率低全双工通信(发送缓存&接收缓存)、面向字节流。 使用TCP的应用:Web浏览器;电子邮件、文件传输程序。 tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端 udp:不可靠的、无连接的服务,传输效率高(发送前时延小),一对一、一对多、多对一、多对多、面向报文,尽最大努力服务,无拥塞控制。 使用UDP的应用:域名系统 (DNS);视频流;IP语音(VoIP)。 udp是无链接的,先启动哪一端都不会报错 5:问题:osi7层协议/4/5 答:应用层,表示层,会话层,表示层,传输层,网络层,数据链路层,物理层。 socket:链路层,网络层,运输层,应用层---------------------------------处于运输层和应用层之间。 6:问题:三次握手、四次挥手? 答: 三次握手:是指建立一个TCP连接时,需要客户端和服务器总共发送3个包。 一次握手:客户端发送一个TCP的SYN标志位置1的包。----------指明客户打算连接的服务器的端口,以及初始序号X,保存在包头的序列号(Sequence Number)字段里。 二次握手:服务器发回确认包(ACK)应答。---------------------即SYN标志位和ACK标志位均为1同时,将确认序号(Acknowledgement Number)设置为客户的I S N加1以.即X+1 三次握手:客户端再次发送确认包(ACK)。--------------------SYN标志位为0,ACK标志位为1.并且把服务器发来ACK的序号字段+1,放在确定字段中发送给对方.并且在数据段放写ISN的+1 三次握手完成,客户端与服务器开始传送数据。 三次握手目的:同步连接双方的序列号和确认号,并交换TCP窗口大小信息 四次挥手: 四次挥手:就是TCP的连接关闭需要发送四个包,因此称为四次挥手(four-way handshake)。-----------------------------客户端或服务器均可主动发起挥手动作,在socket编程中,任何一方执行close()操作即可产生挥手操作。 1. 主动关闭一端会向被动端发送FIN终止信号序列,关闭数据传输。--------------------------------------------------主动端状态变为FIN_WAIT_1。此时关闭了主动端向被动端的数据传输。 2. 被动端收到FIN后,向主动端发送一个ACK序列(值为FIN序号加1),此时被动端向主动端的数据传输还没有关闭。-------完成发送后被动端状态置为CLOSE_WAIT,此时被动端向主动端的数据传输还没有关闭。 3. 主动端收到被动端发送的ACK响应序列,被动端关闭与客户端的连接,向主动端发送一个FIN终止信号序列。-------------被动端状态会切换为LAST_ACK(最后一个响应状态)。 4. 主动端收到被动端发出的FIN,接着发送ACK响应序列到被动, 被动端收到ACK后,释放掉socket资源。------------------ 为什么连接的时候是三次握手,关闭的时候却是四次握手? 答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。 其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时, 很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。 只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。: 8:问题:IO多路复用 答:IO多路复用就是,单个进程可以同时监听多个网络连接IO,IO指的是输入输出,一部分指的是文件操作,还有一部分网络传输操作,例如:Socket就是其中之一。 多路复用指的是一种机制,同时使用多个IO。例如同时监听多个文件句柄(Socket对象一旦传送或者接收对象),一旦文件句柄出现变化就会立刻感知到。 I/O多路是用于:提升效率,单个进程可以同时监听多个网络连接IO 1:IO多路复用的实现? select-----------------select模块实现多路复用,使同一个端口同时接收多个链接 poll-------------------epoll.poll 是一个实例方法,用于等待 IO 事件并返回一个已就绪 IO 事件的列表, epoll------------------select.epoll 是一个类方法,返回一个 epoll 对象,这里的参数可以为 -1 或 一个正数, 只是 epoll 内部结构优化使用的参数,一般使用默认即可 9:问题:生产者消费者模型 ? 答:生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。 生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列, 消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。 生产者消费者模型解决绝大多数并发问题。 1:为什么要使用生产者和消费者模式? 答: 在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢, 那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。 为了解决这个问题于是引入了生产者和消费者模式。 2:如何实现生产者消费者模型? 经常使用的基于队列的生成者消费者模型。 管道加锁 共享数据 3:锁是什么东西? 进程间的数据隔离,但共享一套文件系统,因而可以通过文件来实现进程直接的通信,但必须加锁处理。 4:加锁的目的? 为了保证多个进程修改同一块数据时,同一时间只能有一个修改,就是串行的修改。-------这样虽然速度慢了,但保证了数据的安全。 10:异步非阻塞? 答: 例如,对于一个聊天室来说,因为有多个连接需要同时被处理,所以阻塞或同步的方法是不合适的, 这就像买票只开了一个窗口,佷多人排队等一样。那么我们解决这个问题的方法 主要有三种方法:forking、threading、异步I/O。 Forking和threading的方法非常简单,通过使用SocketServer服务类的min-in类就可以实现。 forking只适用于类Unix平台;threading需要注意内存共享的问题。 异步I/O如果底层的方法来实现是有点困难的。 要简单点,我们可以考虑使用标准库中的框架或Twisted(Twisted是一个非常强大的异步网络编程的框架)。 11:并发的本质? 答:切换加保存状态。并发只是我们感觉是同时运行的,其实是在多个任务之间来回切换,切的时候保存状态。 切的话遇到IO阻塞的时候才切。 12:进程,线程,协程谈谈你的认识? 答:进程:一个程序正在执行的过程,有自己的内存地址。 线程:一条流水线式的工作过程,一个车间有多条流水线。 这个车间的工作过程就是进程,一个进程中可以运行多个线程,共享进程的内存地址,进程里面真正干活的是线程。 协程:一个任务执行完,切换到另一个任务。单线程下实现并发。 13:进程,线程,协程的区别? 答:工作单位最小的是线程,一个进程可以开多个线程。 而协程是我们可以自己分配的,先执行那个,后执行那个。 14:为什么使用生产者消费者模型? 答:在多线程开发中,如果生产者生产过快,而消费者消费过慢,那么生产者必须等待消费者, 反着,消费者就必须等待生产者,这样就形成了互相等待的现象,从而降低了效率。 所以才引进了生产者消费者模型。 15:什么是生产者消费者模型? 答:生产者消费者模型通过一个容器来解决他们之间的强耦合问题,他们之间不彼此通信。 而是通过阻塞队列进行通信。生产者只负责生产数据,不用等消费者,生产完直接扔给阻塞队列, 消费不找生产者要数据,从阻塞队列中拿数据。 队列相当一个缓存去,平衡了生产者消费者的处理能力。 使用生产者消费者模型能解决大多数的并发问题。 16:进程间数据怎么共享?---------------进程与进程之间数据隔离,需要共享----------Manage 答:进程之间的有数据隔离的,要想实现数据共享通过Manager来实现。 IPC(进程间的三种数据共享) Queue队列:先进先出。 pipe管道:管道也是先进先出,但是不自动加锁。管道加锁,实现生产者消费者模型。 Manage共享数据:共享数据也没有加锁的功能。 17:线程与线程怎样做数据隔离?---------线程与线程之间数据共享,需要隔离----------本地线程 答:线程运行在进程里,共享进程的内存地址,数据也是共享的。 要数据隔离的话用本地线程。 本地线程:保证每个线程都有一份自己的数据,在操作时不会影响别人。 即使是多线程也是数据隔离的。 九:熟悉 Scrapy框架和分布式爬虫框架redis-scrapy组件, 爬虫相关Requests和BeautifulSoup模块使用。 答; 问题:爬虫基本流程? 答: 模拟浏览器发送请求,下载网页代码,只提取有用的数据,存放于数据库或文件中。 请求:向目标站点发送一个htttp请求,是一个request.包括请求方式,请求的url,请求头,请求体。 响应:如果服务器正常响应,获取响应内容,得到一个response。包括html,json,图片,视频,等。 解析:解析内容:解析响应的html,可以使用正则,第三方库,Beautifulsoup,pyquery等。 保存:保存数据:可以用mongoDB,mysql等 问题:说一下你了解的request?请求库 答:request是一个请求库,requests模块的api更加便捷(本质就是封装了urllib3)。 requests库发送请求将网页内容下载下来以后, 并不会执行js代码,这需要我们自己分析目标站点然后发起新的request请求 问题:selenium?请求库 答:selenium最初是一个自动化测试工具, 而爬虫中使用它主要是为了解决requests无法直接执行JavaScript代码的问题。 selenium本质是通过驱动浏览器,完全模拟浏览器的操作,比如跳转、输入、点击、下拉等, 来拿到网页渲染之后的结果,可支持多种浏览器。 选择器:Xpath,获取标签属性。 等待元素被加载:有隐式等待,显示等待。 问题:说一下你理解的Beautiful Soup ? 答:Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库. 遍历文档树? 获取标签的名称 获取标签的属性 获取标签的内容 嵌套选择 子节点、子孙节点 父节点、祖先节点,兄弟节点 搜索文档树? fild,fild_all 五种过滤器: 字符串、正则表达式、列表、True、方法 css选择器。 问题:如何抓包的? 答:根据需要爬取的内容,进行抓包分析,比如登录,模拟浏览器发送http请求,获取页面。 输入用户名,将密码输错,发送post请求,获取到cookie。 问题:scrapy-redis? 答:是一个基于redis的scrapy组件,通过它可以快速实现简单分布式爬虫程序,该组件本质上提供了三大功能: scheduler - 调度器, dupefilter - URL去重规则(被调度器使用) pipeline - 数据持久化 问题:分布式爬虫? 答:共享队列,重写Scheduler,让其无论是去重还是任务都去访问共享队列 为Scheduler定制去重规则(利用redis的集合类型) 问题:说说你所了解的Scrapy框架? Scrapy 是基于twisted(twisdande)框架开发而来,twisted是一个流行的事件驱动的python网络框架。 因此Scrapy使用了一种非阻塞(又名异步)的代码来实现并发。 用途:页面抓取,数据挖掘,检测和自动化测试。 优点:使用它可以以快速、简单、可扩展的方式从网站中提取所需的数据。 组件: 1:引擎(EGINE): 引擎负责控制系统所有组件之间的数据流,并在某些动作发生时触发事件。 2:调度器(SCHEDULER): 用来接受引擎发过来的请求,压如队列中,并在引擎再次请求时再次返回。 3:下载器(DOWLOADER) 用于下载网页内容,并将网页内容返回给EGINE,下载器是建立在twisted这个高效的异步模型上的。 4:爬虫(SPIDERS) SPIDERS是开发人员自定义的类,用解析responses,并且提取items,或者发送新的请求。 5:项目管道(ITEM PIPLINES) 在iterms被提取后负责处理他们,主要包括清理,验证,持久化........ 6:下载器中间件(Downloader Middlewares) 位于Scrapy引擎和下载器之间,主要用来处理从EGINE传到DOWLOADER的请求request, 已经从DOWNLOADER传到EGINE的响应response 7:爬虫中间件(Spider Middlewares) 位于引擎(EGINE)和爬虫(SPIDERS)之间,主要工作是处理SPIDERS的输入(即responses)和输出(即requests)。 流程:在Scrapy的数据流是由执行引擎控制:流程如下: 1:引擎接收初始的请求,准备爬取。--------------一定捆绑一个回调函数。 2:引擎将请求交给调度器,调度器负责调度这个请求,通过队列或堆栈的方式存储起来。 3:调度器返回请求给引擎。 4:引擎发送调度好的请求通过中间件发给下载器。 5: 下载器下载完毕通过中间发送Response回传给引擎。 6:引擎从下载器接收到响应,将其通过爬虫中间件发送给爬虫。 7:爬虫程序通过引擎的响应通过爬虫中间件将处理过的items对象和新的请求返回给引擎。 8:引擎将处理过的items对象发送到项目管道,然后将处理过的请求发送给调度程序。 9:进程重复(从步骤1),直到不再有来自调度器的请求。 问题:了解spider吗? 答:Spiders是由一系列类(定义了一个网址或一组网址将被爬取)组成, 具体包括如何执行爬取任务并且如何从页面中提取结构化的数据。 1:生成初始的Requests来爬取第一个URLS,并且标识一个回调函数。 2: 在回调函数中,解析response并且返回值。 3: 在回调函数中解析页面内容。 4最后,针对返回的Items对象将会被持久化到数据库 总共提供个五个类。 1scrapy.spiders.Spider 2scrapy.spiders.CrawlSpider 3scrapy.spiders.XMLFeedSpider 4scrapy.spiders.CSVFeedSpider 5scrapy.spiders.SitemapSpider 问题:Scapy框架用什么解析? 答:XPath和它自带的Selectors(silandes) 问题:scapy的好处? 答:应用广泛广泛。 成熟度最高的框架, 利用成熟产品,避免重复“造轮子”,可以更快速的构建项目。 十:熟悉 Git使用。 1:git init------------------初始化 2:git add .-----------------从工作区,添加到版本库 3:git commit -m"xxx"--------从暂存区,添加到分支 4:git status----------------查看状态 5:git log ------------------查看版本库的日志 6:git reflog----------------查看所有日志 7:git reset --head 版本号---切换 8:git stash-----------------保存 9:git stash-----------------将第一个记录从“某个地方”重新拿到工作区(可能有冲突) git stash list----------------------------------------------------查看“某个地方”存储的所有记录 git stash clear---------------------------------------------------清空“某个地方” git stash pop-----------------------------------------------------将第一个记录从“某个地方”重新拿到工作区(可能有冲突) git stash apply --------------------------------------------------编号,将指定编号记录从“某个地方”重新拿到工作区(可能有冲突) git stash drop --------------------------------------------------编号 ,删除指定编号的记录 10:git branch dev----------创建分支 11:git branch -d dev-------删除分支 12:git checkout dev--------切换分支 13:git merge dev-----------合并分支 14:git branch--------------查看所有分支 15:git clone https:xxx-----克隆 16:git add origin https:xxx-起个别名 17:git push origin dev ----添加到dev分支 18:git pull origin master--拉代码 19:git fetch origin master-去仓库获取 20:git merge origin/master-和网上下的master分支合并 协同开发: 默认是master分支--------------------master 开发的分支--------------------------dev 做代码review------------------------reciew 程序员自己的分支--------------------....... 1:每个员工创建自己的分支 2:将自己的代码提交的到自己的分支----------xxx,sss,wwww....... 3:由组长或老大做代码的review,-------------代码提交的review分支 4:再提交到dev. 5: 再合并到master分支 十一:熟悉 Linux常用操作。 1:man rm------------------------------查看命令帮助 2:mkdir-------------------------------创建目录 3:touch-------------------------------创建文件 4:cd----------------------------------切换。 5:ls----------------------------------查看目录 6:ls_l--------------------------------查看目录详细 7:pwd---------------------------------查看当前目录 8:vim---------------------------------添加内容 9:echo--------------------------------追加内容 10:cat--------------------------------查看文件内容 11:mv---------------------------------移动 12:cp---------------------------------拷贝 13:avi--------------------------------重命名 14:bak--------------------------------备份 15:find-------------------------------搜索 16:rm---------------------------------删除数据 17:ping-------------------------------查看能不能上网 18:awk--------------------------------查看区间 19:tar--------------------------------打压缩 20:tar xf-----------------------------解压缩 安装: yum install rpm包安装 编译安装 快捷键: 1:Tab键---------------------------自动补全命令或路劲。 2:ctrl+l--------------------------清屏 3: ctrl+c--------------------------取消当前操作 4:vi/vim 快捷键: 复制当前行 --------------------yy 粘贴---------------------------p 剪切---------------------------dd 撤销---------------------------u 恢复---------------------------ctrl + r 使用上一个命令的最后一个东西---esc + .(点) e:项目经历: 一:慧聪学城介绍:-------2017.07月-至今(6个月,后面加了一个题库的功能) 公司开发慧聪学城,主要用于为中小学生实现在线学习和作业管理的在线视频学习平台,并有有望成为西安第一家, 针对中小学生编程教育的机构,系统使用前后端分离的模式,前端使用vue框架,后端使用restframeworl实现。 系统主要用于为用户提供学习平台,并创建一对一辅导,并整合用户支付和微信消息的推送和提醒。使用Git进行协同开发。 项目主要有三大模块: 项目三大模块: 1:导师后台是:基于Thanos组件,给导师"管理学员作业"、"学习进度"、"跟进记录"等。 2:管理后台:基于Thanos组件,给运营使用:"上传视频"、"写文章"、"优惠券的发放"、办理"退款"、"休学"等业务。 3:主站(基于Vue+Django restful framework框架): (2)我主要负责主站后端的开发。主站主要有分为五大模块: 第一个模块是课程:主要包括:课程大类和子类两种,分别有学位课程和普通课程两种, 区别在于学位课程除了包含普通课程的优惠卷和价格策略 还包含学位奖学金和分配相应的导师进行一对一的跟进辅导。 除此之外还有: "课程列表","课程详细","课程章节","课程目录","看视频","评论"。 第二个模块是深科技:是基于restful将数据序列化,基于restful api规范的不同请求方式 来实现响应的功能, 如使用get方法查看”文章",post方法实现"点赞"、"收藏"、"评论"等功能。 使用option实现复杂请求 解决跨域问题。 第三个是购物车:提交请求获取当前课程信息,价格策略等信息并写入redis实现加入购物车, 有两种支付方式:跳转到支付页面立即支付,用户进入购物车点击去结算, 从redis获取相应信息,点击去结算,获取商品价格策略, 获取当前用户的优惠卷,贝里,判断是否过期,判断优惠卷类型,进行相应的折扣, 点击立即支付,生成订单,判断状态是否支付成功,支付成功清空购物车,也就redis 中的相应商品信息,最后是基于关注公司微信公众号实现的微信消息推送。 然后是个人中心: 基于restful获取数据,将数据序列化。主要包括"我的账户","我的订单", "我的收藏","个人资料","账号安全"。 最后一个模块就是一些:”关于我们“,”联系我们“,”商务合作等“。 (3)主要功能,大多数只是根据前端需求,获取数据,序列化返回。 1. 自定义rest认证token 认证。 2. 序列化以及自定义验证对请求数据合法性进行校验和认证。 3. API访问的接口速率限制。 4. 课程优惠券,通用优惠券,视频点赞,点踩,收藏,更换头像,上传作业,评论等。 5. 支付宝支付/微信支付。 6. 微信开发,针对关注微信公众号,实现对学位课程报名的用户作业奖惩和导师的跟进奖惩。 问题:你们公司主要是做什么的? 答:主要做教育教学软件的研发和销售,还有中小学的教育和作业辅导,有望做中小学的编程教育。 问题:rst是如何获取前端数据的? 答:request.data.get(\'xxx\') 问题:如何序列化的? 答:Django-rest-framework的序列化。 问题:购物车是如何如何实现的? 答:1:当用户选择需要购买的课程和价格策略后,有两种方式购买课程: 一:直接购买,将课程id和选择的价格策略放到redis中, 跳到去支付页面,从redis中获取购买课程的id和价格策略id, 如果该用户要使用优惠券和贝利,则选择当前用户所拥有并且未使用和没过期的优惠券, 得到折后价格,点击去支付完成支付,删除购物车信息,生成订单,保存到数据库。 二:添加到购物车中完成支付(post) 获取到所选课程的id和价格策略id传到后端, 在后端获取,根据课程id获取到当前课程,根据当前前课程所有的价格策略, 判断在后端获取到的价格策略id在不在当前课程的价格策略中, 不在的话则抛出异常价格策略不存在,在的话继续执行。 将该课程所有价格策略的id,时间周期和价格存放到列表中(price_policy_list)中。 继续将该课程的:课程id,课程图片地址,课程标题, 所有价格策略,默认价格策略封装在字典(course_dict)中。 判断当前用户的购物车中是否有东西,没有的话则把当前课程放入到购物车中, 如果购物车中已经有课程课程,则将该课程添加到购物车中,最后封装好放到redis中。 三:修改购物车总的价格策略(patch): 如果要修改购物车的总价格策略:向后台发课程id和要修改为的价格策略id, 判断课程是否在购物车中,判断传递过来的价格策略是否在当前课程的价格策略中, 在的话将 redis中的当前课程的默认价格策略修改为当前的价格策略。 去结算: 获取用户提交的课程id, 判断是否选择要结算的课程,没选择则抛出异常。 检测购物车中是否已经有课程(应该有课程的)。 检测购物车中是否有用户要购买的课程 如果所结算的课程在购物车中, 获取选中价格策略的价格详细, 选择购物车中当前课程下的所有价格策略以及相等的价格策略,获取其信息, 获取当前用户所有的优惠券, 区分用户的优惠券种类,课程优惠券添加到课程中;全局优惠券添加到全局, 循环遍历当前用户的所有优惠券,判断他们是否过期, 如果没过期,判断他们是全局优惠券还是针对某个课程的优惠券, 区分好是什么优惠券,后面区分该优惠券是什么类型, 是通用券,还是满减券,折扣券,全局优惠券, 针对不同的优惠卷,最后将所有数据封装放到redis上。 去支付: 1.去结算中心获取要结算的所有课程和优惠券, 2.循环遍历每一个课程, 开始总价格和折扣价都为0, 如果该课程没有使用优惠券,则支付价格=课程原价,折扣价=0, 3.如果使用了优惠券,则需要去判断所使用的优惠券是否已经过期, 或是否已经被使用,如果过期了或者被使用了就抛出异常, 否则继续往下执行,判断该优惠券的类型. 3.判断优惠卷类型计算出相应的折扣。支付总价=商品总价 - 折扣 4.获取当前用户的贝里,获取总价。判断贝里是否能全额支付。 6.点击立即支付以后, 进行数据库操作,生成订单。 生成订单: 点击立即支付,生成订单,订单根据前面支付是否等于0来判断支付状态:已支付或者待支付; 生成订单详情,循环课程信息,写入课程的原价,折后价,支付价,视频的有效期即订单的有效期, 对优惠券进行处理,把该用户已使用的优惠券状态改为已使用; 修改贝里,该账户的贝里减去使用的贝里,并更新贝里消费记录表, 显示账户的消费金额,账户的余额等。 最后删除购物车中的信息,即redis中的购物车信息。 问题:你们项目总共多少张表,多少行? 答:48张表,是我参加项目表最多的一次,光表就有1005行。 问题:说几张你熟悉的表? 课程相关:课程大类,课程子类,学位课程,普通课程,课程详细,课程大纲,课程章节,课时目录,常见问题等 深科技相关:文章来源,文章资讯,文章点赞,文章收藏,文章评论。 用户相关:用户表,用户Token表。 购物车相关:购物车是基于Redis实现的。 还有:优惠卷生成规则表,价格与有课程效期表等等。 问题:项目是几个人负责的? 答:后端主要两个人,我写主站,还有一个写后台管理。 问题:你在开发中遇到的坑有那些? 答:坑到没有什么,涉及的技术也不是特别难,在做购物车和立即支付的时候,业务逻辑比较复杂, 因为其中涉及到价格策略,优惠卷,我们自己的贝里。 问题:CORS? 答:前端问我,最开始听说过,不太熟悉,就了解了一下 概述: 问题:如何连接redis的? 答: POOL=redis.ConnectionPool(host=\'192.168.20.58\',port=6379) conn=redis.Redis(connection_pool=POOL) 问题:自定义rest认证token 认证是如何实现的? 答:自定义token认证。客户端传递过来的token等参数进行身份验证。 通过rest framework为我们提供的认证机制去验证,每一类继承BaseAuthention,里面实现了一个authtent方法, 获取用户传过来的token,看传过来的token是否和数据库的一致,一致验证通过,不一致,说明是无效token. 问题:API接口的速率限制是如何实现的? 答:针对匿名用户和登录用户加以区分,登录用户记录它的name,匿名用户记录它的IP。 我们项目中又判断了一下是否是管理员,根据用户类型的不同,来判定不同的限流策略。 创建一个类继承BaseThrethe,写一个allow_request和wuit方法限制访问。 问题:做一个项目你都用了哪些技术点 像是慧聪学习系统,我们写后端的时候就是用restformwork来做的,返回数据的时候 是基于序列化来实现的。用methodSerialize直接从数据库中查到响应的值返回, 类似于django中的ModelForm。 参考restfulwork源码实现的认证功能,还可以基于__call__方法实现的自定义验证规则 二:慧聪管理系统:-------2016.8-2017.5--后期在持续优化(开发周期差不多一年) 慧聪管理介绍:这套系统主要是给公司内部使用的,公司刚成立的时候,人员少,存储数据都用Excel保存, 但随着公司的扩大,业务的发展,客户的增多Excel无法满足公司的需要, 需要一套,方便快捷管理公司业务的系统。 系统基于权限管理的使用,对系统人员进行权限控制对学校、老师、班级和销售进行统一管理, 其中涉及销售的报表和跟进记录,并对销售人员成单比进行统计,通过highchart做出图表。 以及基于Thanos组件做的,用户管理,部门管理,班级管理,课程管理 学校管理,销售管理,客户关系管理,抢单管理,员工管理。 主要业务是"销售管理": 主要针对的是公司销售部门的工作管理,主要用于工作安排, 销售进度的跟进以及为业绩考核提供数据支持。 首先公司拿到一批资源,通过单条输入,或批量导入的方式添加 数据到客户表,批量导入支持Excel导入。 添加到数据库后系统自动按销售的权重将客户分派给销售(radis), 并发送短信,微信,邮箱提醒销售。 销售进行跟进,客户分派表新增数据,客户跟进记录表新增数据。 销售拿到客户进行跟进,若15天未成单或3天未跟进(定时任务), 客户将变成公共客户,销售可以在公共客户中进行抢单,但不能抢 课程顾问是自己的,抢单成功,客户分派表新增一条记录, 销售拿到客户进行跟进。 基于Thanos组件,保存上课记录,学生考勤,记录学生成绩,图表展示学生成绩等。 除此之外还有:"员工管理","课程管理","班级管理(班级评分,作业管理)"等模块 还有会议室预定,调查问卷,基于角色粒度精确到按钮的权限控制。 功能: 1: 基于Session和极验滑块验证的登录认证功能,以及注册,注销,找回密码等功能。 2:基于中间件和session实现权限组件,粒度到按钮级别。 3:基于BootStrap实现页面展示。 4:使用xlrd实现excel批量操作。。 5:基于HighChart对销售业绩进行可视化显示。 6:参考Django.Admin源码 实现Thanos组件开发,用于快速实现大量的增删改查功能。 7:基于Form实现可定制的调查问卷 8:基于BootStrap datetimepicker插件实现会议室预定。 9: 基于redis的销售自动分配。 问题:Django-Admin源码流程? 1:在Django项目启动时,扫描每个APP项目下的admin.py文件的文件, 创建admin.site中的对象,site = AdminSite(),本质实例化一个对象,以后不管谁来调用都使用这个对象, 执行对象的register方法,目的将注册类添加到_register中, admin.site是一个对象(单例模式创建),其中封装了_register。 2:再次调用admin.site的urls属性。 返回了一个元组,元组有三个元素,self.get_urls(),\'admin\',self.name。 第一个元素是一个函数返回的是一个列表,列表中是url,是循环admin.site中的_register(ruanzhiste), 中的注册类,生成url,放在列表中。为每个注册类生成一级URL,其次调用类的样式对象下的get_url_func(self) 函数,生成二级URL,同时为每一个增删改查URL创建别名,用于反向解析,每个url对应一个视图函数。 问题:怎样实现粒度精确到按钮的? 答: 用户登录成功之后,获取所有权限,并且保存在session中。 获取session中的信息,循环进行正则匹配,如果匹配成功,说明有权限。 匹配不成功则无权限。 在做这个认证的时候,因为很多的视图函数都要进行认证,所以我是用中间件来处理的。 匹配成功的时候,把code[权限1,权限2]保存在request里面,循环判断有没有相应的权限。 问题:权限管理总共几张表? 答:五个类,七张表。 用户表,角色表,权限表,权限组表,菜单表。 一个用户可以有多个角色,一个角色有多个权限。 一个"菜单"下面有多个"权限组"。 一个"菜单"下面有多个"权限"。 问题:订单分配如何实现的? 答: 根据对销售人员的以往业绩的分析制定每个销售人员的销售任务,综合多方面数据对销售人员进行权重的评定。 在工作分配时,通过权重进行排序,权重大的优先安排任务。 首先,获取权重表里面的所有销售人员并根据权重进行降序排序,根据权重和转换人数将销售的id依次循环放入列表中。 循环一次加一人,直销售人员的分配人数等于可以接收的转化人数,就跳过该销售人员。 若所有销售人员都达到目标值则将剩余销售任务重复之前的方案进行分配。 当循环完以后,调用reset()重置即可。 创建一个类,获取权重表中的所有销售ID,并根据权重降序排列, 根据权重和每个销售可以接收的数量,将销售ID依次循环,循环一次添加一条资源(客户), 直到销售的资源等于可以接收的数量,跳出循环。 问题:使用Redis的原因? 答:主要使用Redis来完成(原因:减轻内存压力,其次Redis可以有效的处理多进程问题,支持持久化)。 问题:销售管理的具体实现? 答: 客户来源主要有三项:运营部门根据权重进行分配的。 在公司的公共资源中抢单,刚进公司的销售没有权重,可以在公共资源中抢单,转化成功,分配权重。 自己寻找资源,自己录入。 销售人员得到任务后对应的客户状态改为开始接洽,记录起始时间, 客户状态状态从公司资源更改为销售人员的个人资源,其他人在订单转移前不可接触订单信息。 销售人员在跟进订单时,每一次与客户接洽都会在数据库中生成一条记录。 若客户在十五日内被销售人员转化成功,则将该客户的状态由待转化变为转化成功, 并在正式客户表中生成该客户的记录。 在销售人员的订单记录中将这笔订单的状态改为转化成功。 若当前与客户接洽的销售人员三天未跟进订单或十五天内未促成交易。 则相关客户信息会被移动到公司公共资源中,并且原先跟进订单的销售人员不可以选择继续跟进, 直至该客户再次被移入公司公共资源。 原销售人员的订单跟进记录中会显示有一单未能转化,并显示原因,重新接手该订单后即使转化成功,本条记录不会被覆盖。 其他销售可以在公司公共客户资源中进行抢单,抢单成功,将该用户添加到我的客户,默认显示正在跟进, 客户跟进记录表中再新增一条记录,获取当前时间为接单时间。进行跟进。 销售人员所接触过的每一个客户,不管什么来源,是否转换成功都会保存起来,为以后的权重划分和绩效考核为依据。 通过实现查看我的客户就可以一目了然的看到该销售人员的所有客户 在销售人员的订单记录中记录了销售人员从第一笔业务到最近一笔业务的所有信息。 可以为销售人员的业绩考核提供:接单数,转化率,订单未转化原因等数据。 问题:学校管理业如何实现的? 答: 对教学班级,校区,课程,学生等进行管理,主要用于班级的成员管理、课堂出勤情况记录以及学员成绩记录。 销售人员与客户接洽完毕,将客户转化为学员后,根据其选择的校区、课程、班级将其信息录入学员的数据库中。 初始化该学员的账号和密码,以便其进入教学管理系统查看自己的成绩以及出勤记录。 若该学员因某些原因中途退学或进入其他班级,则将其记录删除,新增一条数据。 课堂出勤情况记录: 每日上课前由班主任或当日讲师初始化当日的考勤信息,初始化时默认全部全员正常出勤。 如有学员存在:迟到、旷课、早退或请假等情况。可由班主任或当日讲师修改其考勤状况(支持批量修改)。 若有学生中途进入班级,进班前的考勤记录不予生成。若有学生中途离开教学班级,离班前的考勤记录不予删除。 上课教师和班主任对学生进行考勤管理,考勤直接影响这节课的成绩,考勤种类为已签到,迟到,缺勤,早退,请假. 初始化实现原理: 点击复选框选中要初始化的当天班级课程,点击action中学生初始化对学生完成初始化 默认全部出勤, 初始化管理: 如果有个别学生出现违规情况,在studyrecord中对该学生进行操作 实现原理: 教师和班主任在课程记录页面点击考勤管理,调转到该班级的学习记录页面,列出该班级的所有学生, 利用action对学生进行批量的 考勤管理 如果有个别学生出现违规情况,在studyrecord中对该学生进行操作。 实现原理: 教师和班主任在课程记录页面点击考勤管理,调转到该班级的学习记录页面, 列出该班级的所有学生,利用action对学生进行批量的。 学员成绩记录: 在班主任或当日讲进行初始化考勤信息操作时可以选择当日是否有作业(支持修改)。 若当日有作业则开放学生作业提交的功能,学生须在提交时间内提交,提交时间结束关闭该功能。 学生提交作业后在提交时间内允许撤销提交并重新提交。提交时间结束后,导师可以下载学员提交文件, 并进行打分评定。打分和评定评语结束后可立即上传至教学管理系统, 学生及学生家长可以通过教学管理系统进行查询(支持批量导入)。 若有学生中途进入班级,进班前的成绩记录不与不予生成。若有学生中途离开教学班级,离班前的成绩记录不予删除。 原理: 在课程记录页面点击成绩录入调到到成绩录入页面, 主要是根据当前趁机记录的id获取该节课所有的学生记录对象,发送到前端, 其中由于成绩打分要严格区分,所以成绩和评语有type动态生成ModelForm对象传到前端, fields字段分别是score_学生记录id,homework_note_学生记录对象id,post传到后端 的时候在courseRecordConfig中。 问题:批量导入如何实现的? 答:基于xlrd实现的。 问题:项目中遇到问题,如何解决的? 答: 1:我们一开始做订单分配,使用的是__iter__方法,返回一个迭代器,每next一下取一个值。由于重启或求进程的时候会有问题。 所以我们用redis来实现自动分配,首先根据权重进行排序,权重大的优先安排任务, 分配任务的时候,按照权重排序从高到底循环分配,根据每个销售可以接收的数量进行分配。 如果那个销售可以接收的数量达到目标值,则跳过,将剩余的资源继续进行分配, 若全部销售达到目标值,还有资源,则在原来的基础上添加,也就是重复上面的操作。 2:popup本身很简单,就一段js代码,但是在页面跳转时,indow.open("","name")打开一个页面,第二个参数不能重名, 否则打开多个页面。执行回调函数。 2:在FK时,可以使用limit_choice_to(引用另外一张表中的数据时,对另外一张表添加条件) 条件可以是字典和Q对象, 不能通过传参的方式做,只能用两个类的字段,用related_name和model_name传递过去, 获取所有的反向关联字段,获取limit_choice_to字段,再进行查询。 3:路由系统动态生成url: 看了Admain源码实现的。参考Admin我知道了:用include进行路由分发,一个url对应一个元组, 元组中是一个列表,后面是app名称和namespace. 4:开发组件时,最开始看到admin源码不太理解,当和权限系统配合时,才领悟其中真谛。预留的钩子,是和权限搭配的。 开始想的只要用add_btn=True,show_searche=True等等就可以了,为什么还要用get_add_btn() 和get_show_search等等,后来开发组件进行权限管理时才明白,这都是预留给权限用的, 根据继承的先后顺序和登录用户所拥有的权限判断是否显示按钮等。 5:学生录入成绩时,为了区分是给那个学生录成绩, 并且在后台获取的时候能够区分这个成绩和评语是给那个学生的利用了type动态生成form还有动态生成field字段。 问题:慧聪管理系统总共几张表?都有什么表?都有哪些字段? 答:12张:员工信息表,部门表,课程表,校区表,班级表, 客户表,客户分配表,销售和权重表,缴费记录表,学生表,上课记录表,成绩考勤表。 字段具体记不清了 问题:有那些类,他们的继承关系? 问题:调查问卷? 答: 调查问卷是为了调查学生对学校设备和讲师讲课的满意程度, 以及他没有什么困难等,获取他们的意见以方便我们进行改进, 问卷只能有班主任和教务总监发起,并明确的规定班级和起始日期和结束时间, 并且只有本班学生才能填写,调查问卷的题型有三种,填写内容(建议),单选,打分。 学生打分后点击提交即可完成调查问卷,学生提交后,在首页可显示答卷的人数。 问题:会议室预定? 答:公司人员增多,空间有限,会议室需要被预定才可使用。 每个会议室从早上八点-晚上八点可以预定,一小时划分, 如果该会议室当前时间被预定了,如果预定的人是自己,再点击则取消,如果是别人预定的则不可以点击, 如果没有没预定点击则预定。 问题:权限管理如何实现的? 答: 权限的管理,是基于角色的访问控制。用户通过角色与权限进行关联。 一个用户拥有若干角色,每一个角色拥有若干权限,这样,就构造成“用户-角色-权限”的授权模型。 另外还有菜单和权限组,一个"菜单"下面有多个"权限组",一个"权限组"下面有多个"权限"。 流程: 当用户登录成功的时候设置session。判断有没有相应的权限,(查看,添加,删除,编辑)粒度到了按钮。 登录成功之后获取用户所有的权限信息并且保存到session中, 获取session中保存的信息,循环url进行正则匹配,如果匹配成功, 表示有权限;如果匹配不成功,说明没有权限。 整个认证的过程,有很多的视图,每一个都要进行认证,所以我们把认证写在了一个中间件里面。 当匹配成功的时候,把code保存在了request里面,方便以后判断有没有相应的权限。 并且让这些菜单分级显示默认展开的组内菜单,如果是非菜单,默认选中原菜单。 在设计表的时候设计了一个组内菜单(自关联),当是NULL的时候就说明是可以作为菜单的。 在初始化的时候,初始化权限信息,获取权限并放置到session中, 去session中获取到菜单相关信息,匹配url,生成菜单。在这里渲染页面的时候, 我们用了自定义的标签@register.includsion_tag("xxx.html") (用includsion_tag自动会读取到这个文件并把返回值在页面上渲染) “在母版中:{%menu_html request%} request是参数,记得要加上{% load rbac %} 问题:Thanos组件? 答: Thanos我们是模仿Django.admin,实现对表的url分配管理,解决大量的增删改查操作。 主要实现了动态生成url,每个url对应一个视图函数。每个注册类对应四个url,p快速实现增删改查。 搜索模糊匹配,组合搜索,批量操作,popup跳转,用Query_Dict实现原搜索条件的保留,自定制的分页器组件 而且内置了多个钩子函数,用于对程序功能进行扩展。 问题:技术点? 1.:通过ChangeList封装好多数据, change_listview列表页面代码太多,而却有好几个功能, 代码结构不清晰,修改代码麻烦,传到前端的东西过多, 封装后代码结构清晰,解耦性提高,便于维护,而且在changelist_view中只需要传入changelist对象即可。 2. 销售中公共资源:Q查询,3天 15天 销售接单后开始记录时间,如果三天 未跟进十五天未成单, 该客户进入公司的公共资源池,并且当前销售人员不能在公共资源池里面对该客户没有任何权限. 3. 使用yield实现(前端需要循环生成数据时),生成器函数,对数据进行加工处理__iter__和yield配合 组合搜索时用到先在ChangList中的get_combine_seach_filter()中返回row对象, 然后在FilterRow类中创建__iter__方法 yied生成每个组合搜索所对应的url 4. 获取Model类中的字段对应的对象, Foo.get_field(\'xx\')---------------------------------------------获取字段 model.UserInfo._meta.app_label----------------------------------获取当前app的名称 model.UserInfo._meta.model_name---------------------------------获取表名 model.UserInfo._meta.get_field(\'外键或多对多字段\').rel.to ------得到关联的model类 models.UserInfo._meta.get_field(\'name\')-------------------------根据字段名称,获取字段对象 models.UserInfo._meta.fields -----------------------------------获取类中所有的字段 models.UserInfo._meta._get_fields()-----------------------------获取类中所有的字段(包含反向关联的字段) models.UserInfo._meta.many_to_many------------------------------获取m2m字段 5. 模糊搜索功能 用到Q查询 根据show_search_form判断是否显示模糊搜索框,search_fileds=[]代表可以以什么搜索 6. Type创建类 主要用于动态生成modelForm时用到,在调查问卷和成绩录入是用到 Type中第一个参数是类名,第二个是继承的类,第三个是字典,其中我们操作主要是在字典中进行操作 成绩录入 7. 自动派单 原来在内存中实现,问题:重启和多进程时,都有问题。后面用redis。 8. 使用 list_diplay配置 list_display = [函数名,字段名。。。。] 9. reverse反向生成URL 根据url中name字段的值利用reverse生成, 如果有namespace则需要在最前面加上,并用“:””分隔,url中有参数还需要传参args=[] 反向生成url 10. 母版 模板的继承 模板中 {%block body%}{%endblock%} 子版中最顶行{% extends \'母版的路径\' %} {%block body%}{%endblock%} 11. ready方法定制起始文件 文件导入实现单例模式,stark.apps 12. inclusion_tag 在权限管理生成菜单和crm中生成popup数据时用到 当前所装饰的函数所得到的值,传到inclusion_tag中的html中使用,(这个html一般是一个子版),如果有模板需要用到这个html模板,则需要在当前模板中 {% inclusion_tag所修饰的函数名 参数一 参数二....%} 13. 中间件的使用 登录和权限管理用到, 需要继承MiddlewareMixin,有五个方法: process_request(self,request) process_response(self, request, response process_view(self, request, callback, callback_args, callback_kwargs) process_template_response(self,request,response) process_exception(self, request, exception) 14. importlib + getattr 在发送消息是用到,参考django源码可知,中间件也是采用这种方法 15. FilterOption,lambda表达式 目的是为了判断关联表的关联字段是不是主键还是其他字段 16. QueryDict原条件的保留 17. ModelForm可以自定义也可以使用satrkcofig中的type生成ModelForm。 18. 面向对象的 @property @classmethod 19. mark_safe在后台写的html传到前端能够正常显示,目的是为了防止xss攻击还有一种类似的方法, 直接在前端 {{aaa|safe}} 20. 组件中的装饰器,实现self.request = request 21. js自执行函数 (function(arg){ })(\'sf\') 22. 多继承 python3中都是新式类,遵从广度优先 python2中既有经典类和新式类,经典类是指当前类和父类都没有继承obj类, 新式类是指当前类或其父类只要有继承了obj类就算新式类 经典类遵循深度优先 新式类遵循广度优先 23. 批量导入,xlrd 24. redis连接池 25. 工厂模式 工厂模式实际上包含了3中设计模式,简单工厂,工厂和抽象工厂,关键点如下: 简单工厂通过构造时传入的标识来生产产品,不同产品都在同一个工厂中生产, 这种判断会随着产品的增加而增加,给扩展和维护带来麻烦。 工厂模式无法解决产品族和产品等级结构的问题 抽象工厂模式中,一个工厂生产多个产品,它们是一个产品族, 不同的产品族的产品派生于不同的抽象产品(或产品接口)。 1. 使用了接口来表达抽象工厂或者抽象产品,那么可以用抽象类吗?有何区别? 从功能上说,完全可以,甚至可以用接口来定义行为,用抽象类来抽象属性。 抽象类更加偏向于属性的抽象,而用接口更加偏向行为的规范与统一。 使用接口有更好的可扩展性和可维护性,更加灵活实现松散耦合,所以编程原则中有一条是针对接口编程而不是针对类编程。 2. 到底何时应该用工厂模式 根据具体业务需求。不要认为简单工厂是用switch case就觉得一无是处, 也不要觉得抽象工厂比较高大上就到处套。我们使用设计模式是为了解决问题而不是炫技, 所以根据三种工厂模式的特质,以及对未来扩展的预期,来确定使用哪种工厂模式。 3.说说你在项目中工厂模式的应用 crm项目中发送消息是用到,因为我们要同时发短信,微信,钉钉,和邮件信息, 我们把他包装在一个baseMessage中,使用时直接调用baseMessage的send()即可 三:Thanos组件:---------(一个月) Thanos我们是模仿Django.admin,实现对表的url分配管理,解决大量的增删改查操作。 主要实现了动态生成url,每个url对应一个视图函数。每个注册类对应四个url,p快速实现增删改查。 搜索模糊匹配,组合搜索,批量操作,popup跳转,用Query_Dict实现原搜索条件的保留,自定制的分页器组件 而且内置了多个钩子函数,用于对程序功能进行扩展。 问题:如何动态生成的url? 答:1:在Django项目启动时,扫描App项目下admin.py文件。创建一个admin.site()对象,site = AdminSite(), (其本质就是一个实例化对象,以后不管谁来使用都调用这个对象) 2:执行对象的register方法,将注册类添加到_register中。admin.site()类封装了_register。 3:再次调用admin.site()的urls属性。(url用了@property。类名就可执行)。 返回的是一个元祖,元祖有三个元素,self.get_urls(),admin,self.name. 第一个元素是一个函数返回的是一个列表,列表中值url()。 4:url是循环admin.site的_register(),中的注册类,放在列表中。为每个注册类,生成一级url, 其次调用类的样式对象下的get_url_func(self)函数,生成二级url,同时为每一个url创建别名,用于反向解析。 问题:如何实现模糊搜索的? 答:通过form提交数据数据,传到后端,用q查询实现查询。 问题:组合搜索如何实现? 答:获取字段,添加到filetr中。 问题:批量操作如何实现? 问题:popup跳转是如何实现的? 问题:Django-Admin源码流程? 1:在Django项目启动时,扫描每个APP项目下的admin.py文件的文件, 创建admin.site中的对象,site = AdminSite(),本质实例化一个对象,以后不管谁来调用都使用这个对象, 执行对象的register方法,目的将注册类添加到_register中, admin.site是一个对象(单例模式创建),其中封装了_register。 2:再次调用admin.site的urls属性,url用了@property。 返回了一个元组,元组有三个元素,self.get_urls(),\'admin\',self.name。 第一个元素是一个函数返回的是一个列表,列表中是url,是循环admin.site中的_register(ruanzhiste), 中的注册类,生成url,放在列表中。为每个注册类生成一级URL,其次调用类的样式对象下的get_url_func(self) 函数,生成二级URL,同时为每一个增删改查URL创建别名,用于反向解析。 1:一级别url和二级url的设计思路: site为类Starksite的单例实例化对象, site.urls 调用类下的urls方法, 而urls方法实际上是执行get_urls(self) 方法, 为每一个model首先生成一级URL,其次调用类的样式对象下的get_url_func(self) 函数,生成二级URL,同时为每一个增删改查URL创建别名,用于反向解析。 2:数据显示的思路: 首先类中定义一个 list_display = ["__str__"]的静态变量, 此静态变量在用于没有重写该变量时,则默认显示该对象的 def __str__(self) 方法的返回值。 将显示的数据以列表中套列表的形式在后台处理成功后,循环判断要显示的数据是一个字符串还是可执行函数, 若是可执行函数则执行函数后追加保存到列表中,若是字符串则直接追加保存在列表中。 (1):getattr(obj,str) 函数用于反射,实现通过对象的字段名名称找到该字段对应的值。 (2):要循环的字段实际上是new_list_display, 即对list_display进行扩展, 是默认显示的字段有复选框、对象名称、编辑、删除 (3):编辑和删除a标签的实现 把编辑和删除的a标签分别作为一个函数,使该函数的返回值为 mark_safe("<a>编辑</a>"), 在循环要显示的字段对象的数据时,执行该函数。 PS: mark_safe()函数实现前端safe相同的功能,使django不转移标签对象。 (4):表头的实现 根据list_display 循环显示表头,当遇到“__str__”时, 找到该该对象的“__str__“”方法的返回值,当遇到制定的字段名称, 如public时,则使用 self.model._meta.get_field(i).verbose_name获取该字段的verbose_name,追加到列表中。 PS: get_field(arg) 可以获取到model表中该arg字段对象,该对象可以调用字段的属性,如verbose_name。 (5):让字段作为a标签,实现跳转到编辑页面的实现 在循环显示数据的时候, 判断通过getattr()方法获取的字段值是否在list_display_link中,如果在,则使用mark_safe()使该value成为a标签。 (6)ModelForm 组件实现页面的增删改查 只需要为要进行form渲染的类继承ModelForm,即可。 (7):使用ModelForm实现显示中文错误信息 在样式类中创建一个model_form = None的静态变量, 在创建 ModelForm 类的函数 def default_modelform(self)中判断model_form的值, 值为None,则使用默认的modelform,如果为True,则使用用户stark.py中定义的样式类。 问题:分页如何实现的? 问题:保留页面原有搜索条件如何实现的? 答:request.GET是QueryDict类型的对象,默认不可设置,添加mutable=True时可设置, request.GET.urlencode()用于将k,v构造成url的字符串,打包,拼接,(params.urlencode()) 返回前端,添加到按钮后面,POST提交时,获取原来列表页面传过来的条件。 四:权限管理: 介绍: 公司的权限的管理,是基于角色的访问控制。 就是用户通过角色与权限进行关联。 简单地说,一个用户拥有若干角色,每一个角色拥有若干权限。 这样,就构造成“用户-角色-权限”的授权模型。另外还有菜单和权限组 一个"菜单"下面有多个"权限组",一个"权限组"下面有多个"权限"。 流程: a、编写登录:当用户登录成功的时候设置session b、判断有没有添加权限,有没有删除权限,有没有编辑权限,粒度到了按钮 具体实现:登录成功之后获取用户所有的权限信息并且保存到session中, 获取session中保存的信息,循环url进行正则匹配,如果匹配成功, 表示有权限;如果匹配不成功,说明没有权限。 这个认证的过程,如果有很多的视图,每一个都要进行认证,所以我们把认证写在了一个中间件里面。 当匹配成功的时候,把code保存在了request里面,方便以后判断有没有添加的权限,有没有删除,编辑的权限等。 C、生成菜单:并且让这些菜单分级显示默认展开的组内菜单是红色的。如果是非菜单,默认选中原菜单 具体实现:在设计表的时候设计了一个组内菜单(自关联),当是NULL的时候就说明是可以作为菜单的。 在初始化的时候,初始化权限信息,获取权限并放置到session中, 去session中获取到菜单相关信息, 匹配url,生成菜单。在这里渲染页面的时候, 我们用了自定义的标签@register.includsion_tag("xxx.html") (用includsion_tag自动会读取到这个文件并把返回值在页面上渲染) “在母版中:{%menu_html request%} request是参数,记得要加上{% load rbac %} 问题:总共几张表? 五个类,7张表: 用户表: 角色表: 权限表: 权限组表: 菜单表: "用户表"和"角色表"是多对多的关系(一个用户可以有多个角色,一个角色也可以属于多个用户) "角色表"和"权限表"是多对多的关系(一个角色可以有多个权限,一个权限可以对应多个角色) 一个"菜单"下面有多个"组" 一个"组"下面有多个"菜单" 一个"菜单"下面有多个"权限" 问题:功能? 答: - 粒度精确到按钮的权限控制。 - 基于用户角色权限控制 - 使用中间件实现权限检测和拦截 - 支持配置白名单 - 通过session实现权限信息的保存 - 使用多继承实现权限的应用 - 可扩展的动态配置菜单并且保留选中状态 问题:如何实现粒度精确到按钮的? 答:用户登录成功之后,获取所有权限,并且保存在session中。 获取session中的信息,循环进行正则匹配,如果匹配成功,说明有权限。 匹配不成功则无权限。 在做这个认证的时候,因为很多的视图函数都要进行认证,所以我是用中间件来处理的。 匹配成功的时候,把code[权限1,权限2]保存在request里面,循环判断有没有相应的权限。 五:保障系统 1、 保障系统(个人博客,报障系统) 项目背景:需要对公司内网运维工作绩效考评,以及对于内网运维工作效率的提高创建知识库的功能,所以才开发这么一个系统 周期:一个人,三周左右 有两大功能: 1、员工填写故障单 2、运维人员接单并处理,两小时内如果不回答,就成别人的了。记录个接单时间,比较接单时间和现在的时间是否相差两小时: 员工通过用户名密码登录到公司内网的系统,如果谁有问题就发送给内网运维。这些运维人员就会接收单,接单的时候会有提醒:比如微信提醒或者邮箱提醒等 #公司的员工发送问题,内网运维接单。 #接单的时候会有提醒:微信提醒/邮箱提醒/rtx提醒(就是企业用的QQ,腾讯通)等 3、常见知识库分类和筛选: 一些常用的问题写到知识库里面,自己去查也就解决了。 在这个知识库里面会有点赞,评论的功能。 常见问题还有分类 4、基于HighChar实现员工工作绩效的对比 5、提醒:微信提醒,邮箱提醒 外网运维:公司的所有网站不挂掉,都是外网运维干的 内网运维:比如你的电脑连不上网络,等是内网运维干的 六:会议室预定业务 公司人员增多,空间有限,会议室需要被预定才可使用,每个会议室从早上八点-晚上八点可以预定,一小时划分, 如果该会议室当前时间被预定了,如果预定的人是自己,再点击则取消,如果是别人预定的则不可以点击,如果没有没预定点击则预定。 七:调查问卷业务 调查问卷是为了调查学生对学校设备和讲师讲课的满意程度, 以及他没有什么困难等,获取他们的意见以方便我们进行改进, 问卷只能有班主任和教务总监发起,并明确的规定班级和起始日期和结束时间, 并且只有本班学生才能填写,调查问卷的题型有三种,填写内容(建议),单选,打分。 学生打分后点击提交即可完成调查问卷,学生提交后,在首页可显示答卷的人数。 写代码: Django: 1:MTV操作--------------数据库操作 Model操作: 1:创建表:qmodels.字段类型 from django.db import models Class 表名(models.Model):-------------------->继承models.Model 1:字段名 = models.CharField(verbose_name=\'微信ID\', max_length=32, null=True, blank=True) *字段名 = models.字段类型--->中文名称(verbose_name)----->最大字符数(max_length)----->是否可以为空----->关联字段可以不填 def __str__(self): return self.表名。 def __str__(self): return "{}({}期)".format(self.course.name, self.semester) 字段类型:(1)AutoField:--------"一个自动递增的整型字段",添加记录会自动增长,主要用于主键,models默认实现,ID。 (2)CharField:--------"字符串字段,单条输入",用于比较短的字符串,必须写max_length=32,12,24,36。 (3)TextField:--------"容量很大的文本字段"。 (4)BoolField:--------"布尔字段",True,False。用0和1表示。 (5)DataField:--------"日期字段", (6)TimeField:--------"时间字段", (7)DataTimeField:----"时间日期字段", (8)IntageField:------"整型字段", (9)FloatField:-------"浮点型字段",max_digits总位数,decimal_places。 (10)EmailField:------"Email字段", (11)ImageField:------"保存图片字段", (12)URLField:--------"url字段", (13)XMLFieldField----"XML字段", (14)FileField:------"个文件上传字段"。 要求一个必须有的参数: upload_to, 一个用于保存上载文件的本地文件系统路径。 (15)IPAddressField:--"字符串形式的IP地址", (如 “202.1241.30″)。 (16)PhoneNumberField:-"电话号"一个带有合法美国风格电话号码校验的 CharField(格式:XXX-XXX-XXXX)。 (17)SmallIntegerField-"单选字段与 IntegerField这个字段类型很类似",不同的是SmallInteger类型只能在一个确定的范围内(数据库依赖) "(verbose_name=\'性别\', choices=gender_choices, default=1) (18)PasswordField-----"密码字段", (19)SlugField---------"短标题",是一个新闻术语。一个slug只能包含字母、数字、下划线或者是连字符,通常用来作为短标签。通常它们是用来放在URL里的。 (20)BinaryField-------"二进制"用来存储原始二进制码的Field。 Field选项: (1)null :---------------------缺省设置为false.通常不将其用于字符型字段上。 (2)blank:---------------------该字段是否可以为空。如果为假,则必须有值。 (3)choices:-------------------一个用来选择值的2维元组。第一个值是实际存储的。 (4)to_field:-------------------关联字段 (5)verbose_name:---------------中文名称 (6)max_length:-----------------最大字符数 (7)default:--------------------默认 (8)hrlp_text:------------------中文提醒,admin模式下帮助文档 (9)primary_key:----------------主键 (10)unique:------------------------关联字段 (11)core:----------------------db_column,db_index 如果为真将为此字段创建索引 (12)limit_choices_to------------limit_choices_to={"department_id": 1002}) (13)related_name:---------------关系名,用于反向查询 (14)primary_key:---------------设置主键。 (15)max_digits,decimal_places---字符总长数,小数点后位数 关联字段: OneToOneField---------------一对一:class_obj = models.ForeignKey(verbose_name="班级", to="ClassList") ForeignKey------------------一对多:customer = models.OneToOneField(verbose_name=\'客户信息\', to=\'Customer\') ManyToManyField-------------多对多:teachers = models.ManyToManyField(verbose_name=\'任课老师\', to=\'UserInfo\', related_name=\'teach_classes\', limit_choices_to={"department_id__in": [1003, 1004, 1005]}) 存储在内存中: 1:choices--------------------"本地找":地段名_choices,适用于不经常改动的字段。 education_choices = [(1, \'重点大学\'), (2, \'普通本科\'), (3, \'独立院校\'), (4, \'民办本科\'), (5, \'大专\'), (6, \'民办专科\'), (7, \'高中\'), (8, \'其他\')] education = models.IntegerField(verbose_name=\'学历\', choices=education_choices, null=True, blank=True) 2:limit_choices_to-----------具体字段中的具体信息。 teachers = models.ManyToManyField(verbose_name=\'任课老师\', to=\'UserInfo\', related_name=\'teach_classes\', limit_choices_to={"department_id__in": [1003, 1004, 1005]}) consultant = models.ForeignKey(verbose_name="课程顾问", to=\'UserInfo\', limit_choices_to={\'department_id\': 1000},null=True, blank=True) 2:添加数据:create,save 方式1: publish_obj=Publish(name="人民出版社",city="北京",email="renMin@163.com") publish_obj.save() # 将数据保存到数据库 方式2: publish_obj=Publish.objects.create(name="人民出版社",city="北京",email="renMin@163.com") 3: 编辑数据:updata,save 方式一:updata models.Book.objects.filter(nid=id).update(title=title,author=author,publishDate=pubDate,price=price) 方式二: book_obj=models.Book.objects.filter(nid=id)[0] book_obj.title="金平" book_obj.save( 4: 删除:delete models.表名.object.filter(name="python").delete 6:查找: 普通查找: <1> all(): ------------------查询所有结果 #QuerySet <2> filter(**kwargs): -------它包含了与所给筛选条件相匹配的对象 #QuerySet <3> get(**kwargs): ----------返回与所给筛选条件相匹配的对象,返回结果只有一个,超过一个或者没有都会抛出错误。# model对象 <4> first():----------------返回第一条记录 # model对象 <5> last(): ----------------返回最后一条记录 # model对象 <6> distinct():--------------从返回结果中剔除重复纪录 #QuerySet <7> count():----------------返回数据库中匹配查询(QuerySet)的对象数量。 # int <8> values(*fie8ld):----------返回一个ValueQuerySet一个特殊的QuerySet,model的实例化对象,而是一个可迭代的字典序列#QuerySet <9> values_list(*field):-----它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列 #QuerySet <10> order_by(*field):--------对查询结果排序 #QuerySet <11> reverse():---------------对查询结果反向排序 #QuerySet <12> exclude(**kwargs):-------它包含了与所给筛选条件不匹配的对象 #QuerySet <13> exists():---------------如果QuerySet包含数据,就返回True,否则返回False __下划线查找: __lt=:----------小于 __gt=:----------大于 __gte=:---------等于 __in=:----------在 __is=:----------是 __contains=-----包含 __iscontains=---包含,不区分大小写 __reange=-------...到...之间 __Year=---------年 __moth=---------月 __day=----------日 __istartswith---以...开头 __iendswith-----以...结尾 __endswith------以...内容终止 Q()查询:与,或,非操作,将连个条件合并。 bookList=Book.objects.filter(Q(authors__name="yuan")|Q(publishDate__year=2017)).values_list("title") F()查询:计算操作+,-,*,/,AVg,Min,Max,Sum.,会进行数量的计算,变成整形 Book.objects.all().update(price=F("price")+30) 连表查询:aggregate() 一对多查询: 正向查询,按字段: 查询linux这本书的出版社的名字: models.Book.objects.all().filter(title="linux").values("publish__name") 反向查询:按表名: 查询人民出版社出版过的所有书籍的名字 models.Publish.objects.filter(name="人民出版社出版").values("book__title") 一对一查询: 正向查询,按字段: models.Author.objects.filter(name="egon).values("ad__tel") 反向查询:按表名: models.AuthorDetail.objects.filter(tel="151").values("author__name") 多对多: 正向查询,按字段: models.Book.objects.filter(title="python").values("authorList__name") [{},{},{},{}] 反向查询,按表名: models.Author.objects.filter(name="alex").values("book__price") 聚合查询:aggregate querySet().aggregate(聚合函数)------返回的是一个字典,不再是一个querySet Book.objects.all().aggregate(average_price=Avg(\'price\')) 分组查询:annotate() querySet().annotate() --- 返回的是querySet 统计每一个出版社中最便宜的书籍的价格 models.Book.objects.values("publish__name").annotate(Min("price")) 优化查询:only()----只取only中的对象,defer()---只取befer里面的对象,select_related()----速度最快,prefetch_related()速度快 only()----只取only中的对象, defer()---只取befer里面的对象, select_related()-------------速度最快,select_related主要针一对一和多对一关系进行优化 多外键查询:article=models.Article.objects.select_related("category").get(nid=1) 深层查询:article=models.Article.objects.select_related("blog").get(nid=1) prefetch_related()-----------速度快,对于多对多字段(ManyToManyField)和一对多字段,可以使用prefetch_related()来进行优化 article_obj=models.Article.objects.prefetch_related("tags").all() where / tables---------您可以使用where定义显式SQL WHERE子句 也许执行非显式连接。 您可以使用tables手动将表添加到SQL FROM子句。 where和tables都接受字符串列表。所有where参数均为“与”任何其他搜索条件。 queryResult=models.Article.objects.extra(where=[\'nid in (1,3) OR title like "py%" \',\'nid>2\']) select----------------可以让你在 SELECT 从句中添加其他字段信息,它应该是一个字典,存放着属性名到 SQL 从句的映射。 queryResult=models.Article.objects.extra(select={\'is_recent\': "create_time > \'2017-09-05\'"}) ------------------------------------------------------------- data_list = models.UserInfo.objects.all().only(\'name\',\'email\')--------只取出的是only里面的对象 for item in data_list: item.id item.name --------------------------------------------------------------- data_list = models.UserInfo.objects.all().defer(\'name\',\'email\') for item in data_list: item.id item.pwd 需求:获取2017-11-11所有预定信息:打印:用户名称,会议室名称, 预定时间段 解决方案一:11次 bk = models.Booking.objects.filter(date=2017-11-11) for item in bk: print(item.time_id, item.room.caption, item.user.user) 解决方案二:1次 select * from ... left join user ... join room bk = models.Booking.objects.filter(date=2017-11-11).select_related(\'user\',\'room\') for item in bk: print(item.time_id, item.room.caption, item.user.user) 解决方案三:3次 bk = models.Booking.objects.filter(date=2017-11-11).prefetch_related(\'user\',\'room\') for item in bk: print(item.time_id, item.room.caption, item.user.user) 2: Template操作:-------将变量巧妙的嵌入的html页面。 1:变量:{{}}。深度查询: 通过句点符号 .---------------句点符也可以用来引用对象的方法(无参数方法)。 2:过滤器: 语法:------{{obj|filter__name:param}}{{var|filter_name}} 1:default---------------------如果变量是false或者为空,使用指定的默认值。否则使用变量值。 ------------------------{{ value|default:"nothing" }} 2:length----------------------返回值得长度,对字符串和列表都起作用 ------------------------{{ value|length }} 3:filesizeformat--------------将格式化为一个"人类可读的尺寸"--如:\'13KB\',\'4.1MB\',\'102 bytes\'.......等 ------------------------{{ value|filesizeformat }} 4:data------------------------value=datetime.datetime.now() ------------------------{{ value|date:"Y-m-d" }} 5:truncatechars---------------字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾。参数:要截断的字符数 ------------------------{{ value|truncatechars:9 }} 6:safe-------------------------为了在Django中关闭HTML的自动转义有两种方式,如果是一个单独的变量我们可以通过过滤器“|safe”的方式告诉Django这段代码是安全的不必转义。 ------------------------{{ value|safe}} 7:slice------------------------如果 value="hello world",去掉前两个和后面一个 ------------------------{{ value|slice:"2:-1" }} 3: 标签: 标签比变量更加复杂:一些在输出中创建文本,一些通过循环或逻辑来控制流程,一些加载其后的变量将使用到的额外信息到模版中。 for:----------------遍历每一个元素 if else:------------会对一个变量求值,如果它的值是“True”(存在、不为空、且不是boolean类型的false值),对应的内容块会输出。 {% if num > 100 or num < 0 %} <p>无效</p> {% elif num > 80 and num < 100 %} <p>优秀</p> {% else %} <p>凑活吧</p> {% endif %} for ... empty-----------------------------------for 标签带有一个可选的{% empty %} 从句,以便在给出的组是空的或者没有被找到时,可以有所操作。 {% for person in person_list %} <p>{{ person.name }}</p> {% empty %} <p>sorry,no person here</p> {% endfor %} with------------------------------------使用一个简单地名字缓存一个复杂的变量,当你需要使用一个“昂贵的”方法(比如访问数据库)很多次的时候是非常有用的 {% with total=business.employees.count %} {{ total }} employee{{ total|pluralize }} {% endwith %} csrf_token------------------------------这个标签用于跨站请求伪造保护 4: 模板继承{% block xxxx%} {% endblock%} {%extends%} {% \'xxx.html\'%} {% endblock%} 模板继承(extend):Django模版引擎中最强大也是最复杂的部分就是模版继承了。它包含您站点中的全部元素,并且可以定义能够被子模版覆盖的 blocks 。 1:在模板中键一个盒子: {% block classIfo%} {% endblock%} 2:继承: {% extends "studentIfo.html"%} {%%} {% block classIfo %} <h>学生信息</h> <h3>{{ class_info }}</h3> {% endblock %} 3:Views:操作:---------视图函数 1:视图层之路由配置系统(view):url(r\'^admin/\', admin.site.urls), url(r\'^admin/$\', admin.site.urls), urlpatterns = [ url(正则表达式, views视图函数,参数,别名), ] 2: 路由分发:------------------url(r\'^app1/\',include(\'app1.urls\')), 3:请求:request: request.GET: -----------------------GET请求的数据 {} request.POST:-----------------------POST请求的数据 {} request.method:-------------------------------------------------请求方式:GET 或 POST request.POST.getlist("hobby") -----------------------------------请求某个键下多个值时 request.path :---------------------------------------------------请求路径 request.path:/index.html/23--------------------------------------请求url:http://127.0.0.1:8000/index.html/23?a=1 request.get_full_path()------------------------------------------请求url:http://127.0.0.1:8000/index.html/23?a=1 request.path_info------------------------------------------------获取当前的url request.COOKIE---------------------------------------------------获取cookie request.SESSION--------------------------------------------------获取session 4: 响应:response: return HttpResponse("")--------------------------------------------返回字符串实例 return render(request, \'login.html\', {"login_form": login_form})----返回变量 return redirect("/index/")-----------------------------------------重定向(**********) 6:CBV: def login(request): """登录""" if request.method == \'GET\': login_form = my_forms.LoginForm return render(request, \'login.html\', {"login_form": login_form}) else: login_form = my_forms.LoginForm(request.POST) if not login_form.is_valid(): return render(request, \'login.html\', {"login_form": login_form}) else: username = login_form.cleaned_data.get(\'username\') password = login_form.cleaned_data.get(\'password\') user_obj = rbac_models.User.objects.filter(username=username, password=password).first() if not user_obj: return redirect(reverse(\'login\')) else: init_permission(request, user_obj) department_id = user_obj.userinfo.department_id if department_id == 1000: return redirect(reverse(\'app03_customer_mine\')) elif department_id == 1001: pass return redirect(reverse(\'app03_courserecord_changelist\')) def logout(request): """注销""" request.session.flush() return redirect(reverse(\'login\')) def index(request): """主页""" return render(request, \'index.html\') 7:FBV: class LoginView(APIView): authentication_classes = [] # 这里不进行验证,因为是login页面 def post(self, request): \'\'\' 接收用户名和密码,跟数据库中的进行匹配,验证成功则再数据库中写入token :param request: :return: \'\'\' ret = {\'code\': 1000, \'msg\': None} user = request.data.get(\'username\') pwd = request.data.get(\'password\') obj = models.Account.objects.filter(username=user, password=pwd).first() if not obj: ret[\'code\'] = 1001 ret[\'msg\'] = \'用户名或密码错误\' response = JsonResponse(ret) response[\'Access-Control-Allow-Origin\'] = "*" return response # 创建随机字符串,当做token token_obj = models.UserAuthToken.objects.filter(user_id=obj.id).first() if not token_obj: import time import hashlib ctime = time.time() key = \'%s|%s\' % (user, ctime) m = hashlib.md5() m.update(key.encode(\'utf-8\')) token = m.hexdigest() # 保存数据 userToken = models.UserAuthToken() userToken.token = token userToken.user_id = obj.id userToken.save() ret[\'token\'] = token_obj.token ret[\'username\'] = obj.username response = JsonResponse(ret) response[\'Access-Control-Allow-Origin\'] = "*" return response def options(self, request, *args, **kwargs): response = HttpResponse() response[\'Access-Control-Allow-Origin\'] = \'*\' response[\'Access-Control-Allow-Headers\'] = \'*\' return response 4;Form class 类名(form)------------------------------------------继承form 字段 = fields.CharField---------------------------显示框 ChoiceField-------------------------------单选选下拉框 MultipleChoiceField(choices=)-------------多选下拉框 EmailField--------------------------------邮箱 规则required=True---------------------不能为空 error_messages={\'required\':\'用户名不能为空\'}--定义报错信息 widget=widgets.TextInput(attrs={\'placeholder\':\'用户名\',\'class\':\'form-control\'}--定义input框的类型,样式 username = fields.CharField( required=True, error_messages={\'required\':\'用户名不能为空\'}, widget=widgets.TextInput(attrs={\'placeholder\':\'用户名\',\'class\':\'form-control\'})) 5:ModelForm Class Myform(forms.ModelForm): Calss Meta: Model=models.user field=(\'name\',\'age\') fields = "__all__" 6:COOkie: 1:获取Cookie:----request.COOKIES.get("islogin",None) 2:设置Cookie:----obj.set_cookie("username", username) obj.set_cookie("islogin",True) #设置cookie值,注意这里的参数,一个是键,一个是值 obj.set_cookie("haiyan","344",20) #20代表过期时间 3、删除Cookie----obj.delete_cookie("cookie_key",path="/",domain=name) 7:Session: 1:设置session值----------request.session["session_name"]="admin" 2:获取session值----------session_name = request.session("session_name") 3:删除session值----------del request.session["session_name"] 删除一组键值对request.session.flush() 删除一条记录 4:检测是否操作session值--if "session_name" is request.session: 8:手写模板语法循环字典 Views.py d = {‘a’:’ddd’,’b’:’xxx’} return render(request,’index.html’,{‘d’:d}) .html {% for item in d%} {{ item }} {{ v }} {% endfor %} Mysql: 库操作: 查---------------------------show databases; 增---------------------------create database db1; 改---------------------------alter database db1 charset utf-b/gbk 删---------------------------drop database db1; 表操作: 查---------------------------show tables; 增---------------------------create tables t1(id int,name char(10),age int,价格 fioat(5,2)) 改---------------------------alter tables t1 add age int;------追加一个字段 ---------------------------alter table t1 modify name char(12); 删---------------------------drop table t1; 记录操作: 查-------------select * from t1; 增-------------insert into db1.t1 value(1,\'wsl1\',1,\'wsl2\') 改-------------updata t1 set name=\'sb\' where id=1;updata t1 set name=\'sb\' where name=\'alex\'; alter t1 modify age int not null; 删-------------deltete from t1 where id=1; 清空-----------truncate t1 数据类型: char(10)----------字符 varchar(10)-------字符,精准 float(5,2)--------浮点型 int()-------------整型 bigint()----------个数 tinyint()---------长整型 data()------------日期 Time()------------时间 Year()------------年 num()-------------枚举,单选,规定一个范围 set()-------------集合,多选,规定一个范围 约束: not null ---------------------不为空 unqiue ---------------------唯一 default ---------------------默认 primary_key-------------------设置主键 auto_increament---------------自增ID auto_increment_offset:--------偏移量 auto_increment_increment:-----步长 一对多:----------------------foreign key(t2_id) referencer t1(id)on update cascade on cascade delete; create table press(id int primary key auto_increment,name varchar(20)); create table book(id int primary key auto_increment, ectnamevarchar(20), press_id int not null, froeignkey(press_id) referencer press(id)on delete cascade on update cascade); 多对多:----------------------constraint fk_t1 foreign key(t2_id) referencer t1(id) on ... on ... create table author(id int primary key auto_increment,name varchar(20); create table author2book(id int not null unique auto_increment, author_id int not null, book_id int not null, constraint fk_author foreign key(author_id) references author(id) on delete cascadeon update cascade, constraint fk_book foreign key(book_id) references book(id) on delete cascadeon update cascade,primary key(author_id,book_id)); 一对一:----------------------foreign key (t2_id) referencer t1(id) on... on ...------被关联字段必须唯一 create table student(id int primary key auto_increment, class_name varchar(20) not null, customer_id int unique, #该字段一定要是唯一的 foreign key(customer_id) references customer(id)on delete cascadeon on update cascade); customer_id int unique, ---该字段一定要是唯一的 customer(id)---------------外键的字段一定要保证uniqueon 查询: from----------------------------------1.找到表:from where---------------------------------2.拿着where指定的约束条件,去文件/表中取出一条条记录 group by------------------------------3.将取出的一条条记录进行分组group by,如果没有group by,则整体作为一组 having--------------------------------4.将分组的结果进行having过滤 select--------------------------------5.执行select distinct------------------------------6.去重 order by------------------------------7.将结果按条件排序:order by limit---------------------------------8.限制结果的显示条数 seelct name from t1 where id = 1 group by num having age > 10 order by limt 1 distince; --------------------------------------------------------------------------------------- where(<,>,=,is,in,not,or,and,not between 10000 and 20000,name like \'%n%\',\'e__n\') --------------------------------------------------------------------------------------- 聚合:select max,min,sum,avg,count,group_concat(name) from t1; --------------------------------------------------------------------------------------- 正则: WHERE name = \'egon\'; WHERE name LIKE \'yua%\';--------开头 WHERE name REGEXP \'on$\';-------结尾 多表查询: 笛卡尔积:----------select * from t1,t2 where t1.id = t2.t2_id 连接查询: 全连接----------select * from t1 inner join t2 on t1.id = t2.t2_id. 左连接----------select * from t1 left join t2 on t1.id = t2.t2_id. 右连接----------select * from t1 right join t2 on t1.id = t2.t2_id. 子查询:------------select name from t1 where t2_id in(select id from t2 where name=\'sb\') is ,in ,notin,exists。 加索引: careate table t1(id int primary key auto_increament,age int(),性别 num(男,女),index s1(name)); careate index s1 on t1(name);-----------------------添加普通索引 create unique index s1 on t1(name);-----------------添加唯一索引 alter table t1 add primary_key(id);-----------------添加主键索引 create index name on s1(id,name);-------------------添加联合普通索引 创建视图:creat view 视图 as select * form teacher. 手写装饰器: def temper (func): def inner(*args,**kwargs): start = time.time() re = func(*args,**kwargs): print(start - time.time()) return re return inner @temper def t1(a): print(\'\') rest-framework返回Json数据: 1:单表: class UsersSerializer(serializers.Serializer): name = serializers.CharField() pwd = serializers.CharField() class UsersView(APIView): def get(self,request,*args,**kwargs): self.dispatch # ----------------------------------------方式一------------------------------------: # user_list = models.UserInfo.objects.all().values(\'name\',\'pwd\',\'group__id\',"group__title") # return Response(user_list) # ----------------------------------------方式二之多对象,many=True----------------------------: # user_list = models.UserInfo.objects.all() # ser = UsersSerializer(instance=user_list,many=True) # return Response(ser.data) # ---------------------------------------方式二之单对象,many=False------------------------------: user = models.UserInfo.objects.all().first() ser = UsersSerializer(instance=user, many=False) return Response(ser.data) 2:跨表:重写UsersSerializer,source可以点出来 class UsersSerializer(serializers.Serializer): name = serializers.CharField() pwd = serializers.CharField() #跨表 group_id = serializers.CharField() group_title = serializers.CharField( ="group.title") group_mu_name = serializers.CharField(source="group.mu.name") class UsersView(APIView): def get(self,request,*args,**kwargs): #方式一 # url_list = models.UserInfo.objects.all().values(\'name\',\'pwd\',\'group__id\',"group__title") # return Response(url_list) #方式二,多对象-------------------many=True #url_list = models.UserInfo.objects.all() #ser = UsersSerializer(instance=url_list,many=True) #return Response(ser.data) # 方式二,单对象-------------------many=Flase user = models.UserInfo.objects.all().first() ser = UsersSerializer(instance=user, many=False) return Response(ser.data) 3:复杂序列化: 解决方式一:------------自定义类MyCharField:--------->r = MyCharField(source="roles.all") from django.shortcuts import render from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.authentication import BasicAuthentication from rest_framework.versioning import QueryParameterVersioning,URLPathVersioning,HostNameVersioning from rest_framework.renderers import BrowsableAPIRenderer,JSONRenderer from rest_framework.parsers import JSONParser,FormParser from rest_framework.request import Request from rest_framework import serializers from . import models # Create your views here. class MyCharField(serializers.CharField): def to_representation(self, value): data_lsit = [] for row in value: data_lsit.append(row.id) data_lsit.append(row.name) return data_lsit class UsersSerializer(serializers.Serializer): name = serializers.CharField() pwd = serializers.CharField() #跨表 group_id = serializers.CharField() group_title = serializers.CharField(source="group.title") group_mu_name = serializers.CharField(source="group.mu.name") role = serializers.CharField(source="roles.all") #复杂序列化 r = MyCharField(source="roles.all") class UsersView(APIView): def get(self,request,*args,**kwargs): #方式一 # url_list = models.UserInfo.objects.all().values(\'name\',\'pwd\',\'group__id\',"group__title") # return Response(url_list) #方式二,多对象-------------------many=True url_list = models.UserInfo.objects.all() ser = UsersSerializer(instance=url_list,many=True) return Response(ser.data) 解决方式二:--------------------------------role = serializers.ListField(child=MyCharField(),source="roles.all") from django.shortcuts import render from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.authentication import BasicAuthentication from rest_framework.versioning import QueryParameterVersioning,URLPathVersioning,HostNameVersioning from rest_framework.renderers import BrowsableAPIRenderer,JSONRenderer from rest_framework.parsers import JSONParser,FormParser from rest_framework.request import Request from rest_framework import serializers from . import models class MyCharField(serializers.CharField): def to_representation(self, value): return {\'id\':value.id,\'name\':value.name} class UsersSerializer(serializers.Serializer): #基本序列化 name = serializers.CharField() pwd = serializers.CharField() #跨表 group_id = serializers.CharField() group_title = serializers.CharField(source="group.title") group_mu_name = serializers.CharField(source="group.mu.name") role = serializers.CharField(source="roles.all") #复杂序列化:解决方式1 # r = MyCharField(source="roles.all") #复杂序列化:解决方式2 role = serializers.ListField(child=MyCharField(),source="roles.all") class UsersView(APIView): def get(self,request,*args,**kwargs): #方式一 # url_list = models.UserInfo.objects.all().values(\'name\',\'pwd\',\'group__id\',"group__title") # return Response(url_list) #方式二,多对象-------------------many=True url_list = models.UserInfo.objects.all() ser = UsersSerializer(instance=url_list,many=True) return Response(ser.data) 解决方式三:--------------------------------role = serializers.SerializerMethodField()------自定义钩子get_当前字段名 from django.shortcuts import render from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.authentication import BasicAuthentication from rest_framework.versioning import QueryParameterVersioning,URLPathVersioning,HostNameVersioning from rest_framework.renderers import BrowsableAPIRenderer,JSONRenderer from rest_framework.parsers import JSONParser,FormParser from rest_framework.request import Request from rest_framework import serializers from . import models class UsersSerializer(serializers.Serializer): #基本序列化 name = serializers.CharField() pwd = serializers.CharField() #跨表 group_id = serializers.CharField() group_title = serializers.CharField(source="group.title") group_mu_name = serializers.CharField(source="group.mu.name") role = serializers.CharField(source="roles.all") #复杂序列化:解决方式1 # r = MyCharField(source="roles.all") #复杂序列化:解决方式2 # role = serializers.ListField(child=MyCharField(),source="roles.all") # 复杂序列化:解决方式2----------推荐使用 role = serializers.SerializerMethodField() def get_role(self, obj): # role_list=obj.roles.all() role_list = obj.roles.filter(id__gt=1) data_list = [] for row in role_list: data_list.append({\'id\': row.id, \'name\': row.name}) return data_list class UsersView(APIView): def get(self,request,*args,**kwargs): #方式一 # url_list = models.UserInfo.objects.all().values(\'name\',\'pwd\',\'group__id\',"group__title") # return Response(url_list) #方式二,多对象-------------------many=True url_list = models.UserInfo.objects.all() ser = UsersSerializer(instance=url_list,many=True) return Response(ser.data) 4:基于Model实现序列化: class UsersSerializer(serializers.ModelSerializer): """ 基于model:序列化 """ x1 = serializers.CharField(source=\'group.id\') #自定义字段 class Meta: model = models.UserInfo # fields = "__all__" #所有字段 fields = [\'name\', \'pwd\',\'group\'] #具体字段 depth = 2 #查询深度 class UsersView(APIView): def get(self,request,*args,**kwargs): #方式一 # url_list = models.UserInfo.objects.all().values(\'name\',\'pwd\',\'group__id\',"group__title") # return Response(url_list) #方式二,多对象-------------------many=True url_list = models.UserInfo.objects.all() ser = UsersSerializer(instance=url_list,many=True) return Response(ser.data) 5:生成url:------------------------------------------------group = serializers.HyperlinkedIdentityField(view_name=\'detail\') app05.urls.py from django.conf.urls import url from app05 import views urlpatterns = [ url(r\'^users/\', views.UsersView.as_view(),name=\'u\'), url(r\'xxx/(?P<pk>\d+)/\',views.UsersView.as_view(), name=\'detail\'), ] views.py class UsersSerializer(serializers.ModelSerializer): group = serializers.HyperlinkedIdentityField(view_name=\'detail\') class Meta: model = models.UserInfo fields = "__all__" fields = [\'name\', \'pwd\',\'group\'] depth = 1 class UsersView(APIView): def get(self,request,*args,**kwargs): self.dispatch # 方式一: # user_list = models.UserInfo.objects.all().values(\'name\',\'pwd\',\'group__id\',"group__title") # return Response(user_list) # 方式二之多对象 user_list = models.UserInfo.objects.all() # [obj1,obj2,obj3] ser = UsersSerializer(instance=user_list,many=True,context={\'request\':request}) return Response(ser.data) 6:全部生成url:---------class UsersSerializer(serializers.HyperlinkedModelSerializer): views.py class UsersSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = models.UserInfo fields = "__all__" # fields = "__all__" # fields = [\'id\',\'name\',\'pwd\'] exclude=[\'roles\'] #排除 class UsersView(APIView): def get(self,request,*args,**kwargs): self.dispatch # 方式一: # user_list = models.UserInfo.objects.all().values(\'name\',\'pwd\',\'group__id\',"group__title") # return Response(user_list) # 方式二之多对象 user_list = models.UserInfo.objects.all() # [obj1,obj2,obj3] ser = UsersSerializer(instance=user_list,many=True,context={\'request\':request}) return Response(ser.data) 算法:就是一个计算的过程,解决问题的方法。时间复杂度:用来估算时间的一个式子。空间复杂度:用来评估算法占用内存大小的式子 1:冒泡排序:列表中两个相邻的数做比较,如果前面的比后面的大,就互换位置。 def bubble_sort(li): for i in range(len(li) - 1): # i 表示趟数 # 第 i 趟时: 无序区:(0,len(li) - i) change = False for j in range(0, len(li) - i - 1): if li[j] > li[j+1]: li[j], li[j+1] = li[j+1], li[j] change = True if not change: return 2: 直接插入排序:列表被分为有序区和无序区,最初有序区只有一个元素,每次从无序区选择一个元素插入到有序区,直到无序区变空。 def insert_sort(li): for i in range(1, len(li)): # i 表示无序区第一个数 tmp = li[i] # 摸到的牌 j = i - 1 # j 指向有序区最后位置 while li[j] > tmp and j >= 0: #循环终止条件: 1. li[j] <= tmp; 2. j == -1 li[j+1] = li[j] j -= 1 li[j+1] = tmp li = list(range(10000)) insert_sort(li)#insert_sort running time: 0.003001689910888672 secs. print(li)#0~9999已排好序 3:选择插入排序:循环遍历,记录最小的数,放在第一个位置,再遍历一趟,取出剩余元素中记录的最小数,继续放置 def select_sort(li): for i in range(len(li) - 1): # i 表示趟数,也表示无序区开始的位置 min_loc = i # 最小数的位置 for j in range(i + 1, len(li) - 1):#去除已经归为的最小数 if li[j] < li[min_loc]: min_loc = j li[i], li[min_loc] = li[min_loc], li[i] li = list(range(10000)) select_sort(li)#select_sort running time: 9.220226049423218 secs. print(li)#0~9999已排好序 4:快速排序:取出一个元素P,使P归位,列表被P分成两半,左边比P小,右边比P大。关键点递归,归位。 def partition(li,left,right):-------------------------归位函数 tmp=li[left]--------------------------------------取出P while left < right: while left < right and li[right] >= tmp: right -= 1--------------------------------从右边找比p小的数,填充到p的位置 li[left]=li[right] while left < right and li[left] <= tmp: left += 1---------------------------------从左边找比p小的数,放到右边位置 li[right] = li[left] 5:二分查找? 从有序列表的候选区data[0:n]开始,通过对待查找的值与候选区中间值的比较,可以使候选区减少一半 二分查找: 在一段数字内,找到中间值,判断要找的值和中间值大小的比较。 如果中间值大一些,则在中间值的左侧区域继续按照上述方式查找。 如果中间值小一些,则在中间值的右侧区域继续按照上述方式查 找。 直到找到我们希望的数字。 @source_data:数据集 @binary_num:要查找的数 @mid:中间数的键值 def binary_search(source_data,search_num): #传入数据集计算中间数键值 mid = int(len(source_data)/2) #确认数据集的数据个数大于1 if int(len(source_data)) >1: #判断要找的数与中间数比较,如果中间数大于要找的数,要找的数在中间数左边 if source_data[mid] > search_num: # 显示数据的大概位置 print("search_num in left [%s]" % source_data[mid]) #重复判断查找 binary_search(source_data[:mid], search_num) #如果中间数小于要找的数,要找的数在中间数右边 elif source_data[mid] < search_num: #显示数据的大概位置 print("search_num in right [%s]" % source_data[mid]) # 重复判断查找 binary_search(source_data[mid:], search_num) else: # 中间数正好等于要找的数,则打印出来 print("find search_num",source_data[mid]) else: #如果等于1输出提示信息 print("can\'t find search_num") if __name__== \'__main__\': data = [1,2,3,4,5,6,7] print(data) binary_search(data,4) 数据结构: 答:程序 = 数据结构 + 算法 数据结构:就是设计数据以何种方式组织存储在计算机中。 常见的数据结构按逻辑可分为:线性结构,树结构和图结构。 线性结构就是:数据结构中的元素存在一对一的相互关系。 树结构就是:数据结构中的元素存在一对多的相互关系。 图结构就是:数据结构中的元素存在多对多的相互关系。 数据结构类型常见的有:列表,栈 列表:的话在其他语言中叫数组,是一种基本的数据结构。 栈:是一个数据集合,可以理解为只能在一端进行插入删除等操作。先进后出。进栈(压栈):push,出栈:pop取栈顶:gettop 队列:是一个数据集合,只能在一端进行插入,另一端进行删除。先进先出。 链表:链表中的每个元素都是一个对象,每个对象称为一个节点,包含数据域key和下一个节点指针next。 通过各个节点之间的相互链接,串联成一个列表。 哈希表:哈希表是一种线性的存储结构。由一个数组和一个哈希函数组成,哈希函数将元素做为自变量,返回元素的存储下标。 二叉树:将二叉树的节点定义为一个对象,节点之间通过类似链表的的链接方式连接。 列表查找: 顺序查找: li=[1,2,3,4,5,6,7,8,9,10] def linear_search(data_set,value): for i in data_set: if data_set[i]==value: return i return the_index = linear_search(li,3) print(the_index) 二分查找 li=[1,2,3,4,5,6,7,8,9,10] def bin_search(data_set, value): low = 0 high = len(data_set) - 1 while low <= high: mid = (low+high)//2 if data_set[mid] == value: return mid elif data_set[mid] > value: high = mid - 1 else: low = mid + 1 bin_index=bin_search(li,3) print(bin_index) #O(logn)可以发现二分查找比顺序查找要更快 二分法使用递归查找 def bin_search_rec(data_set,value,low,high): if low <= high: mid=(low+high) // 2 if data_set[mid] == value: return mid elif data_set[mid] > value: return bin_search_rec(data_set,value,low,mid-1) else: return bin_search_rec(data_set,value,high,mid+1) else:return None li=[1,2,3,4,5,6,7,8,9,10] a=bin_search_rec(li,3,1,10) print(a)#2 列表去重: 1:方式一 l1 = [] for i in list: if i not in l1: l1.append(i) 2: 方式二 list(set(list1)) 列表排序: 1:list1.sorted() 2: list1.sort() 字典排序: sorted(li ,key=lamdba k : k[\'day\']) 手写递归斐波那契数列 def fab(): if n<=1: return 1 else: return fab(n-1)+fab(n-2) print([fab(i) for I in range(5)]) 单例: 1:模块单例 Class Singletion(object): def foo(self): pass Singletion = Singletion() from xxx inport Singletion 2:装饰器单例 def Singletion(ls,*args,**kwargs): instance = {} def _sinletion(*args,**kwargs): if ls not in instance: instalce[ls] = ls[*args,**kwargs] return intance[ls] return _sinletion @Singletion class A: pass 3:静态变量 Class Singletion(oject): def __new__(ls,a): if not hasattr(ls,\'_instance_\'): ls._instance_ = object.__new__(ls) return ls._instance_ def __init__(self,a): self.a = a def a(self): print(self.a) a = Singletion("a") 4:元类单例 发AJAX请求: 1:ajax的参数 (1):url:"" (2):type:"GET"-----------请求的方式 (3):data当前ajax请求要携带的数据,是一个json的object对象,ajax方法就会默认地把它编码成某种格 (4):processData声明当前的data数据是否进行转码或预处理,默认为true,即预处理;if为false, (5):contentType---------发送数据的格式,"urlencoded"(默认)-------------客户端给服务端发送数据的格式。 (6):success:function回调函数。 (7):error:function回调函数(错误的情况下) function testData() { $.ajax("/test",{ //此时的data是一个json形式的对象 data:{ a:1, b:2 } }); 原生SQL(即将毙掉) - extra - raw - connection - models.User.objects.all().using(\'default\') 基本数据类型: 字符串(srt): 1:print(l.strip()) -----------------1:strip-----------------去除前后空格lstrip(左),strip(右)1 2:print(l.startswith(\'W\'))----------2:startswith------------判断是否以\'....\',开头 3:print(l.endswith(\'g\'))------------3:endswith--------------判断是否以\'....\',结尾 4:print(l.upper())------------------4:upper-----------------变大写 5:print(l.lower())------------------5:lower-----------------变小写 6:print(l.swapcase())---------------6:swapcase--------------大小写转换 7:print(l.split(\'s\'))---------------7:split-----------------以\'....\',切分。 8:print(l.index(\'s\'))---------------8:index-----------------求\'....\'的索引,若找不到就报错。 9:print(l.find(\'p\'))----------------9:find------------------求\'....\'的索引,找不到返回负。 10:print(l.count(\'a\'))--------------10:count----------------统计\'....\'次数。 11:print(l.replace(\'shang\',\'S\'))----11----------------------替换 12:print(len(l))--------------------12:len()----------------求长度 13:print(\'+\'.join(l))---------------13:join-----------------用\'....\'连接。 14:print(l.center(20,\'*\'))----------14:center---------------填充空白 15:print(l.format())----------------15:format---------------取出字符串的字符 列表(list): l = [\'1\',2,3,\'wang\',\'shang\',\'long\'] 1:l.append(123)---------------------1:append------------追加 2:l.insert(4,\'hwuhuhuhuhwuhuuw\')----2:insert------------增加(按索引) 3:l.extend(\'王,尚,long\')------------3:extend------------增加(追加多个,字符) 4:l.remove(\'1\')---------------------4:remove------------删除(按元素删) 5:l.pop(4)--------------------------5:opo---------------删除(按索引) 6:del l[0]--------------------------6:del---------------删除(按索引) a = ["q","w","e","r","t","y"] 7:print(a[0:3])-----------------------------------------0到3不包括3 8:print(a[0::2])----------------------------------------面试 9:print(a[0:3:2])---------------------------------------0到3,步长为2 10:print(a[-1])-----------------------------------------面试,最后一个 11:print(a[-1:])----------------------------------------结果是列表 12:print(a[3:1:-1])-------------------------------------面试(倒叙) 字典(dict): lis = [\'a\',\'gfg\',[\'qwe\',20,[\'tre\',\'1\',3],89]] 0:根据索引找到tre元素,并改成TRE lis[2][2][0] = \'TRE\' lis[2][2][0] = lis[2][2][0].upper() 1:找到数字3通过相加的方式让其加上97,变成100。 lis[2][2][2] += 97 2:找到字符串\'1\',通过相加方式变成字符串\'101\'。 lis[2][2][1]+=\'01\' 3:找到数字20,通过数字相加和字符转换方式使其变成\'120\'. lis = [\'a\',\'gfg\',[\'qwe\',20,[\'tre\',\'1\',3],89]] lis[2][1]=str(lis[2][1]+100) tu = (\'alex\',[11,22,{\'k1\':\'v1\',\'k2\':[\'name\',\'age\'],\'k3\':(1,2,3)},32]) 1,tu变量第一个元素能否被修改? 不能 2,k2对应的值是什么类型?能否被修改,如果可以添加一个\'jx\' tu[1][2][\'k2\'].append(\'jx\') 3,k3对应的值是什么类型?能否被修改,如果可以添加一个\'jx\' 元祖不能修改 字典的操作 字典是python中唯一的映射类型,采用键值对(key-value)的形式存储数据。 python对key进行哈希函数运算,根据计算的结果决定value的存储地址, 所以字典是无序存储的,且key必须是可哈希的。 可哈希表示key必须是不可变类型,如:数字、字符串、元组。 dic = {"name":"jin","age":18,"sex":"male"} dic2 = {"name":"alex","weight":75} 1:for key in dic: print(key) 2:for item in dic.items(): print(item) 3:for key,value in dic.items(): print(key,value) 增 dic[[1,2,3]] = ["a","b","c"] print(dic) setdefault 在字典中添加键值对,如果只有键那对应的值是none,但是如果原字典中存在设置的键值对, 则他不会更改或者覆盖。 1:dic.setdefault(\'k\',\'v\') print(dic)-----------{\'age\': 18, \'name\': \'jin\', \'sex\': \'male\', \'k\': \'v\'} 删 dic_pop = dic.pop("a",\'无key默认返回值\') pop根据key删除键值对,并返回对应的值,如果没有key则返回默认返回值 print(dic_pop) del dic["name"] # 没有返回值。 print(dic) dic_pop1 = dic.popitem() # 随机删除字典中的某个键值对,将删除的键值对以元祖的形式返回 print(dic_pop1) # (\'name\',\'jin\') dic_clear = dic.clear() # 清空字典 print(dic,dic_clear) # {} None 改 dic2.update(dic) # 将dic所有的键值对覆盖添加(相同的覆盖,没有的添加)到dic2中 print(dic2) 查 value1 = dic["name"] # 没有会报错 print(value1) value2 = dic.get("djffdsafg","默认返回值") # 没有可以返回设定的返回值 print(value2) 例题: dic = {\'k1\':\'v1\',\'k2\':[\'name\',\'age\'],\'k3\':{\'a\':\'b\',"c":\'d\'}} 1找出字典中所有的key值,values值,key,values值 print(dic.keys()) print(dic.values()) print(dic.items())m 2,添加一个键值对,\'k4\':\'v4" dic.setdefault(\'k4\',\'v4\') print(dic) dic.update({\'k4\':\'v4\'}) print(dic) 3,在k3对应的值里面增加一个键值对,\'aa\':\'bb\',并输出k3对应的值的键值对 dic[\'k3\'].update({\'aa\':\'bb\'}) print(dic[\'k3\']) 4,请在\'k2\'对应的值第一个位置加入一个\'sex\',并输出该值 dic[\'k2\'].insert(1,\'sex\') print(dic) 5,将k2对应的值增加多个值,这些值是这个列表[1,2,3,4,5]的每一个元素 dic[\'k2\'].extend([1,2,3,4,5]) print(dic) av_catalog = { "欧美":{ "www.youporn.com": ["很多免费的,世界最大的","质量一般"], "www.pornhub.com": ["很多免费的,也很大","质量比yourporn高点"], "letmedothistoyou.com": ["多是自拍,高质量图片很多","资源不多,更新慢"], "x-art.com":["质量很高,真的很高","全部收费,屌比请绕过"] }, "日韩":{ "tokyo-hot":["质量怎样不清楚,个人已经不喜欢日韩范了","听说是收费的"] }, "大陆":{ "1024":["全部免费,真好,好人一生平安","服务器在国外,慢"] } } 1:将"资源不多,更新慢" 变成 "还是自拍好呀" av_catalog["欧美"]["letmedothistoyou.com"][1]="还是自拍好呀" print(av_catalog) 2:将"服务器在国外,慢"追加一句:\'好多找不到了\' av_catalog["大陆"]["1024"].append("好多找不到了") print(av_catalog) 3:列表 lis = [\'a\',\'b\',\'c\'] 创建一个字典(用上lis列表),让其变成dic = {\'a\':1,\'b\':1,\'c\':1} w = {} lis = [\'a\',\'b\',\'c\'] for i in lis: w.setdefault(i,1) print(w) 4:列表 li = [1,2,3,4,5,6,7,8,9],将所有大于6的值保存在字典第一个key中,将所有小于6的值保存在字典第二个key中 如:{\'k1\':所有大于6的值;\'k2\':所有小于6的值} li = [1,2,3,4,5,6,7,8,9] w = [] s = [] l = {\'k1\':w,\'k2\':s} for i in li: if i > 6: w.append(i) else: s.append(i) print(l) a = {\'k1\':[],\'k2\':[]} for i in li: if i >6: a[\'k1\'].append(i) else: a[\'k2\'].append(i) print(a) li = [1,2,3,4,5,6,7,8,9]将索引为偶数位对应的值相加并添加到li列表的最后面。 li = [1,2,3,4,5,6,7,8,9] cont = 0 for i in li: if i % 2 ==0: cont+=i li.append(cont) print(li) 1----------------------------------- a = 0 for i in range(100,300): print(i) if i%3 ==0 and i%7== 0: a += i print(a) 2----------------------------------- def func(s): shuzi = 0 daxie = 0 xiaoxie = 0 for i in s: if i.isdigit(): shuzi += 1 elif i.islower(): daxie += 1 else: i.isupper() xiaoxie += 1 return shuzi,daxie,xiaoxie a = func(\'zzsSDf32\') print(a) 3----------------------------------------------- l1 = [11,22,33] l2 = [22,33,44] ss = list(set(l1).intersection(set(l2))) print(ss) 4----------------------------------------------- s = ‘老男人‘ a = (bytes(s,encoding=‘utf-8‘)) print(a) #将字符串转换为字节 5---------------------------------------------- print(abs(23)) #绝对值 6---------------------------------------------- 浅拷贝,只拷贝第一层数据, 深拷贝除了最内层全部拷贝 print(bool(‘‘)) print(bool([])) print(bool({})) print(bool(0)) print(bool(None)) 7-------------------------------------------- all() 当所有的元素都为真才为真,有一个是假则为假 any() 只有一个真则为真, 8---------------------------------------------- print(bin(12)) #二进制 print(oct(12)) #八进制 print(hex(12)) #十六进制 9--------------------------------------------- li =[1,2,3] print(help(li)) #显示所有变量或类的介绍 print(dir(li)) #查看变量或者类型,可以使用的函数,属性,如果是类必须用引号 print(type(li)) #查看类型 print(id(li)) # 查看id地址 10------------------------------------------- name = ‘xin‘ if 1 == 1 else ‘kai‘ print(name) #三元运算的表达式 name = ‘xin‘ if 1 != 1 else ‘kai‘ print(name) 11------------------------------------------ func = lambda a:a+100 #lamda表达式 xin =func(82) print(xin) 12------------------------------------------- l1 = [‘alex‘,22,33,44,55] l2 = [‘is‘,22,33,44,55] l3 = [‘good‘,22,33,44,55] l4 = [‘guy‘,22,33,44,55] l5 = zip(l1,l2,l3,l4) for i in l5: print(i) print(i,type) print(‘_‘.join(i)) break #zip实现元祖转换字符串功能 def f1(d,a1,a2): 斐波那契数列算法 明显算不出来乘法的值 print(d) if d == 8: return d,a1,a2 a3 = a1 * a2 r = f1(d+1,a2,a3) return r s = f1(0,1,2) print(s) 二分查找: 面试常问: 1:self? 答:我们调用一个方法时,自动将自己作为第一参数传入函数中, 命名为self,这样就可以不去区分函数的名字,自己就是self,这个变量就是对象自身。 2:with方法? 答:with后面为一个表达式,表达式返回的是一个上下文管理器对象,我的理解就是表达式的返回结果。 例如:with open打开一个文件。 3:super方法? 答:在子类中使用父类的方法。 4:map,Filter,reduce等函数的使用? 答:1:Map:主要包括两个参数,函数和列表。将函数的结果以列表的形式返回。 会将一个函数映射到一个输入列表的所有元素。 规范:map(function_to_apply,list_of_inputs). 大多数时候,我们要把列表中的所有元素一个个的传递给一个函数,并收集输出。 2:Filter:包括两个参数function,list。根据function的返回值是True, 来过滤list的参数中的项,最后返回结果。 过滤列表中的元素,并且返回一个由所有符合要求的元素构成的列表。符合要求 即函数映射到该元素时返回值为True. 3: Reduce:从列表中取出头两个元素并传递到一个二元函数中去, 求出值,再添加到序列中继续循环下一个值,直到最后一个值。 当需要对一个列表进行计算并返回结果时,reduce是一个很有用的函数。 例:当你需要计算一个整数列表的乘积时。通常在python中,你可能会使用基本的for循环来完成任务。 5:next和__next__的区别? 答:在使用迭代器时会使用到,在循环中,每次循环都会调用next方法。 python3以后就用的next. 6:matecclass,seelctclass? 7:可变数据类型,不可变数据类型,为什么可变? 答: 可变:列表list和字典dict; 不可变:整型int、浮点型float、字符串型string和元组tuple。 比如想 x = 1,y = 1 在存储的时候1用的同一个内容地址,指向同一个数据段。 可变数据类型每个数据都是单独的内存地址。 8:推导式? 9:F Q查询? F:专门取对象中某列值的操作.通常情况下我们在更新数据时需要先从数据库里将原数据取出后方在内存里,然后编辑某些属性,最后提交。例如点赞+1. Q:对对象的复杂查询.对关键字参数进行封装,从而更好地应用多个查询。可以组合使用 &(and),|(or),~(not)操作符,当一个操作符是用于两个Q的对象,它产生一个新的Q对象。 10:优化查询? 答:only----------------------------只取定义的字段。 recv----------------------------取反向字段 select_related()----------------方法把关联的对象都查询出来放入对象中 prefetch_related()--------------针对多对多字段进行查询 11:进程与线程的区别? 1:线程共享创建它的进程的地址空间;进程有自己的地址空间。 2: 线程直接访问进程的数据段;进程拥有父进程的数据段的自身副本。 3: 线程可以直接与其他线程的通信;进程必须使用进程与兄弟姐妹进程通信。 4: 很容易创建新线程,需要再次申请内存地址;新进程需要重复父进程。 5: 线程可以对相同进程的线程进行相当的控制;进程只能对子进程进行控制。 6: 对主线程的更改等操作可能影响进程的其他线程的行为;对父进程的更改不会影响子进程。 12: 深浅拷贝 1. copy.copy 浅拷贝 只拷贝父对象,不会拷贝对象的内部的子对象。浅拷贝只是拷贝了一系列引用,当我们在拷贝出来的对象对 可修改的数据类型进行修改的时候,并没有改变引用,所以会影响原对象。而对不可修改的对象进行修改的时候,则是新建了对象, 刷新了引用,所以和原对象的引用不同,结果也就和原对象不同。 2. copy.deepcopy 深拷贝 拷贝对象及其子对象。深拷贝就是将里面引用的对象重新创建了一遍并生成了一个新的一系列引用。但是对于字符串、 数字等不可修改的对象来说,重新创建一份似乎有点浪费内存,所以等到要修改的时候再新建对象,刷新引用。这样能达到节省内存的目的。 13:命中索引? 14:谈谈你对 django rest framework框架的认识? - 路由, - 可以通过as_view传参数,根据请求方式不同执行相应的方法 - 可以在url中设置一个结尾,类似于: .json - 视图, - 帮助开发者提供了一些类,并在类中提供了多个方法以供我们使用。 - 版本, - 在url中设置version参数,用户请求时候传入参数。在request.version中获取版本,根据版本不同做不同处理 - 认证, - 写一个类并注册到认证类,在类的的authticate方法中编写认证逻辑。 - 认证成功(user,auth) - raise AuthticateFaild(....) - None - 权限 - 写一个类并注册到权限类,在类的的has_permission方法中编写认证逻辑。 - True - False - 频率限制 - 写一个类并注册到频率类,在类的的 allow_request/wait 方法中编写认证逻辑。 allow_request - True - False 如果返回False,那么就要执行wait - 解析器, - 根据ContentType请求头,选择不同解析器对 请求体中的数据进行解析。 POST /index/ http1.1.\r\nhost:11.11.11.11\r\nContent-Type:url-formendo.... \r\n\r\nuser=alex&age=123 POST /index/ http1.1.\r\nhost:11.11.11.11\r\nContent-Type:application/json\r\n\r\n{....} - 分页 - 对从数据库中获取到的数据进行分页处理: SQL -> limit offset - 根据页码:http://www.luffycity.com/api/v1/student/?page=1&size=10 - 根据索引:http://www.luffycity.com/api/v1/student/?offset=60&limit=10 - 根据加密:http://www.luffycity.com/api/v1/student/?page=erd8 赠送:页码越大速度越慢,为什么以及如何解决? 原因:页码越大向后需要扫描的行数越多,因为每次都是从0开始扫描。 解决: - 限制显示的页数 - 记录当前页数据ID最大值和最小值,再次分页时,根据ID现行筛选,然后再分页。 - 序列化 - 对queryset序列化以及对请求数据格式校验。 - 渲染器 - 根据URL中传入的后缀,决定在数据如何渲染到到页面上。 15:htpps? 由于:http协议使用的是。 1:通信使用明文( 不加密) , 内容可能会被窃听 2:不验证通信方的身份, 因此有可能遭遇伪装 3:无法证明报文的完整性, 所以有可能已遭篡改这些问题不仅在 HTTP 上出现,其他未加密的协议中也会存在这类问题。 HTTPS 采用共享密钥加密和公开密钥加密两者并用的混合加密机制。 但是公开密钥加密与共享密钥加密相比,其处理速度要慢。所以应充分利用两者各自的优势, 将多种方法组合起来用于通信。在交换密钥环节使用公开密钥加密方式,之后的建立通信交换报文阶段则使用共享密钥加密方式。 为什么不用https通行协议? 答:因为与纯文本通信相比,加密通信会消耗更多的 CPU 及内存资源。如果每次通信都加密,会消耗相当多的资源, 平摊到一台计算机上时,能够处理的请求数量必定也会随之减少。 因此,如果是非敏感信息则使用 HTTP 通信,只有在包含个人信息等敏感数据时,才利用 HTTPS 加密通信。 特别是每当那些访问量较多的 Web 网站在进行加密处理时,它们所承担着的负载不容小觑。 在进行加密处理时,并非对所有内容都进行加密处理,而是仅在那些需要信息隐藏时才会加密,以节约资源。 其他: 1:金融量化分析: Ipython--------交互式的命令行 numpython------数组计算 Pands----------数据分析 Matplotlib-----绘图可视化 Tushare--------金融量化分析包 JoinQuant------平台 策略: 布林带策略,PEG策略,羊驼交易法则,海龟交易法则。 2:redis如何实现降序排列的。---------用堆栈做的。 答:初始化栈为空列表,判断栈是否为空,返回栈顶元素 返回栈的大小,把新的元素堆进栈里,把栈顶元素丢出去。 队列先进先出,在队头做删除操作,在队尾做插入操作。 栈先进后出,在栈顶做插入和删除操作。 堆和它们不同,不存在是先进后出还是先进先出 3:python的版本? 答:前面用2.7,后面改用3.5了。 4:mysql的版本? 答:用的是5.1 5:centos版本? 答:6.5 6:反爬取? 答: 伪装header,获取用户IP,检测用户的行为,限制访问次数。 7:Django版本? 答:1.1.6 8:redis清空所有数据? 答:fushall
版权声明:本文为Ebola-原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。