欢迎来到Felix的博客

Do the right things! And talk is cheap,show me your code!

搭建自己的博客(二十九):增加绑定邮箱的功能,完善用户信息

1、邮箱服务器使用了腾讯服务器

具体操作见:python自动发邮件

2、变化的部分

3、上代码:

{# 引用模板 #}
{% extends 'base.html' %}
{% load staticfiles %}
{% load comment_tags %}
{% load likes_tags %}

{% block header_extends %}
    <link rel="stylesheet" href="{% static 'blog/blog.css' %}">
    <link rel="stylesheet" href="{% static 'fontawesome-free-5.5.0-web/css/all.min.css' %}">

    {#  处理公式  #}
    <script src='https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-MML-AM_CHTML'
            async></script>
    <script type="text/javascript" src="{% static "ckeditor/ckeditor-init.js" %}"></script>
    <script type="text/javascript" src="{% static "ckeditor/ckeditor/ckeditor.js" %}"></script>

{% endblock %}

{# 标题 #}
{% block title %}
    {{ blog.title }}
{% endblock %}

{# 内容#}
{% block content %}
    <div class="container">
        <div class="row">
            <div class="col-10 offset-1">
                <ul class="blog-info-description">
                    <h3>{{ blog.title }}</h3>
                    <li>作者:{{ blog.author }}</li>
                    {# 时间过滤器让时间按照自己需要的格式过滤 #}
                    <li>发布日期:{{ blog.created_time|date:"Y-m-d H:i:s" }}</li>
                    <li>分类:
                        <a href="{% url 'blogs_with_type' blog.blog_type.pk %}">
                            {{ blog.blog_type }}
                        </a>
                    </li>
                    <li>阅读({{ blog.get_read_num }})</li>
                    <li>评论({% get_comment_count blog %})</li>
                </ul>
                <div class="blog-content">{{ blog.content|safe }}</div>

                <div class="like"
                     onclick="likeChange(this,'{% get_content_type blog %}',{{ blog.pk }})">
                    <i class="far fa-thumbs-up {% get_like_status blog %}"></i>
                    <span class="liked-num">{% get_like_count blog %}</span>
                    <span>喜欢</span>
                </div>

                <p>上一篇:
                    {% if previous_blog %}
                        <a href="{% url 'blog_detail' previous_blog.pk %}">{{ previous_blog.title }}</a>
                    {% else %}
                        <span>没有了</span>
                    {% endif %}
                </p>
                <p>下一篇:
                    {% if next_blog %}
                        <a href="{% url 'blog_detail' next_blog.pk %}">{{ next_blog.title }}</a>
                    {% else %}
                        <span>没有了</span>
                    {% endif %}
                </p>
            </div>
        </div>
        <div class="row">
            <div class="col-10 offset-1">
                <div class="comment-area">
                    <h3 class="comment-area-title">提交评论</h3>
                    {% if user.is_authenticated %}
                        <form id="comment-form" action="{% url 'update_comment' %}" method="post"
                              style="overflow: hidden">
                            {% csrf_token %}
                            <label for="form-control">{{ user.get_nickname_or_username }},欢迎评论~</label>
                            <div id="reply-content-container" style="display: none;">
                                <p id="reply_title">回复:</p>
                                <div id="reply-content">

                                </div>
                            </div>
                            {% get_comment_form blog as comment_form %}
                            {% for field in comment_form %}
                                {{ field }}
                            {% endfor %}
                            <span id="comment-error" class="text-danger float-left"></span>
                            <input type="submit" value="评论" class="btn btn-primary float-right">
                        </form>
                    {% else %}
                        您尚未登录,登录之后方可评论
                        {# 提交登录的时候带上从哪里访问的路径 #}
                        <a class="btn btn-primary" href="{% url 'login' %}?from={{ request.get_full_path }}">登录</a>
                        <span> or </span>
                        <a class="btn-danger btn" href="{% url 'register' %}?from={{ request.get_full_path }}">注册</a>
                    {% endif %}
                </div>
                <div class="-comment-area">
                    <h3 class="comment-area-title">评论列表</h3>
                    <div id="comment-list">
                        {% get_comment_list blog as comments %}
                        {% for comment in comments %}
                            <div id="root-{{ comment.pk }}" class="comment">
                                <span>{{ comment.user.get_nickname_or_username }}</span>
                                <span>{{ comment.comment_time|date:"Y-m-d H:i:s" }}</span>
                                <div id="comment-{{ comment.pk }}">{{ comment.text|safe }}</div>
                                {# 点赞 #}
                                <div class="like"
                                     onclick="likeChange(this,'{% get_content_type comment %}',{{ comment.pk }})">
                                    <i class="far fa-thumbs-up {% get_like_status comment %}"></i>
                                    <span class="liked-num">{% get_like_count comment %}</span>
                                </div>

                                <a href="javascript:reply({{ comment.pk }})">回复</a>

                                {% for reply in comment.root_comment.all %}
                                    <div class="reply">
                                        <span>{{ reply.user.get_nickname_or_username }}</span>
                                        <span>{{ reply.comment_time|date:"Y-m-d H:i:s" }}</span>
                                        <span>回复:</span><span>{{ reply.reply_to.get_nickname_or_username }}</span>
                                        <div id="comment-{{ reply.pk }}">{{ reply.text|safe }}</div>
                                        {# 点赞 #}
                                        <div class="like"
                                             onclick="likeChange(this,'{% get_content_type reply %}',{{ reply.pk }})">
                                            <i class="far fa-thumbs-up {% get_like_status reply %}"></i>
                                            <span class="liked-num">{% get_like_count reply %}</span>
                                        </div>

                                        <a href="javascript:reply({{ reply.pk }})">回复</a>
                                    </div>
                                {% endfor %}
                            </div>
                        {% empty %}
                            <span id="no-comment">暂无评论</span>
                        {% endfor %}
                    </div>
                </div>
            </div>
        </div>
    </div>
{% endblock %}

{% block js %}
    <script>
        // 处理点赞
        function likeChange(obj, content_type, object_id) {
            let is_like = obj.getElementsByClassName('active').length === 0;


            $.ajax({
                url: "{% url 'like_change' %}",
                type: 'GET',
                data: {
                    content_type: content_type,
                    object_id: object_id,
                    is_like: is_like,
                },
                cache: false,
                success: function (data) {
                    console.log(data);
                    if (data['status'] === 'SUCCESS') {
                        // 更新点赞状态
                        let element = $(obj.getElementsByClassName('fa-thumbs-up'));
                        if (is_like) {
                            element.addClass('active');
                        } else {
                            element.removeClass('active');
                        }
                        // 更新点赞数量
                        let like_num = $(obj.getElementsByClassName('liked-num'));
                        like_num.text(data['liked_num']);

                    } else {

                        if (data['code'] === 400) {
                            $('#login_model').modal('show');
                        } else {
                            alert(data['msg']);
                        }
                    }
                },
                error: function (xhr) {
                    console.log(xhr);
                }
            });
        }

        // 处理回复
        function reply(reply_comment_id) {
            $('#reply_comment_id').val(reply_comment_id);
            let html = $('#comment-' + reply_comment_id).html();

            $('#reply-content').html(html);
            $('#reply-content-container').show();  // 显示内容


            // 滚动富文本编辑器
            $('html').animate({scrollTop: $('#comment-form').offset().top - 60}, 300, function () {
                // 动画执行完毕后执行的方法
                // 让富文本编辑器获得焦点
                CKEDITOR.instances['id_text'].focus();
            });
        }

        function numFormat(num) {
            return ('00' + num).substr(-2);
        }

        function timeFormat(timestamp) {
            let datetime = new Date(timestamp * 1000);
            let year = datetime.getFullYear();
            let month = numFormat(datetime.getMonth() + 1);
            let day = numFormat(datetime.getDate());
            let hour = numFormat(datetime.getHours());
            let minute = numFormat(datetime.getMinutes());
            let second = numFormat(datetime.getSeconds());
            return `${year}-${month}-${day} ${hour}:${minute}:${second}`
        }

        // 提交评论
        $('#comment-form').submit(function () {
            // 获取错误框
            let comment_error = $('#comment-error');
            comment_error.text('');

            // 更新数据到textarea
            CKEDITOR.instances['id_text'].updateElement();
            let comment_text = CKEDITOR.instances['id_text'].document.getBody().getText().trim();
            // 判断是否为空
            if (!(CKEDITOR.instances['id_text'].document.getBody().find('img')['$'].length !== 0 || comment_text !== '')) {
                // 显示错误信息
                comment_error.text('评论内容不能为空');
                return false;
            }
            //异步提交
            $.ajax({
                url: "{% url 'update_comment' %}",
                type: 'POST',
                data: $(this).serialize(),// 序列化表单值
                cache: false, // 关闭缓存
                success: function (data) {
                    let reply_comment = $('#reply_comment_id');
                    if (data['status'] === 'SUCCESS') {
                        console.log(data);
                        //  插入数据
                        //  es6写法
                        let like_html = `<div class="like"
                                     onclick="likeChange(this,'${data['content_type']}',${data["pk"]})">
                                    <i class="far fa-thumbs-up"></i>
                                    <span class="liked-num">0</span>
                                </div>`;
                        if (reply_comment.val() === '0') {
                            // 插入评论
                            let comment_html = `<div id="root-${data["pk"]}" class="comment">
                                <span>${data["username"]}</span>
                                <span>${timeFormat(data["comment_time"])}</span>
                                <div id="comment-${data["pk"]}">${data["text"]}</div>
                                ${like_html}
                                <a href="javascript:reply(${data["pk"]})">回复</a>
                            </div>`;

                            $('#comment-list').prepend(comment_html);

                        } else {
                            // 插入回复

                            let reply_html = `<div class="reply">
                                        <span>${data["username"]}</span>
                                        <span>${timeFormat(data["comment_time"])}</span>
                                        <span>回复:</span><span>${data["reply_to"]}</span>
                                        <div id="comment-${data["pk"]}">${data["text"]}</div>
                                        ${like_html}
                                        <a href="javascript:reply(${data["pk"]})">回复</a>
                                    </div>`;
                            $('#root-' + data['root_pk']).append(reply_html);

                        }

                        //  清空编辑框的内容
                        CKEDITOR.instances['id_text'].setData('');
                        $('#reply-content-container').hide(); // 回复完隐藏掉要回复的内容
                        reply_comment.val('0'); // 将回复标志重置0
                        $('#no-comment').remove(); // 如果有没回复标志,清除掉5
                        comment_error.text('评论成功');
                    } else {
                        // 显示错误信息
                        comment_error.text(data['message'])
                    }
                },
                error: function (xhr) {
                    console.log(xhr);
                }
            });
            return false;
        });
    </script>

    <script>
        $(".nav-blog").addClass("active").siblings().removeClass("active");
    </script>
{% endblock %}
blog下的blog_detail.html
from django.http import JsonResponse
from django.contrib.contenttypes.models import ContentType
from .models import Comment
from .forms import CommentForm


def update_commit(requests):
    comment_form = CommentForm(requests.POST, user=requests.user)
    if comment_form.is_valid():
        comment = Comment()
        comment.user = comment_form.cleaned_data['user']
        comment.text = comment_form.cleaned_data['text']
        comment.content_object = comment_form.cleaned_data['content_object']

        parent = comment_form.cleaned_data['parent']
        if parent is not None:
            comment.root = parent.root if parent.root is not None else parent
            comment.parent = parent
            comment.reply_to = parent.user
        comment.save()
        # 返回数据
        data = {
            'status': 'SUCCESS',
            'username': comment.user.get_nickname_or_username(),
            'comment_time': comment.comment_time.timestamp(),  # 返回时间戳
            'text': comment.text.strip(),
            'reply_to': comment.reply_to.get_nickname_or_username() if parent is not None else '',
            'pk': comment.pk,
            'root_pk': comment.root.pk if comment.root is not None else '',
            'content_type': ContentType.objects.get_for_model(comment).model,
        }

    else:
        data = {
            'status': 'ERROR',
            'message': list(comment_form.errors.values())[0][0],
        }
    return JsonResponse(data)
comment下的views.py
"""
Django settings for myblog project.

Generated by 'django-admin startproject' using Django 2.1.3.

For more information on this file, see
https://docs.djangoproject.com/en/2.1/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.1/ref/settings/
"""

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'ea+kzo_5k^6r7micfg@lar1(rfdc08@b4*+w5d11=0mp1p5ngr'

# SECURITY WARNING: don't run with debug turned on in production!2.
DEBUG = True

ALLOWED_HOSTS = ['*']

# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'ckeditor',
    'ckeditor_uploader',
    'blog.apps.BlogConfig',  # 将自己创建的app添加到设置中
    'read_statistics.apps.ReadStatisticsConfig',  # 注册阅读统计app
    'comment.apps.CommentConfig',  # 注册评论
    'likes.apps.LikesConfig',  # 注册点赞
    'user.apps.UserConfig',  # 用户相关
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'blog.middleware.mymiddleware.My404',  # 添加自己的中间件
]

ROOT_URLCONF = 'myblog.urls'

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',
                'user.context_processors.login_model_form',  # 自定义模板变量,直接用模板语言调用
            ],
        },
    },
]

WSGI_APPLICATION = 'myblog.wsgi.application'

# Database
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases

DATABASES = {
    # 'default': {
    #     'ENGINE': 'django.db.backends.sqlite3',
    #     'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    # }
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'myblogs',  # 要连接的数据库,连接前需要创建好
        'USER': 'root',  # 连接数据库的用户名
        'PASSWORD': 'felixwang',  # 连接数据库的密码
        'HOST': '127.0.0.1',  # 连接主机,默认本级
        'PORT': 3306  # 端口 默认3306
    }
}

# Password validation
# https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]

# Internationalization
# https://docs.djangoproject.com/en/2.1/topics/i18n/

# LANGUAGE_CODE = 'en-us'
# 语言
LANGUAGE_CODE = 'zh-hans'

# TIME_ZONE = 'UTC'
# 时区
TIME_ZONE = 'Asia/Shanghai'

USE_I18N = True

USE_L10N = True

# 不考虑时区
USE_TZ = False

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.1/howto/static-files/

STATIC_URL = '/static/'
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static")
]

# media
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

# 配置ckeditor
CKEDITOR_UPLOAD_PATH = 'upload/'

# 自定义参数
EACH_PAGE_BLOGS_NUMBER = 7

# 设置数据库缓存
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
        'LOCATION': 'my_read_num_cache_table',
    }
}

# ckeditor 代码高亮,以及公式
CKEDITOR_CONFIGS = {
    'default': {
        'skin': 'moono',
        'tabSpaces': 4,
        'mathJaxLib': 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-MML-AM_CHTML',
        'toolbar': (
            ['div', 'Source', '-', 'Save', 'NewPage', 'Preview', '-', 'Templates'],
            ['Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-', 'Print',
             'SpellChecker', 'Scayt'],
            ['Undo', 'Redo', '-', 'Find', 'Replace', '-', 'SelectAll', 'RemoveFormat',
             '-', 'Maximize', 'ShowBlocks', '-', "CodeSnippet", 'Mathjax', 'Subscript',
             'Superscript'],
            ['Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button',
             'ImageButton', 'HiddenField'],
            ['Bold', 'Italic', 'Underline', 'Strike', '-'],
            ['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', 'Blockquote'],
            ['JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock'],
            ['Link', 'Unlink', 'Anchor'],
            ['Image', 'Flash', 'Table', 'HorizontalRule', 'Smiley', 'SpecialChar',
             'PageBreak'], ['Styles', 'Format', 'Font', 'FontSize'],
            ['TextColor', 'BGColor'],),
        'extraPlugins': ','.join([
            'codesnippet',
            'mathjax',
            'dialog',
            'dialogui',
            'lineutils',
        ]),
    },
    'comment_ckeditor': {
        'toolbar': 'custom',
        'toolbar_custom': [
            ['Bold', 'Italic', 'Underline', 'Strike', 'Subscript', 'Superscript'],
            ['TextColor', 'BGColor', 'RemoveFormat'],
            ['NumberedList', 'BulletedList'],
            ['Link', 'Unlink'],
            ['Smiley', 'SpecialChar', 'Blockquote'],
        ],
        'width': 'auto',
        'height': '180',
        'tabSpace': 4,
        'removePlugins': 'elementspath',
        'resize_enabled': False,
    }
}

# 发送邮箱设置
MAIL_HOST = 'smtp.qq.com'  # smtp服务地址
EMAIL_PORT = 465  # 端口号
EMAIL_HOST_USER = '1403179190@qq.com'  # qq邮箱
EMAIL_HOST_PASSWORD = '''  # 如果是qq邮箱的话该密码是配置qq邮箱的SMTP功能的授权码
FROM_WHO = 'FCBlog'  # 前缀
seetings.py
# -*- coding: utf-8 -*-
# @Time    : 18-11-27 上午11:07
# @Author  : Felix Wang

from PIL import Image, ImageDraw, ImageFont, ImageFilter
from io import BytesIO
import random

from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from email.utils import parseaddr, formataddr
from email.header import Header
import smtplib
from hashlib import md5


class CheckCode:
    def __init__(self, font_file, font_size=36, width=240, height=60, char_length=4, start_color_num=0,
                 end_color_num=255, is_simple=True, is_oncache=False):
        self.is_oncache = is_oncache
        self.is_simple = is_simple
        self.font_file = font_file
        self.font_size = font_size
        self.width = width
        self.height = height
        self.char_length = char_length
        self.start_num = start_color_num
        self.end_num = end_color_num
        # 定义使用Image类实例化一个长为120px,宽为30px,基于RGB的(255,255,255)颜色的图片
        self.image = Image.new('RGB', (self.width, self.height), (255, 255, 255))
        # 创建Font对象:
        self.font = ImageFont.truetype(self.font_file, self.font_size)
        # 创建Draw对象:
        self.draw = ImageDraw.Draw(self.image)
        # 双下划綫的变量在类中不能直接访问起到保护的作用
        self.__code_text = []

    def get_random_code(self, time=1):
        """
        :param is_simple: 是否包含中文繁体字,默认不包含,True表示不包含
        :param time: 返回多少个
        :return: 返回一个随机字符列表
        """
        is_simple = self.is_simple
        codes = []
        for i in range(time):
            num = chr(random.randint(0x30, 0x39))  # 随机生成数字
            lowercase = chr(random.randint(0x61, 0x74))  # 随机生成小写字母
            capcase = chr(random.randint(0x41, 0x5a))  # 随机生成大写字母

            # Unicode编码的汉字,带繁体字 ,共2万多字
            zh = chr(random.randint(0x4e00, 0x9fbf))

            # gbk编码的简单汉字,无繁体字
            head = random.randint(0xb0, 0xf7)
            body = random.randint(0xa1, 0xf9)  # 在head区号为55的那一块最后5个汉字是乱码,为了方便缩减下范围
            val = f'{head:x}{body:x}'
            ch = bytes.fromhex(val).decode('gb2312')

            if is_simple:
                # code = random.choice([ch, num, lowercase, capcase])
                code = random.choice([ch, num, lowercase, capcase])
            else:
                code = random.choice([zh, num, lowercase, capcase])
            codes.append(code)
        return codes

    # 随机颜色:
    def rndColor(self, start, end, randomflag=True):
        """
        :param start:
        :param end:
        :param randomflag: 是否返回随机参数,
        :return:
        """
        return (random.randint(start, end), random.randint(start, end),
                random.randint(start, end))

    def rotate(self):
        self.image.rotate(random.randint(0, 90), expand=0)

    # 随机点
    def randPoint(self):
        return (random.randint(0, self.width), random.randint(0, self.height))

    # 随机线
    def randLine(self, num):
        draw = ImageDraw.Draw(self.image)
        for i in range(0, num):
            draw.line([self.randPoint(), self.randPoint()], self.rndColor(0, 255))
        del draw

    # 获取验证码
    def get_codes(self):
        return self.__code_text

    def draw_pic(self):
        # 填充每个像素:
        # 单一背景
        color = self.rndColor(170, 255)  # 可以把背景调浅色
        for x in range(self.width):
            for y in range(self.height):
                self.draw.point((x, y), fill=color)

        # 输出文字:
        codes = self.get_random_code(time=self.char_length)
        self.__code_text = []
        for ii in range(self.char_length):
            code = self.get_random_code()[0]
            self.__code_text.append(code)
            self.draw.text([random.randint(int((self.width / 2 - self.font_size / 2) / self.char_length * 2 * ii),
                                           int((self.width / 2 - self.font_size / 2) / self.char_length * 2 * (
                                                   ii + 1))), random.randint(0, self.height / 4)],
                           code, font=self.font, fill=self.rndColor(0, 120))  # 把字体调深色
        # self.rotate()

        # 画出随机线
        self.randLine(10)

        # 模糊:
        # self.image = self.image.filter(ImageFilter.BLUR)
        if self.is_oncache:  # 保存在缓存
            f = BytesIO()
            self.image.save(f, 'jpeg')
            return f.getvalue()
        else:
            # 保存
            self.image.save('{}.jpg'.format(''.join(self.get_codes())), 'jpeg')


# 自动发邮件
class AutoSendEmail:
    def __init__(self, sender, password, title, from_who, recever, smtp_server="smtp.qq.com", port=465):
        """
        :param sender: 邮件发送者
        :param password: 密码
        :param title: 邮件发送主题
        :param from_who: 邮件来自谁
        :param recever: 邮件接收者,可以是多个
        :param smtp_server: 邮件服务器,默认qq邮箱服务器
        :param port: 服务器端口qq邮箱默认端口为465
        """
        self.smtp_server = smtp_server  # 使用qq转发需要用到,可以在QQ邮箱设置中查看并开通此转发功能
        self.smtp_port = port  # smtp默认的端口是465
        # 接受者可以是多个,放在列表中
        self.recever = recever
        self.sender = sender
        self.password = password  # 该密码是配置qq邮箱的SMTP功能的授权码
        self.msg = MIMEMultipart()
        self.msg['Subject'] = title  # 邮件标题
        self.msg['From'] = self._format_addr(u'{} <{}>'.format(from_who, self.sender))

    # 添加文字信息
    def addTextMsg(self, text):
        text_plain = MIMEText(text, 'plain', 'utf-8')
        self.msg.attach(text_plain)

    # 添加图片
    def addImageMsg(self, imgPath):
        extend = imgPath.split('.')[-1]
        with open(imgPath, 'rb')as f:
            sendimagefile = f.read()
            filename = md5(sendimagefile).hexdigest() + '.' + extend
        image = MIMEImage(sendimagefile)
        image.add_header('Content-ID', '<image1>')
        image["Content-Disposition"] = u'attachment; filename={}'.format(filename)
        self.msg.attach(image)

    # 添加附件
    def addFile(self, filePath):
        extend = filePath.split('.')[-1]
        with open(filePath, 'rb')as f:
            sendfile = f.read()
            filename = md5(sendfile).hexdigest() + '.' + extend
        # 构造附件
        text_att = MIMEText(sendfile, 'base64', 'utf-8')
        text_att["Content-Type"] = 'application/octet-stream'
        text_att["Content-Disposition"] = u'attachment; filename="{}"'.format(filename)
        self.msg.attach(text_att)

    # 添加html格式
    def addHtml(self, html):
        # 构造html
        # 发送正文中的图片:由于包含未被许可的信息,网易邮箱定义为垃圾邮件,报554 DT:SPM :<p><img src="cid:image1"></p>
        text_html = MIMEText(html, 'html', 'utf-8')
        self.msg.attach(text_html)

    # 格式化邮件地址
    def _format_addr(self, s):
        name, address = parseaddr(s)
        return formataddr((Header(name, 'utf-8').encode(), address))

    # 发送邮件
    def sendEmail(self):
        server = smtplib.SMTP_SSL(self.smtp_server, self.smtp_port)  # 链接服务器
        server.set_debuglevel(1)  # 打印出和SMTP服务器交互的信息
        server.login(self.sender, self.password)  # 登录
        server.sendmail(self.sender, self.recever, self.msg.as_string())  # 发送邮件
        server.quit()  # 退出
        print('邮件发送成功')


if __name__ == '__main__':
    smtp_server = "smtp.qq.com"  # smtp服务地址
    port = 465  # 端口号
    recever = ['1403179190@qq.com']  # 接收人列表可以是多个
    sender = "1403179190@qq.com"  # 发送人邮箱
    password = ""  # 如果是qq邮箱的话该密码是配置qq邮箱的SMTP功能的授权码
    title = '验证码'
    from_who = 'felixCRM'  # 发送人姓名

    # 实例化对象
    autoEmail = AutoSendEmail(sender=sender, recever=recever, password=password, title=title, from_who=from_who,
                              smtp_server=smtp_server, port=port)

    html = """
        <html>  
          <head></head>  
          <body>  
            <p>Hi!<br>  
               欢迎注册felixCRM系统!
               <br>  
               Here is the <a href="http://www.baidu.com">link</a> you wanted.<br> 
            </p> 
            <img src="http://img.zcool.cn/community/01f09e577b85450000012e7e182cf0.jpg@1280w_1l_2o_100sh.jpg"></img>
          </body>  
        </html>  
        """
    # 以html的形式发送文字,推荐这个,因为可以添加图片等
    autoEmail.addHtml(html)
    # 发送邮件
    try:
        autoEmail.sendEmail()
    except Exception as e:
        print(e)
        print('邮件发送失败')

if __name__ == '__main__':

    # 这里的字体采用能识别中文的字体
    # font_file = '‪C:\Windows\Fonts\simhei.ttf' # windows使用这个
    font_file = '/home/felix/.local/share/fonts/SIMHEI.TTF'
    checkcode = CheckCode(font_file=font_file, is_simple=True, char_length=random.randint(3, 5))

    # 生成多张验证码
    for i in range(1):
        checkcode.draw_pic()
        # 生成的验证码的内容
        codes = checkcode.get_codes()
        print(i, codes)
utils.py
{% load staticfiles %}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <!-- 根据屏幕自动响应布局 -->
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>
        {#  用来放标题  #}
        {% block title %}

        {% endblock %}
    </title>
    {# 加载css代码 #}
    <link rel="stylesheet" href="{% static 'bootstrap4.1/bootstrap.min.css' %}">
    <link rel="stylesheet" href="{% static 'css/base.css' %}">

    {# js代码放在后面可以增加性能 #}
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="{% static 'js/jquery-3.3.1.min.js' %}"></script>
    <script src="{% static 'bootstrap4.1/popper.min.js' %}"></script>
    <script src="{% static 'bootstrap4.1/bootstrap.min.js' %}"></script>

    {% block header_extends %}
        {#    用来做头部扩展,如加载某些静态文件     #}
    {% endblock %}
</head>
<body>

{# 导航栏 #}
<nav class="navbar navbar-expand-lg navbar-light bg-light sticky-top">
    <a class="navbar-brand" href="{% url 'home' %}">Felix Blog</a>

    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
    </button>


    <div class="collapse navbar-collapse" id="navbarSupportedContent">
        <ul class="navbar-nav mr-auto">
            <li class="nav-item nav-home"><a href="{% url 'home' %}" class="nav-link">首页</a></li>
            <li class="nav-item nav-blog"><a href="{% url 'blog_list' %}" class="nav-link">博客</a></li>
        </ul>

        <ul class="navbar-nav">
            {% if not user.is_authenticated %}
                <li class="nav-item">
                    <a class="nav-link" href="{% url 'login' %}?from={{ request.get_full_path }}">登录</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="{% url 'register' %}?from={{ request.get_full_path }}">注册</a>
                </li>
            {% else %}
                <li class="nav-item dropdown">
                    <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button"
                       data-toggle="dropdown"
                       aria-haspopup="true" aria-expanded="false">
                        {{ user.username }}
                        {% if user.has_nickname %}
                            ({{ user.get_nickname }})
                        {% endif %}
                    </a>
                    <div class="dropdown-menu" aria-labelledby="navbarDropdown">
                        <a class="dropdown-item" href="{% url 'user_info' %}">个人资料</a>
                        {% if user.is_staff or user.is_superuser %}
                            <a class="dropdown-item" href="{% url 'admin:index' %}">后台管理</a>
                        {% endif %}
                        <div class="dropdown-divider"></div>
                        <a class="dropdown-item" href="{% url 'logout' %}?from={{ request.get_full_path }}">登出</a>
                    </div>
                </li>
            {% endif %}
        </ul>
    </div>
</nav>

{# 用来放内容 #}
{% block content %}

{% endblock %}

<!-- Modal 登录模态框 -->
<div class="modal fade" id="login_model" tabindex="-1" role="dialog"
     aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
    <div class="modal-dialog modal-dialog-centered" role="document">
        <div class="modal-content">
            <form id="login_model_form" action="" method="post">
                <div class="modal-header">
                    <h5 class="modal-title">登录</h5>
                    <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                        <span aria-hidden="true">&times;</span>
                    </button>
                </div>
                <div class="modal-body">
                    {% csrf_token %}
                    {% for field in login_model_form %}
                        <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                        {{ field }}
                    {% endfor %}
                    <span id="login_model_tip" class="text-danger"></span>
                </div>
                <div class="modal-footer">
                    <button type="submit" class="btn btn-primary">登录</button>
                    <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
                </div>
            </form>
        </div>
    </div>
</div>
<script>
    $('#login_model_form').submit(function (event) {
        $('#login_model_tip').text('');
        event.preventDefault(); // 阻止原事件提交
        $.ajax({
            url: '{% url 'login_for_model' %}',
            type: 'POST',
            data: $(this).serialize(),
            cache: false,
            success: function (data) {
                if (data['status'] === 'SUCCESS') {
                    window.location.reload();
                } else {
                    $('#login_model_tip').text('用户名或密码不正确')
                }
            }
        });
    })
</script>

{# 导入资源建议放在js代码前 #}
{# 用来放js代码 #}
{% block js %}

{% endblock %}

</body>
</html>
templates下的base.html
{% extends 'base.html' %}

{% block title %}
    {{ page_title }}
{% endblock %}

{% block content %}
    <div class="container">
        <div class="col-xl-6 offset-xl-3">
            <div class="card">
                <h5 class="card-header">{{ form_title }}</h5>
                <div class="card-body">
                    <form action="" method="post">
                        {% csrf_token %}
                        {% for field in form %}
                            {% if not field.is_hidden %}
                                <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                            {% endif %}
                            {{ field }}
                            <p class="text-danger">{{ field.errors.as_text }}</p>
                        {% endfor %}
                        <span id="error-tip" class="text-danger">{{ form.non_field_errors }}</span>
                        <div class="clearfix"></div>
                        <div class="float-left">
                            {% block other-buttons %}{% endblock %}
                        </div>
                        <div class="float-right">
                            <input type="submit" value="{{ submit_text }}" class="btn btn-primary">
                            <button class="btn" onclick="window.location.href='{{ return_back_url }}'">返回</button>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
{% endblock %}

{% block js %}
    {# 将首页这个按钮设置激活状态 #}
    <script>
        $(".nav-home").addClass("active").siblings().removeClass("active");
    </script>
{% endblock %}
templates下的form.html
{% extends 'form.html' %}

{% block other-buttons %}
    <div id="send_code" class="btn btn-primary">发送验证码</div>
{% endblock %}

{% block js %}
    <script>
        $("#send_code").click(function () {
            if ($(this).hasClass('disabled')) {
                return false;
            }

            let email = $('#id_email').val();
            if (email === '') {
                $('#error-tip').text('邮箱不能为空');
                return false
            }
            let re_email = /^([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+\.[a-zA-Z]{2,3}$/;
            if (!re_email.test(email)) {
                alert('邮箱格式不正确');
                return false
            }

            // 发送验证码
            $.ajax({
                url: "{% url 'send_verification_code' %}",
                type: 'GET',
                data: {
                    'email': email,
                },
                cache: false,
                success: function (data) {
                    if (data['status'] === 'ERRORS') {
                        alert(data['msg']);
                    } else {
                        alert(data['msg']);
                    }

                }
            });

            // # 把按钮变灰
            $(this).addClass('disabled');
            $(this).attr("disabled", true);
            let time = 60;
            let interval = setInterval(() => {
                time -= 1;
                $(this).text(`再次发送(${time}s)`);
                if (time <= 0) {
                    // 时间等于0,进行复原
                    clearInterval(interval);
                    $(this).removeClass('disabled');
                    $(this).attr('disabled', false);
                    $(this).text('再次发送');
                    return false;
                }
            }, 1000);

        });
    </script>
{% endblock %}
user下的bind_email.html
{% extends 'base.html' %}

{% block title %}
    个人资料
{% endblock %}

{% block content %}
    <div class="container">
        <div class="col-xl-8 offset-xl-2">
            <h2>{{ user.username }}</h2>
            {% if user.is_authenticated %}
                <ul>
                    <li>昵称:{{ user.get_nickname }} <a
                            href="{% url 'change_nickname' %}?from={{ request.get_full_path }}">修改昵称</a></li>
                    <li>
                        邮箱:
                        {% if user.email %}
                            {{ user.email }}
                        {% else %}
                            未绑定 <a href="{% url 'bind_email' %}?from={{ request.get_full_path }}">绑定邮箱</a>
                        {% endif %}
                    </li>
                    <li>上一次登录时间:{{ user.last_login|date:"Y-m-d H:i:s" }}</li>
                    <li><a href="">修改密码</a></li>
                </ul>
            {% else %}
                {# 未登录跳转到首页 #}
                <script>
                    window.location.href = '{% url 'home' %}'
                </script>
            {% endif %}
        </div>
    </div>
    </div>
{% endblock %}

{% block js %}
    {# 将首页这个按钮设置激活状态 #}
    <script>
        $(".nav-home").addClass("active").siblings().removeClass("active");
    </script>
{% endblock %}
user_info.html
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.models import User

from .models import Profile


# Define an inline admin descriptor for Employee model
# which acts a bit like a singleton
class ProfileInline(admin.StackedInline):
    model = Profile
    can_delete = False


# Define a new User admin
class UserAdmin(BaseUserAdmin):
    inlines = (ProfileInline,)
    list_display = ('username', 'nickname', 'email', 'is_staff', 'is_active', 'is_superuser')

    def nickname(self, obj):
        return obj.profile.nickname

    nickname.short_description = '昵称'  # 后台字段


# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, UserAdmin)


@admin.register(Profile)
class ProfileAdmin(admin.ModelAdmin):
    list_display = ('user', 'nickname')
user下的admin.py
# -*- coding: utf-8 -*-
# @Time    : 18-11-20 下午8:10
# @Author  : Felix Wang

import re
from django import forms
from django.contrib import auth
from django.contrib.auth.models import User


class LoginForm(forms.Form):
    username = forms.CharField(label='用户名', required=True,
                               widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': '请输入用户名'}))
    # widget指定input标签类型
    password = forms.CharField(label='密码',
                               widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': '请输入密码'}))

    def clean(self):  # 验证数据
        username = self.cleaned_data['username']
        password = self.cleaned_data['password']
        user = auth.authenticate(username=username, password=password)
        if user is None:
            raise forms.ValidationError('用户名或密码错误')
        self.cleaned_data['user'] = user  # 将验证过的user放入clean_data
        return self.cleaned_data


class RegisterForm(forms.Form):
    # 用户名字段
    username = forms.CharField(label='用户名',
                               max_length=30,
                               min_length=3,
                               required=True,
                               widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': '请输入用户名'}))
    # 邮箱字段
    email = forms.EmailField(label='邮箱',
                             min_length=3,
                             required=True,
                             widget=forms.EmailInput(attrs={'class': 'form-control', 'placeholder': '请输入邮箱'}))
    # 密码字段
    password = forms.CharField(label='密码',
                               min_length=6,
                               required=True,
                               widget=forms.PasswordInput(
                                   attrs={'class': 'form-control', 'placeholder': '请输入密码'}))
    # 再次输入密码
    password_again = forms.CharField(label='确认密码',
                                     min_length=6,
                                     required=True,
                                     widget=forms.PasswordInput(
                                         attrs={'class': 'form-control', 'placeholder': '请再输入一次密码'}))

    def clean_username(self):
        username = self.cleaned_data['username']
        if User.objects.filter(username=username).exists():
            raise forms.ValidationError('用户名已存在')
        return username

    def clean_email(self):
        email = self.cleaned_data['email']
        if User.objects.filter(email=email).exists():
            raise forms.ValidationError('邮箱已存在')
        if not re.match(r'^[0-9a-zA-Z_]{0,19}@[0-9a-zA-Z]{1,13}\.[com,cn,net]{1,3}$', email):
            raise forms.ValidationError('邮箱格式错误')
        return email

    def clean_password_again(self):
        password = self.cleaned_data['password']
        password_again = self.cleaned_data['password_again']
        if password != password_again:
            raise forms.ValidationError('两次输入的密码不一致')
        return password_again


class ChangeNicknameForm(forms.Form):
    nickname_new = forms.CharField(
        label='新的昵称',
        max_length=20,
        widget=forms.TextInput(
            attrs={
                'class': 'form-control',
                'placeholder': '请输入新的昵称',
            }
        )
    )

    def __init__(self, *args, **kwargs):
        if 'user' in kwargs:
            self.user = kwargs.pop('user')
        super().__init__(*args, **kwargs)

    # 表单验证
    def clean(self):
        # 判断用户是否登录
        if self.user.is_authenticated:
            self.cleaned_data['user'] = self.user
        else:
            raise forms.ValidationError('用户尚未登录')
        return self.cleaned_data

    def clean_nickname_new(self):
        nickname_new = self.cleaned_data.get('nickname_new', '').strip()
        if nickname_new == '':
            raise forms.ValidationError('新的昵称不能为空')
        return nickname_new


class BindEmailForm(forms.Form):
    email = forms.EmailField(
        label='邮箱',
        widget=forms.EmailInput(
            attrs={
                'class': 'form-control',
                'placeholder': '请输入邮箱',
            }
        )
    )

    verification_code = forms.CharField(
        label='验证码',
        max_length=20,
        required=False,
        widget=forms.TextInput(
            attrs={
                'class': 'form-control',
                'placeholder': '请输入验证码',
            }
        )
    )

    def __init__(self, *args, **kwargs):
        if 'requests' in kwargs:
            self.requests = kwargs.pop('requests')
        super().__init__(*args, **kwargs)

    # 表单验证
    def clean(self):
        # 判断用户是否登录
        if self.requests.user.is_authenticated:
            self.cleaned_data['user'] = self.requests.user
        else:
            raise forms.ValidationError('用户尚未登录')

        # 判断用户是否已经绑定邮箱
        if self.requests.user.email != '':
            raise forms.ValidationError('你已经绑定邮箱')

        # 判断验证码
        code = self.requests.session.get('bind_email_code', '').upper()
        print('code', code)
        verification_code = self.cleaned_data.get('verification_code', '').upper()
        print('verification_code', verification_code)
        if code != verification_code or code == '':
            raise forms.ValidationError('验证码不正确')
        return self.cleaned_data

    def clean_email(self):
        email = self.cleaned_data['email']
        if User.objects.filter(email=email).exists():
            raise forms.ValidationError('该邮箱已经被绑定')
        if not re.match(r'^[0-9a-zA-Z_]{0,19}@[0-9a-zA-Z]{1,13}\.[com,cn,net]{1,3}$', email):
            raise forms.ValidationError('邮箱格式错误')
        return email

    def clean_verification_code(self):
        verification_code = self.cleaned_data.get('verification_code', '').strip().upper()
        if verification_code == '':
            raise forms.ValidationError('验证码不能为空')
        return verification_code
forms.py
from django.db import models
from django.contrib.auth.models import User


class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    nickname = models.CharField(max_length=20, verbose_name='昵称', default='')

    def __str__(self):
        return '<Profile: {} for {}>'.format(self.nickname, self.user.username)


def get_nickname(self):
    if Profile.objects.filter(user=self).exists():
        profile = Profile.objects.get(user=self)
        return profile.nickname
    else:
        return ''


def get_nickname_or_username(self):
    if Profile.objects.filter(user=self).exists():
        profile = Profile.objects.get(user=self)
        return profile.nickname
    else:
        return self.username


def has_nickname(self):
    return Profile.objects.filter(user=self).exists()


User.get_nickname = get_nickname  # 动态绑定方法
User.has_nickname = has_nickname  # 动态绑定方法
User.get_nickname_or_username = get_nickname_or_username  # 动态绑定方法
models.py
# -*- coding: utf-8 -*-
# @Time    : 18-11-4 下午5:22
# @Author  : Felix Wang

from django.urls import path
from . import views

urlpatterns = [
    path('login/', views.login, name='login'),  # 登录
    path('logout/', views.logout, name='logout'),  # 登录
    path('login_for_model/', views.login_for_model, name='login_for_model'),  # 登录
    path('register/', views.register, name='register'),  # 注册
    path('user_info/', views.user_info, name='user_info'),  # 用户信息
    path('change_nickname/', views.change_nickname, name='change_nickname'),  # 更改昵称
    path('bind_email/', views.bind_email, name='bind_email'),  # 更改昵称
    path('send_verification_code/', views.send_verification_code, name='send_verification_code'),  # 更改昵称
]
urls.py
# -*- coding: utf-8 -*-
# @Time    : 18-11-7 下午4:12
# @Author  : Felix Wang
import random
import re
import time
from django.shortcuts import render, redirect
from django.http import JsonResponse
from django.contrib import auth
from django.contrib.auth.models import User
from django.urls import reverse
from django.conf import settings
from .forms import LoginForm, RegisterForm, ChangeNicknameForm, BindEmailForm
from .models import Profile
from myblog.utils import AutoSendEmail


def login(requests):
    # 如果是form表单提交验证登录
    if requests.method == 'POST':
        login_form = LoginForm(requests.POST)
        if login_form.is_valid():  # 验证是否通过
            # 因为在form表单验证过了,所以不用自己再验证
            user = login_form.cleaned_data.get('user')
            auth.login(requests, user)
            return redirect(requests.GET.get('from', reverse('home')))
        else:
            login_form.add_error(None, '用户名或密码不正确')
    else:
        login_form = LoginForm()
    context = {
        'login_form': login_form,
    }
    return render(requests, 'user/login.html', context)


def login_for_model(requests):
    login_form = LoginForm(requests.POST)

    # 如果是form表单提交验证登录
    if login_form.is_valid():  # 验证是否通过
        # 因为在form表单验证过了,所以不用自己再验证
        user = login_form.cleaned_data.get('user')
        auth.login(requests, user)

        data = {
            'status': 'SUCCESS',
        }
    else:
        data = {
            'status': 'ERROR',
        }
    return JsonResponse(data)


def register(requests):
    if requests.method == 'POST':
        reg_form = RegisterForm(requests.POST)
        if reg_form.is_valid():
            username = reg_form.cleaned_data['username']
            email = reg_form.cleaned_data['email']
            password = reg_form.cleaned_data['password']

            # 创建用户
            user = User.objects.create_user(username=username, email=email, password=password)
            user.save()

            # 登录用户
            user = auth.authenticate(username=username, password=password)
            auth.login(requests, user)
            # 登录之后跳转
            return redirect(requests.GET.get('from', reverse('home')))
    else:
        reg_form = RegisterForm()

    context = {
        'reg_form': reg_form,
    }
    return render(requests, 'user/register.html', context)


def logout(requests):
    auth.logout(requests)
    return redirect(requests.GET.get('from', reverse('home')))


def user_info(requests):
    context = {}
    return render(requests, 'user/user_info.html', context)


def change_nickname(requests):
    redirect_to = requests.GET.get('from', reverse('home'))

    if requests.method == 'POST':
        form = ChangeNicknameForm(requests.POST, user=requests.user)
        if form.is_valid():
            nickname_new = form.cleaned_data['nickname_new']
            profile, created = Profile.objects.get_or_create(user=requests.user)
            profile.nickname = nickname_new
            profile.save()
            return redirect(redirect_to)
    else:
        form = ChangeNicknameForm()

    context = {
        'submit_text': '修改',
        'page_title': '修改昵称',
        'form_title': '修改昵称',
        'form': form,
        'return_back_url': redirect_to,

    }
    return render(requests, 'form.html', context)


def bind_email(requests):
    redirect_to = requests.GET.get('from', reverse('home'))

    if requests.method == 'POST':
        form = BindEmailForm(requests.POST, requests=requests)
        if form.is_valid():
            email = form.cleaned_data['email']
            requests.user.email = email
            requests.user.save()
            return redirect(redirect_to)
    else:
        form = BindEmailForm()

    context = {
        'submit_text': '绑定邮箱',
        'page_title': '绑定邮箱',
        'form_title': '绑定',
        'form': form,
        'return_back_url': redirect_to,

    }
    return render(requests, 'user/bind_email.html', context)


def send_verification_code(requests):
    email = requests.GET.get('email', '')
    if re.match(r'^[0-9a-zA-Z_]{0,19}@[0-9a-zA-Z]{1,13}\.[com,cn,net]{1,3}$', email):
        # 生成验证码
        all_codes = list(range(0x30, 0x39)) + list(range(0x61, 0x74)) + list(range(0x41, 0x5a))  # 大写,小写和数字
        code = ''.join([chr(random.choice(all_codes)) for i in range(6)])
        now = int(time.time())
        send_code_time = requests.session.get('send_code_time', 0)
        if now - send_code_time < 60:
            data = {
                'status': 'ERROR',
            }
        else:
            requests.session['bind_email_code'] = code
            requests.session['send_code_time'] = send_code_time
            title = '验证码'
            auto_email = AutoSendEmail(sender=settings.EMAIL_HOST_USER, recever=[email],
                                       password=settings.EMAIL_HOST_PASSWORD, title=title, from_who=settings.FROM_WHO,
                                       smtp_server=settings.MAIL_HOST, port=settings.EMAIL_PORT)
            html = """
                        <html>
                          <head></head>
                          <body>
                            <p>Hi!<br>
                               非常感谢您绑定邮箱!
                               <br>
                               本次的验证码是:{},请不要透露给其他人!
                               <br>
                            </p>
                            <img style="width:180px;height:240px" src="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1541440161574&di=fd6156e441788866ffbd6c654d75fa23&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fblog%2F201507%2F22%2F20150722222322_Ky8Nj.jpeg" />
                          </body>
                        </html>
                        """.format(code)

            # 以html的形式发送文字,推荐这个,因为可以添加图片等
            auto_email.addHtml(html)
            # 发送邮件
            try:
                auto_email.sendEmail()
                data = {
                    'status': 'SUCCESS',
                    'msg': '邮件发送成功',
                }
            except Exception as e:
                data = {
                    'status': 'ERRORS',
                    'msg': '邮件发送失败',
                }
    else:
        data = {
            'status': 'ERRORS',
            'msg': '邮箱格式不正确',
        }

    return JsonResponse(data)
user下的views.py

 

posted @ 2018-11-29 11:53  寂静的天空  阅读(486)  评论(0编辑  收藏  举报
个人感悟: 一个人最好的镜子就是自己,你眼中的你和别人眼中的你,不是一回事。有人夸你,别信;有人骂你,别听。一根稻草,扔街上就是垃圾;捆上白菜就是白菜价;捆上大闸蟹就是大闸蟹的价。 一个人,不狂是没有出息的,但一直狂,肯定是没有出息的。雨打残花风卷流云,剑影刀光闪过后,你满脸冷酷的站在珠峰顶端,傲视苍生无比英武,此时我问你:你怎么下去? 改变自己就是改变自己的心态,该沉的时候沉下去,该浮的时候浮上来;不争名夺利,不投机取巧,不尔虞我诈;少说、多听、多行动。人每所谓穷通寿夭为命所系,岂不知造物之报施,全视人之自取。 座佑铭:每一个不曾起舞的日子,都是对生命的辜负。