• 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
    View 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') 
    View Code

    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':'用户名邮箱或密码错误'}]}
    

      

     

      

  • 相关阅读:
    Web Browser使用技巧
    Excel 函数
    删除文件夹, 解决源文件名长度大于文件系统支持的长度问题
    Send Mail using C# code
    动态规划——最长回文子串
    字符串处理总结
    打印日期
    A+B
    对称矩阵
    最小年龄的3个职工
  • 原文地址:https://www.cnblogs.com/tangtingmary/p/8137315.html
Copyright © 2020-2023  润新知