Django 六——自定义标签、图片验证码、发送邮件、评论树、组合搜索
1、自定义标签
2、图片验证码
3、生成邮箱验证码、发送邮件
4、评论树实现
5、组合搜索(Q)
1、自定义标签
配置:
a.在app中新建文件夹 templatetags,里面新建 xx.py文件(如tags.py)
b.tags.py文件中:
#自定义标签 from django import template register = template.Library() @register.simple_tag def render_app_name(admin_class): return admin_class.model._meta.verbose_name
ps:如果返回的对象有html标签,则需要用mark_safe()来包裹返回值,同时引入
from django.utils.safestring import mark_safe
c.settings文件中配置:
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')] , 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], # 使用自定义标签是,应该加上这个配置,以便于找到tags的位置 'libraries':{ 'tags': 'king_admin.templatetags.tags', } }, }, ]
d、.html文件中
开头处加载:
{% load tags %} {#加载自定义标签#}
中间处引入:
<tr> <td>{% render_app_name admin %}</td> #函数名称 参数 <td>add</td> <td>change</td> </tr>
e、给返回的数据起个别名,用as
<tr> <td>{% render_app_name admin as obj %}</td> #函数名称 参数 返回的数据的别名为obj </tr>
2、图片验证码
原理:自动生成验证码图片,同时将验证码放在session中,以便取用。
前端中,验证码图片的src指向该验证码函数,每访问一次就重新生成一次验证码图片和对应验证码
a、在utils文件夹中新建check_code.py,生成图片的代码,用于生成验证码和对应的图片
#生成验证码图片 import random from PIL import Image, ImageDraw, ImageFont, ImageFilter # pip3 install Pillow _letter_cases = "abcdefghjkmnpqrstuvwxy" # 小写字母,去除可能干扰的i,l,o,z _upper_cases = _letter_cases.upper() # 大写字母 _numbers = ''.join(map(str, range(3, 10))) # 数字 init_chars = ''.join((_letter_cases, _upper_cases, _numbers)) def create_validate_code(size=(120, 30), chars=init_chars, img_type="GIF", mode="RGB", bg_color=(255, 255, 255), fg_color=(0, 0, 255), font_size=18, font_type="Monaco.ttf", length=4, draw_lines=True, n_line=(1, 2), draw_points=True, point_chance = 2): ''' @todo: 生成验证码图片 @param size: 图片的大小,格式(宽,高),默认为(120, 30) @param chars: 允许的字符集合,格式字符串 @param img_type: 图片保存的格式,默认为GIF,可选的为GIF,JPEG,TIFF,PNG @param mode: 图片模式,默认为RGB @param bg_color: 背景颜色,默认为白色 @param fg_color: 前景色,验证码字符颜色,默认为蓝色#0000FF @param font_size: 验证码字体大小 @param font_type: 验证码字体,默认为 ae_AlArabiya.ttf @param length: 验证码字符个数 @param draw_lines: 是否划干扰线 @param n_lines: 干扰线的条数范围,格式元组,默认为(1, 2),只有draw_lines为True时有效 @param draw_points: 是否画干扰点 @param point_chance: 干扰点出现的概率,大小范围[0, 100] @return: [0]: PIL Image实例 @return: [1]: 验证码图片中的字符串 ''' width, height = size # 宽, 高 img = Image.new(mode, size, bg_color) # 创建图形 draw = ImageDraw.Draw(img) # 创建画笔 def get_chars(): '''生成给定长度的字符串,返回列表格式''' return random.sample(chars, length) def create_lines(): '''绘制干扰线''' line_num = random.randint(*n_line) # 干扰线条数 for i in range(line_num): # 起始点 begin = (random.randint(0, size[0]), random.randint(0, size[1])) #结束点 end = (random.randint(0, size[0]), random.randint(0, size[1])) draw.line([begin, end], fill=(0, 0, 0)) def create_points(): '''绘制干扰点''' chance = min(100, max(0, int(point_chance))) # 大小限制在[0, 100] for w in range(width): for h in range(height): tmp = random.randint(0, 100) if tmp > 100 - chance: draw.point((w, h), fill=(0, 0, 0)) def create_strs(): '''绘制验证码字符''' c_chars = get_chars() strs = ' %s ' % ' '.join(c_chars) # 每个字符前后以空格隔开 font = ImageFont.truetype(font_type, font_size) font_width, font_height = font.getsize(strs) draw.text(((width - font_width) / 3, (height - font_height) / 3), strs, font=font, fill=fg_color) return ''.join(c_chars) if draw_lines: create_lines() if draw_points: create_points() strs = create_strs() # 图形扭曲参数 params = [1 - float(random.randint(1, 2)) / 100, 0, 0, 0, 1 - float(random.randint(1, 10)) / 100, float(random.randint(1, 2)) / 500, 0.001, float(random.randint(1, 2)) / 500 ] img = img.transform(size, Image.PERSPECTIVE, params) # 创建扭曲 img = img.filter(ImageFilter.EDGE_ENHANCE_MORE) # 滤镜,边界加强(阈值更大) return img, strs
b、在views文件夹中建account.py用于细化views.py的功能
from backend.utils import check_code as CheckCode def check_code(request): """ 获取验证码 :param request: :return: """ stream = io.BytesIO()#在内存开辟一块儿空间 # 创建随机字符 code 2234 # 创建一张图片img,将随机字符串2234写到图片上 img, code = CheckCode.create_validate_code() img.save(stream, "PNG")# 将图片放到内存中,以png形式,也可以以其他形式 # 将字符串形式的验证码2234放在Session中 request.session["CheckCode"] = code return HttpResponse(stream.getvalue())#再从内存取出来给用户
c、在urls.py中
from django.conf.urls import url from web.views import account urlpatterns = [ url(r'^check_code/$', account.check_code), ]
d、前端的验证码img标签的src属性指向这个地址,就能自动获取到图片
<span> <img class="check-img" src="/check_code/" alt="验证码" onclick="ChangeCode(this);"> </span> ===================================== <script> function ChangeCode(ths) { ths.src += '?'; } </script>
3、生成邮箱验证码、发送邮件
在commons.py中,定义函数生成随机数
''' 1.生成邮箱验证码 2. ''' import random def random_code(): #生成邮箱验证码 code = '' for i in range(4):#循环4次 current = random.randrange(0,4) if current != i:#生成字母 temp = chr(random.randint(65,90)) else: temp = random.randint(0,9) code += str(temp)#每一次的数字拼接起来 return code
在message.py中配置邮箱,调用即可发送邮件
#配置邮箱,调用即可发送邮件 import smtplib from email.mime.text import MIMEText from email.utils import formataddr def email(email_list, content, subject="抽屉新热榜-用户注册"): #发送设置 msg = MIMEText(content, 'plain', 'utf-8') msg['From'] = formataddr(["抽屉新热榜",'wptawy@126.com']) #设置发件人,发件地址 msg['Subject'] = subject #主题 #服务器的设置 # SMTP服务,随便哪个邮箱都可以,但是要可以设置开启smtp服务 server = smtplib.SMTP("smtp.126.com", 25)#连接**服务器,**端口 server.login("wptawy@126.com", "JUEmimima")#登录名,密码 server.sendmail('wptawy@126.com', email_list, msg.as_string())#email_list发送列表 #自己的邮箱、发送列表、发送内容 server.quit() #调用方法 # email(['xiaohu@live.com','jinxin@live.com'], 'xiaohuzuishuai')
4、递归实现评论树
实现评论树时,需要对后端和前端分别操作
后端:将数据库中取出的数据构造成父评论中有子评论,子评论中有孙子评论,这样的树状字典结构,将这样的数据以json格式传到前端。
前端:采用递归方式,将后端穿过来的数据,一层一层的拿评论,动态的生成父标签、子标签、孙子标签 。有两种方式:、
方式一:前端通过js语句,以递归方式,动态的生成标签,将递归的压力转移给用户。
方式二:采用自定义标签方式,用.py文件来处理递归函数,生成标签树后,再返回给前端,用mark_safe()包一下返回值。
后端:views.py中
def comment_sj(request): #评论实现 news_id = 1 # comment_list = models.Comment.objects.filter(news_id=news_id) # for row in comment_list: # print(row.id,row.comment,row.news_id,row.user_info.name,row.parent_id) comment_list =[ {'id':1,'comment':'python最牛逼','user':'搞基建','parent_id':None}, {'id':2,'comment':'java最牛逼','user':'搞基建','parent_id':None}, {'id':3,'comment':'php最牛逼','user':'搞基建','parent_id':None}, {'id':4,'comment':'胡说','user':'小B虎','parent_id':1}, {'id':5,'comment':'我最帅','user':'李欢','parent_id':1}, {'id':6,'comment':'郭永昌时...','user':'郭永昌','parent_id':4}, {'id':7,'comment':'郭永昌时2...','user':'郭永昌2','parent_id':4}, ] # 将comment_list_dict 构造成{ # 1:[], # 2:[], # } comment_list_dict = {} for item in comment_list: item.update({'children':[]}) #给每一行加上children列表 comment_list_dict[item['id']] = item # print(comment_list_dict) #字典为引用类型 ret = [] for item in comment_list: current_row_parent_id = item['parent_id'] if not current_row_parent_id:#current_row_parent_id 为空 ret.append(item) else: #current_row_parent_id 当前行父亲id不为空时,找到父亲行,把自己加到父亲行的children里面 #法一: # for r in comment_list: # if r['id'] == current_row_parent_id: # r['children'].append(item) #法二:使用comment_list_dict字典 comment_list_dict[current_row_parent_id]['children'].append(item) # print(ret) #方式一:ajax请求后直接返回数据 import json return HttpResponse(json.dumps(ret)) #方式二:跳转到另一个页面,采用自定义标签方式,展示评论树 #return render(request,'comment_load.html',{'ret':ret}) def comment(request): return render(request,'comment.html')
urls.py中
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^comment_sj/', views.comment_sj), url(r'^comment/', views.comment),]
前端中:评论应是点击后再显示,应以ajax形式发送
comment.html中,前端评论树生成方式一:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .comment_box{ margin-left: 45px; } </style> </head> <body> <div class="item"> <a news_id="17" class="com">评论</a> {# <div class="comment_box"><span>父评论</span>#} {# <div class="comment_box"><span>字1评论</span>#} {# </div>#} {# <div class="comment_box"><span>字2评论</span>#} {# <div class="comment_box"><span>孙子评论</span>#} {# </div>#} {##} {# </div>#} </div> </div> <div class="item"> <a news_id="17" class="com">评论</a> </div> <script src="/static/js/jquery-2.1.4.min.js"></script> <script> $(function () { bindCommentEvent(); }); function bindCommentEvent() { $('.com').click(function () { var news_id = $(this).attr('news_id'); var _this = $(this); $.ajax({ url:'/comment_sj', type:'GET', data:{news_id:news_id}, dataType:'JSON',//第二种方式是,这个地方要改成html!!!! success:function (arg) { console.log(arg); //方式一:(推荐此方法,将递归的压力分解给用户) create_tree(arg,_this); //方式二:用render返回另一个.html页面,页面里面放数据,数据由.py文件生成 //_this.after(arg); } }) }) } function diGui(children_list) { var htm = ''; $.each(children_list,function (ck,cv) { var b = '<div class="comment_box"><span>'; b += cv.comment+'</span>'; var result = diGui(cv.children); b += result; b +='</div>'; htm += b; }); return htm; } function create_tree(data,$this) { var htm = '<div class="comment_box">';//父评论框 //父评论内容 $.each(data,function (k,v) { {# console.log(data);#} var a = '<div class="comment_box"><span>'; a += v.comment+'</span>'; //下一级子评论内容加在这里这里调用函数来生成子评论 var result = diGui(v.children); a += result; a +='</div>'; htm += a; }); htm += '</div>'; $this.after(htm); /* 此处应该是after,而不是append,append时加在标签里面,after时加在标签下面*/ {# console.log(htm)#} } </script> </body> </html>
前端,评论树生成方式二:
方式二示例:
comment_load.html中采用方式二:
{% load laogao %} {% create_tree ret %}
在 templetetags文件夹中的 laogao.py文件中:
# 方式二:此py文件创建评论树,待.html文件加载 from django import template from django.utils.safestring import mark_safe register = template.Library() def diGui(children_list): htm = '' for cv in children_list: b = '<div class="comment_box"><span>' b += cv['comment'] + '</span>' result = diGui(cv['children']) b += result b += '</div>' htm += b return htm @register.simple_tag def create_tree(ret): htm = '<div class="comment_box">' for v in ret: b = '<div class="comment_box"><span>' b += v['comment'] + '</span>' b += diGui(v['children']) b += '</div>' htm += b htm += '</div>' return mark_safe(htm)
5、组合搜索(Q)
两种方式
方式一:手动添加Q对象操作
方式二:动态的生成Q对象来操作, 此方式也可用于组合搜索
===========方式一============= obj = models.UserInfo.objects.filter(Q(Q(username=n) & Q(pwd=p)) | Q(Q(email=e) & Q(pwd=p)) ).first() ===========方式二========== # 组合搜索 from django.db.models import Q con = Q() q1 = Q() q1.connector = 'AND' q1.children.append(('email',value_dict['email'])) #即 q1(email = value_dict['email']) q1.children.append(('password',value_dict['pwd'])) q2 = Q() q2.connector = 'AND' q2.children.append(('username', value_dict['user'])) q2.children.append(('password', value_dict['pwd'])) con.add(q1,'OR') con.add(q2,'OR') obj = models.UserInfo.objects.filter(con).first() if not obj: rep.message = {'user':[{'message':'用户名邮箱或密码错误'}]}