CRM项目问答总结
1. 通过ChangeList封装好多数据 DA: 在stark组件中,有五个封装的大类: class FilterOption(object): ----用于封装组合搜索的配置信息(数据库字段,是否多选,是否是choice) class FilterRow(object):----生成按钮以及一系列的<a>标签 class ChangeList(object):----将列表页面的功能封装到此类中 class StarkConfig(object):----用于为每个类生成url的对应关系,并处理用户请求,处理增删改查的基类 class StarkSite(object):----是一个容器,用于放置处理请求的对应关系 2. 销售中公共资源:Q查询,3天 15天 DA:如果有新用户引进,对于销售来说,必须得跟进,但是跟进的过程中会有3天未跟进,15天未成单的限制。 如果该客户成为了公共资源,那么就会有抢单这一功能。在实现抢单时,一般的数据查询在这里不太适用,所有这里引用到Q查询,更高效的查出结果 Q:查询函数可以混合使用 Q 对象参数和关键字参数. 所有提供给查询函数的参数(不管是关键字参数还是Q对象)都被 "AND" 到一起. 如果提供了 Q 对象作为参数,它就必须在其它关键字参数(如果有的话)的前面 3. 使用yield实现 - 生成器函数,对数据进行加工处理 DA:在ChangeList中,def gen_comb_filter(self): 给前端进行循环显示数据用(生成器函数), 在生成器中返回可迭代对象,FilterRow对象(FilterRow对象是自己创建的) - __iter__和yield配合 4. 获取Model类中的字段对应的对象 class Foo(model.Model): xx = models.CharField() Foo.get_field('xx') 5. 模糊搜索功能 DA:模糊搜索就是在列表页面中,增加模糊关键字搜索功能,和是否显示添加按钮,actions定制,组合搜索都封装在了StarkConfig类中。 if key_word and self.get_show_search_form(): for field_name in search_fields: condition.children.append((field_name, key_word)) return condition 6. Type创建类 DA: 用type创建动态的form如: TempForm = type('TempForm',(Form,),{ 'score_%s' %obj.pk :fields.ChoiceField(choices=models.StudyRecord.score_choices), 'homework_note_%s' %obj.pk : fields.CharField(widget=widgets.Textarea()) }) 7. 自动派单 DA: -自动派单: 在处理自动派单问题时应该考虑到当前用户是否为公司的公共资源,在判断公共资源这个问题上就要根据当前用户是否为已经属于公共客户: a:公共客户 所谓的公共的客户就是销售在规定的某个时间段内没有把客户转化为用户,或者没有在允许的时间内跟进,就会被转为公共用户。而成为公共用户之后,该销售则无权跟进(利用Q对象的方法进行排除),
但是也不排除,有的客户冥顽不灵,转化不了,其他的销售都不喜欢或者不愿意跟进,该销售也可以跟进。 b:自己的客户 所谓我的客户就是已经成单或者在规定时间内正在跟进的客户,而且在这里将会有一个客户跟进表中记录销售的跟进内容,生成一条跟进内容如果是一个新的客户的话,就设置一个定时的一个检查的脚本
,以用于查看销售的跟进记录。在新建的表里选出没有报名的用户,在没有报名的用户上加抢单功能,获取用户的ID,修改客户表中的客户,把接单时间修改为当前时间,跟进时间也是当前时间,修改客户顾问为现在的。 c:自动抢单 单条录入,创建url,进入用modelform写的页面,新增客户表中的数据,获取自动分配的课程顾问的ID,当前时间,在客户分配表中新增数据,获取新创建的客户ID,课程顾问ID,
而给课程顾问分配是跟据权重进行分配的 - 原来在内存中实现,问题:重启和多进程时,都有问题。 - redis - 状态 - 原来数据(权重表 权重和个数) - pop数据 DA:在多选或者单选,后面必须添加popup按钮,因为如果是field是ModelChoiceField 则是Fk, 如果是ModelMultipChoice 则是M2M。编辑和增加弹出的子页面都用到了popup代码
需要重写一次,所以引入templatetags 8. 使用 list_diplay配置 DA:因为已经在v1.py中的的class StarkConfig类中封装了封装了前端列表定制列表页面显示的列,所有在使用时,只写数据库字段名即可 list_display = ['title','code'] 9. reverse反向生成URL DA: def index(request): from django.urls import reverse test_url = reverse('xxx') return HttpResponse("index") def back(requext): return HttpResponse("back") urlpatterns = [ url(r'^index/', index,), url(r'^back/dd/nn/', back,name='xxx'), ] 10. 母版 DA: 在最外层的templates:stark>layout.html作为模板,让其他的相关页面去继承layout.html。大大 降低了代码重复 11. ready方法定制起始文件 DA: 就是在启动项目的同时,运用ready方法加载出来运行。 - 文件导入实现单例模式 12. inclusion_tag 13. 中间件的使用 -什么是中间件: django中的中间件(middleware),在django中,中间件其实就是一个类,在请求到来和结束后,django会根据自己的规则在合适的时机执行中间件中相应的方法。在django项目的settings模块中,
有一个 MIDDLEWARE_CLASSES 变量,其中每一个元素就是一个中间件 DA:-用户登录 - 日志记录 - csrf - session - 权限管理 15. importlib + getattr 16. FilterOption,lambda表达式 DA: lanmbda是一个匿名函数它可以通过传给reduce中的函数,依次对数据集中的数据进行操作。 17. QueryDict - 原条件的保留 - filter 18. ModelForm DA:没有用Form,但是这里运用ModelForm创建自动生成的列表页面以及input框的错误提示信息 19. 面向对象的 @property @classmethod DA: @property是把一个方法变成属性可调用的 eg:把一个getter方法变成属性,只需要加上@property就可以了,此时,@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值,于是,
我们就拥有一个可控的属性操作(百度搜索到的说法) @staticmethod可以不需要实例化,直接用 类名.方法名来调用 即:(两者的区别) @staticmethod不需要表示自身对象的self和自身类的cla参数,就跟使用函数一样 @classmethod也不需要self参数,但第一个参数需要表示自身类的cls参数 20. mark_safe DA: 后端解析前端代码,可在前端展示成前端页面,否则是字符串形式展示 21. 抽象方法抽象类+raise Im... DA: 在base.py文件中运用了抽象方法 方法一: from abc import ABCMeta from abc import abstractmethod class BaseMessage(metaclass=ABCMeta): @abstractmethod def send(self,subject,body,to,name): pass 方法二: class BaseMessage(object): def send(self, subject, body, to, name): raise NotImplementedError('未实现send方法') 我们在dingding.py文件中用了抽象类 from .base import BaseMessage class DingDing(BaseMessage): def __init__(self): pass def send(self,subject,body,to,name): print('钉钉消息发送成功') 22. 组件中的装饰器,实现self.request = request 23. 自执行函数 (function(arg){ })('sf') 24. URL的钩子函数 DA: get_show_add_btn(self): .... 对于页面添加等按钮是否显示做出更细致的判断 25. 多继承 从左到右继承(钻石继承) 新式类:python3:广度优先 经典类:python2:深度优先 26. 批量导入,xlrd import xlrd file_obj = request.FILES.get('exfile') with open('xxxxxx.xlsx', mode='wb') as f: for chunk in file_obj: f.write(chunk) workbook = xlrd.open_workbook('xxxxxx.xlsx') sheet = workbook.sheet_by_index(0) maps = { 0: 'name', 1: 'qq', } for index in range(1, sheet.nrows): row = sheet.row(index) # {'name':"把几个",'qq':123456789} # print(row,type(row)) row_dict = {} for i in range(len(maps)): key = maps[i] cell = row[i] row_dict[key] = cell.value 27. redis连接池 DA: 插入单条数据 import redis conn = redis.Redis(host='10.0.0.10',port=6379) conn.set('k1','v1') 向远程redis中写入了一个键值对 val = conn.get('k1') 获取键值对 print(val) 插入多条数据 conn = redis.Redis(host='10.0.0.10',port=6379) conn.lpush('names_list',*['把几个','鲁宁']) v = conn.llen('names_list') for i in range(v): val = conn.rpop('names_list') 从右边第一条开始pop数据 val = conn.lpop('names_list') 从左边第一条开始pop数据 print(val.decode('utf-8')) v = conn.llen('namessssss_list') print(v) 28. 工厂模式 settings.py MSG_PATH = "path.Email" class XXFactory(object): @classmethod def get_obj(cls): settings.MSG_PATH # rsplit # importlib # getattr return obj class Email(object): def send ... class WeChat(object): def send ... class Msg(object): def send ... 29. Models类中自定义save方法 # 创建客户表 #事物,如果一条失败,则全体失败 #通过form.instance.field_name添加其它字段数据 form.instance.consultant_id = sale_id form.instance.recv_date = ctime form.instance.last_consult_date = ctime new_customer = form.save() 30. django admin中注册models时候 from django.contrib import admin from . import models # 方式一 class UserConfig(admin.ModelAdmin): pass admin.site.register(models.UserInfo,UserConfig) # 方式二 @admin.register(models.UserInfo) class UserConfig(admin.ModelAdmin): pass 31. 深浅拷贝 DA: 浅拷贝copy:不管多么复杂的数据结构,浅拷贝都只会copy一层。 深拷贝deepcopy:其实深拷贝就是在内存中重新开辟一块空间,不管数据结构多么复杂,只要遇到可能发生改变的数据类型,
就重新开辟一块内存空间把内容复制下来,直到最后一层,不再有复杂的数据类型,就保持其
原引用。这样不管数据结构多么的复杂,数据之间的修改都不会相互影响。这就是深拷贝 深拷贝在哪里使用过:分页
有一种能力,是持续不断的努力