搭建本身的博客(二十九):增長綁定郵箱的功能,完善用戶信息

一、郵箱服務器使用了騰訊服務器javascript

具體操做見:python自動發郵件css

二、變化的部分html

三、上代碼:java

{# 引用模板 #}
{% 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
相關文章
相關標籤/搜索