基於 Django2 實現郵箱注冊登錄功能


1. 開發環境

Python 3.6.5
Django 2.2

2. 項目功能

  • 用戶登錄
  • 郵箱注冊
  • 圖形驗證碼
  • 找回密碼
  • 修改密碼
  • 用戶退出

3. 項目創建

首先創建項目:

django-admin startproject djangoLoginRegister

創建app:

python manage.py startapp users 

(根目錄新建apps文件夾,將以上 users 拖到 apps 下面)

文件路徑設置:

在/settings.py中:

import os
import sys

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

sys.path.insert(0, BASE_DIR)
sys.path.insert(0, os.path.join(BASE_DIR, 'apps')) 

4. 用戶Models 設計

可以選擇在系統用戶表 users 的基礎上擴展新的用戶表,系統自動生成的users如下:

          id:  主鍵
    password:  密碼
  last_login:  最后登錄時間
is_superuser:  是否是超級用戶(管理員)
    username:  用戶名
  first_name:  
   last_name:
       email:  用戶郵箱 
    is_staff:  后台管理員工
   is_active:  是否激活(例如郵箱激活)
 date_joined:  注冊時間

在上面的基礎上,擴展users

users/models.py 添加UserProfile(用戶模型)和 EmailVerifyRecord(圖形驗證碼模型):

from datetime import datetime

from django.db import models
from django.contrib.auth.models import AbstractUser


class UserProfile(AbstractUser):
    """
    用戶信息
    通過 AbstractUser 繼承 django 原有自帶的 User 類
    """
    gender_choices = (
        ('male', '男'),
        ('female', '女'),
    )

    nick_name = models.CharField('昵稱', max_length=50, default='')
    birthday = models.DateField('生日',null=True,blank=True)
    gender = models.CharField('性別',max_length=10,choices=gender_choices,default='female')
    adress = models.CharField('地址',max_length=100,default='')
    mobile = models.CharField('手機號',max_length=11,null=True,blank=True)

    class Meta:
        verbose_name = '用戶信息'
        verbose_name_plural = verbose_name

    def __str__(self): 
        return self.username
        
        
class EmailVerifyRecord(models.Model):
    """
    圖形驗證碼
    """
    send_choices = (
        ('register','注冊'),
        ('forget','找回密碼')
    )

    code = models.CharField('驗證碼',max_length=20)
    email = models.EmailField('郵箱',max_length=50)
    send_type = models.CharField(choices=send_choices,max_length=10)
    send_time = models.DateTimeField(default=datetime.now)

    class Meta:
        verbose_name = '郵箱驗證碼'
        verbose_name_plural = verbose_name



注冊app

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles', 
    'users',
]

重載 AUTH_USER_MODEL

AUTH_USER_MODEL = 'users.UserProfile'

遷移數據庫

python manage.py makemigrations

python manage.py migrate

執行 python manage.py runserver 查看是否能執行成功

5. 前端文件創建

創建templates文件夾,用了存放html文件;
創建static文件夾,用來存放js、css、image等靜態文件

並在 settings.py 中設置 template 和 static 路徑:

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',
            ],
        },
    },
]
STATIC_URL = '/static/'
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, "static"),
)

6. 首頁和登錄頁

6.1 頁面

首頁:

<!DOCTYPE html>
<html lang="en">

{% load static %}

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>首頁</title>
    <link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
    <link href="{% static 'css/style.css' %}" rel="stylesheet">
</head>
<body>
    <p> 首頁 </p>

    <div>
        {% if name %}

            歡迎,{{name}}
            <a href="{% url 'logout' %}" >  退出 </a>

        {% else %}

            <a href="/login/" class="btn btn-success">登錄</a>
            <a href="/register/" class="btn btn-primary">注冊</a>

        {% endif %}
    </div>

    <script src="{% static 'js/jquery.min.js' %}"></script>
    <script src="{% static 'js/bootstrap.min.js' %}"></script> 
</body>
</html>

登錄頁:

<!DOCTYPE html>
<html lang="en">

{% load static %}

<head>
    <meta charset="UTF-8"> 
    <title>登錄</title> 
</head>

<body>
    <form class="form-horizontal" role="form" action="{% url 'login' %}" method="post" >
        <div class="form-group {% if login_form.errors.username %}errorput{% endif %}">
            <label for="username" class="col-sm-2 control-label">name</label>
            <div class="col-sm-10">
                <input type="text" class="form-control" name="username" id="username" value="{{ login_form.username.value }}" placeholder="手機號/郵箱">
            </div>
        </div>
        <div class="form-group {% if login_form.errors.password %}errorput{% endif %}">
            <label for="password" class="col-sm-2 control-label">pass</label>
            <div class="col-sm-10">
                <input type="password" class="form-control" name="password" id="password" value="{{ login_form.password.value }}" placeholder="">
            </div>
        </div>

        <div class="error btns login-form-tips" id="jsLoginTips">
            {% for key,error in login_form.errors.items %}
            <div style="color: red">
                <span>{{ key }}</span>: <span>{{ error }}</span>
            </div>

            {% endfor %}

            {% if msg %}
                登錄失敗: {{ msg }}
            {% endif %}
        </div>
        <div class="form-group">
            <div class="col-sm-offset-2 col-sm-10">
                <button type="submit" class="btn btn-default">Login</button>
            </div>
        </div>
        {% csrf_token %}
    </form>
    <div style="text-align: left;">
        <a href="{% url 'register' %}">去注冊</a>
        <a href="{% url 'forget_pwd' %}">忘記密碼?</a>
    </div>
 
</body>
</html>

6.2 路由設計

from django.conf.urls import url, include 
from django.contrib import admin
from django.urls import path

from django.views.generic import TemplateView
from users.views import LoginView, LogoutView

urlpatterns = [
    path('admin/', admin.site.urls),
    url('^$', TemplateView.as_view(template_name='index.html'), name='index'),
    url('^login/$', LoginView.as_view(), name='login'),
    url('^logout/$', LogoutView.as_view(), name='logout'), 
]

6.3 自定義表單驗證規則

users目錄下新建forms.py :

from django import forms

# 圖形驗證碼
from captcha.fields import CaptchaField

class LoginForm(forms.Form):
    """
    登錄驗證表單
    """
    # 用戶名密碼不能為空
    username = forms.CharField(required=True)
    password = forms.CharField(required=True, min_length=3)

6.4 views 設計

class LogoutView(auth_views.LogoutView):
    """
    退出
    """
    def get(self, request, *args, **kwargs):
        auth_logout(request)
        return HttpResponseRedirect('/login/')


class LoginView(View):
    """
    登錄
    """
    def get(self,request):
        return render(request, 'login.html')

    def post(self,request):
        # 實例化
        login_form = LoginForm(request.POST)
        if login_form.is_valid():
            # 獲取用戶提交的用戶名和密碼
            # request.POST.get('username', '') 獲取 username 如果不存在則指定默認值 ''
            user_name = request.POST.get('username', '')
            pass_word = request.POST.get('password', '')
            print('user_name:', user_name)
            print('pass_word:', pass_word)

            # 成功返回user對象,失敗None
            user = authenticate(username=user_name, password=pass_word)
            print(user)
            # 如果不是null說明驗證成功
            if user is not None:
                if user.is_active:
                    print('郵箱已激活,並且登錄成功')
                    # 登錄
                    login(request, user)
                    return render(request, 'index.html', {'name': user_name})
                else:
                    print('郵箱未激活,登錄失敗')
                    return render(request, 'login.html', {'msg': '郵箱未激活,登錄失敗', 'login_form':login_form})
            else:
                print('登錄失敗')
                return render(request, 'login.html', {'msg': '用戶名或密碼錯誤', 'login_form':login_form})
        else:
            return render(request, 'login.html', {'login_form':login_form})

7. 注冊

用戶注冊流程為:填入郵箱、密碼、驗證碼,點擊注冊,發送激活郵件,用戶激活后,即注冊成功。

7.1 register.html:

<!DOCTYPE html>
<html lang="en">

{% load static %}

<head>
    <meta charset="UTF-8">  
</head>

<body>
    <form class="form-horizontal" role="form" method="POST" action="{% url 'register' %}">
        <div class="form-group {% if register_form.errors.email %}errorput{% endif %}">
            <label  class="col-sm-2 control-label">email</label>
            <div class="col-sm-10">
                <input type="text" class="form-control" id="email" name="email" placeholder="請輸入郵箱">
            </div>
        </div>
        <div class="form-group {% if register_form.errors.password %}errorput{% endif %}">
            <label  class="col-sm-2 control-label">pass</label>
            <div class="col-sm-10">
                <input type="password" class="form-control" id="password" name="password" placeholder="請輸入密碼">
            </div>
        </div>
        <div class="form-group {% if register_form.errors.captcha %}errorput{% endif %}"> 
            <div class="col-sm-10"> 
                {{ register_form.captcha }}
            </div>
        </div>

        <div class="error btns" id="jsEmailTips">
            {% for key,error in register_form.errors.items %}
                {{ key }} - {{ error }}
            {% endfor %}
            {{ msg }}
        </div>

        <div class="form-group">
            <div class="col-sm-12">
                <button type="submit" class="btn btn-default">Register</button>
            </div>
        </div>
        {% csrf_token %}
    </form>
    <div>
        <a href="{% url 'login' %}">去登錄</a>
    </div> 
</body> 
</html>

7.2 圖形驗證碼

使用 django 第三方庫:

pip install  django-simple-captcha

settings.py 中設置:

INSTALLED_APPS = [
    ...
    
    'captcha', 
]

添加url:

urlpatterns = [
    path('captcha/',include('captcha.urls')),
]

遷移數據庫:

python manage.py makemigrations

python manage.py migrate

注冊頁中顯示驗證碼:

forms.py:

# 圖形驗證碼
from captcha.fields import CaptchaField


class RegisterForm(forms.Form):
    """
    注冊驗證表單
    """
    email = forms.EmailField(required=True)
    password = forms.CharField(required=True, min_length=3)
    captcha = CaptchaField()
<div class="col-sm-10"> 
    {{ register_form.captcha }}
</div>

7.3 注冊 views

class RegisterView(View):
    """
    注冊
    """
    def get(self,request):
        register_form = RegisterForm()
        return render(request, 'register.html', {'register_form': register_form})

    def post(self, request):
        register_form = RegisterForm(request.POST)
        if register_form.is_valid():
            user_name = request.POST.get('email', '')
            # 用戶已存在
            if UserProfile.objects.filter(email = user_name):
                return render(request, 'register.html', {'register_form': register_form, 'msg': '用戶名已存在'})

            pass_word = request.POST.get('password', '')

            print('user_name:', user_name)
            print('pass_word:', pass_word)

            # 實例化一個 useProfile 對象
            user_profile = UserProfile()
            user_profile.username = user_name
            user_profile.email = user_name
            # 默認添加的用戶是激活狀態(is_active=1表示True),這里修改默認的狀態為 False,只有用戶郵箱激活后才改為True
            user_profile.is_active = False
            # 密碼加密
            user_profile.password = make_password(pass_word)
            user_profile.save()
            send_register_email(user_name, 'register')
            return render(request, 'login.html')
        else:
            return render(request, 'register.html', {'register_form': register_form})
             

7.4 郵箱激活

settings.py中配置:

EMAIL_HOST = "smtp.qq.com"
EMAIL_PORT = 25
EMAIL_HOST_USER = "****@qq.com"   # 郵箱
EMAIL_HOST_PASSWORD = "************"   # 郵箱授權碼
EMAIL_USE_TLS= True
EMAIL_FROM = "****@qq.com"  # 郵箱

定義發送郵件方法

在apps下新建 utils/email_send.py:

from random import Random
from django.core.mail import send_mail

from users.models import EmailVerifyRecord
from MxOnline190409.settings import EMAIL_FROM


def random_str(random_length=8):
    str = ''
    # 生成字符串的可選字符串
    chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
    length = len(chars) - 1
    random = Random()
    for i in range(random_length):
        str += chars[random.randint(0, length)]
    return str


# 發送注冊郵件
def send_register_email(email, send_type="register"):
    # 發送之前先保存到數據庫,到時候查詢鏈接是否存在
    # 實例化一個EmailVerifyRecord對象
    email_record = EmailVerifyRecord()
    # 生成隨機的code放入鏈接
    code = random_str(16)
    email_record.code = code
    email_record.email = email
    email_record.send_type = send_type

    email_record.save()

    # 定義郵件內容:
    email_title = ""
    email_body = ""

    if send_type == "register":
        email_title = "django - 注冊激活鏈接"
        email_body = "請點擊下面的鏈接激活你的賬號: http://127.0.0.1:8888/active/{0}".format(code)

        # 使用Django內置函數完成郵件發送。四個參數:主題,郵件內容,發件人郵箱地址,收件人(是一個字符串列表)
        send_status = send_mail(email_title, email_body, EMAIL_FROM, [email])
        # 如果發送成功
        if send_status:
            pass

    if send_type == "forget":
        email_title = "django - 找回密碼"
        email_body = "請點擊下面的鏈接找回你的密碼: http://127.0.0.1:8888/reset/{0}".format(code) 
        send_status = send_mail(email_title, email_body, EMAIL_FROM, [email])
        # 如果發送成功
        if send_status:
            pass

激活郵件 views:

class ActiveUserView(View):
    """
    激活郵件
    """
    def get(self, request, active_code):
        # 查詢郵箱驗證記錄是否存在
        all_record = EmailVerifyRecord.objects.filter(code=active_code)

        if all_record:
            for record in all_record:
                # 獲取到對應郵箱
                email = record.email
                # 查找到郵箱對應的 user
                user = UserProfile.objects.get(email=email)
                user.is_active = True
                user.save()
        # 驗證碼不對的時候跳轉到激活失敗頁面
        else:
            return render(request, 'email_active_fail.html')

        # 激活成功 跳轉到登錄頁面
        return render(request, 'login.html')

7.5 添加注冊和激活的url

from users.views import LoginView, LogoutView, RegisterView, ActiveUserView 


urlpatterns = [ 
    url('^register/$', RegisterView.as_view(), name='register'),
    url('captcha/', include('captcha.urls')),
    url(r'^active/(?P<active_code>.*)/$', ActiveUserView.as_view(), name="user_active"), 
]

8. 找回密碼

8.1 html

找回密碼頁面html:

<!DOCTYPE html>
<html lang="en">

{% load static %}

<head>
    <meta charset="UTF-8"> 
    <title>忘記密碼</title> 
</head>

<body>
    <form class="form-horizontal" role="form" method="POST" action="{% url 'forget_pwd' %}">
        <div class="form-group {% if forget_form.errors.email %}errorput{% endif %}">
            <label  class="col-sm-2 control-label">email</label>
            <div class="col-sm-10">
                <input type="text" class="form-control" id="email" name="email" value="{{ forget_form.email.value }}" placeholder="請輸入郵箱">
            </div>
        </div>
        <div class="form-group captcha1 {% if forget_form.errors.captchal %}errorput{% endif %}">
            <label>輸入驗證碼</label>
            {{ forget_form.captcha }}
        </div>

        <div class="error btns" id="jsForgetTips">
            {% for key,error in forget_form.errors.items %}
                {{key}} - {{ error }}
            {% endfor %}
            {{ msg }}
        </div>

        <div class="form-group">
            <div class="col-sm-12">
                <button type="submit" class="btn btn-default">確認</button>
            </div>
        </div>
        {% csrf_token %}
    </form>
    <div>
        <a href="/">首頁</a>
    </div> 
</body>

</html>

重置密碼頁面html:

<!DOCTYPE html>
<html lang="en">

{% load static %}

<head>
    <meta charset="UTF-8"> 
    <title>重置密碼</title> 
</head>

<body>
    <h4>修改密碼</h4>
    <p>已經通過驗證,請設置新密碼</p>
    <form id="reset_password_form" action="{% url 'modify_pwd' %}" method="post">
        <ul>
            <li class="{% if modify_form.errors.password1 %}errorput{% endif %}">
                <span class="">請輸入新密碼 :</span>
                <input type="password" name="password1" id="pwd">
                <i></i>
            </li>
            <input type="hidden" name="email" value="{{ email }}">
            <li class="{% if modify_form.errors.password2 %}errorput{% endif %}">
                <span class="">請確定新密碼:</span>
                <input type="password" name="password2" id="repwd">
                <i></i>
            </li>
            <div class="error btns" id="jsPasswdResetTips">
                {% for key,error in modify_form.errors.items %}
                    {{ key }}:{{ error }}
                {% endfor %}
                {{ msg }}
            </div>
            <li class="button">
                <input type="submit" value="提交">
            </li>
        </ul>
        {% csrf_token %}
    </form>
    <div>
        <a href="/">首頁</a>
    </div> 
</body>

</html>

8.3 urls

urlpatterns = [  
    # 忘記密碼
    url('^forget/$', ForgetPwdView.as_view(), name='forget_pwd'),
    # 重置密碼時發送的郵箱鏈接
    url(r'^reset/(?P<active_code>.*)/$', ResetView.as_view(), name="reset_pwd"),
    # 重置密碼
    url('^modify_pwd/$', ModifyPwdView.as_view(), name='modify_pwd'),
]

8.4 forms.py


class ForgetPwdForm(forms.Form):
    """
    忘記密碼表單
    """
    email = forms.EmailField(required=True)
    captcha = CaptchaField(error_messages={'invalid': '驗證碼錯誤'})


class ModifyPwdForm(forms.Form):
    """
    重置密碼
    """
    password1 = forms.CharField(required=True, min_length=3)
    password2 = forms.CharField(required=True, min_length=3)

8.5 views.py


class ForgetPwdView(View):
    """
    找回密碼
    """
    def get(self,request):
        forget_form = ForgetPwdForm()
        return render(request,'forgetpwd.html',{'forget_form':forget_form})

    def post(self, request):
        forget_form = ForgetPwdForm(request.POST)
        if forget_form.is_valid():
            email = request.POST.get('email', '')
            send_register_email(email, 'forget')
            return render(request, 'send_success.html')
        else:
            return render(request, 'forgetpwd.html', {'forget_form': forget_form})



class ResetView(View):
    """
    打開郵箱鏈接
    """
    def get(self, request, active_code):
        # 查詢郵箱驗證記錄是否存在
        all_record = EmailVerifyRecord.objects.filter(code=active_code)

        if all_record:
            for record in all_record:
                email = record.email
                return render(request, 'password_reset.html', {'email': email})

        # 驗證碼不對的時候跳轉到激活失敗頁面
        else:
            return render(request, 'active_fail.html')
        return render(request, 'login.html')



class ModifyPwdView(View):
    """
    重置密碼
    """
    def post(self, request):
        modify_form = ModifyPwdForm(request.POST)
        if modify_form.is_valid():
            pwd1 = request.POST.get('password1', '')
            pwd2 = request.POST.get('password2', '')
            email = request.POST.get('email', '')

            if pwd1 != pwd2:
                return render(request, 'password_reset.html', {'email': email, 'msg': '兩次輸入密碼不一致'})

            user = UserProfile.objects.get(email = email)
            user.password = make_password(pwd2)
            user.save()

            return render(request, 'login.html')

        else:
            email = request.POST.get('email', '')
            return render(request, 'password_reset.html', {'email': email, "modify_form": modify_form})



最終數據庫中的表格:

+------------------------------------+
| auth_group                         |
| auth_group_permissions             |
| auth_permission                    |
| captcha_captchastore               |
| django_admin_log                   |
| django_content_type                |
| django_migrations                  |
| django_session                     |
| users_emailverifyrecord            |
| users_userprofile                  |
| users_userprofile_groups           |
| users_userprofile_user_permissions |
+------------------------------------+


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM