zoukankan      html  css  js  c++  java
  • 基于 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 |
    +------------------------------------+
    
  • 相关阅读:
    Matlab Tricks(二十九) —— 使用 deal 将多个输入赋值给多个输出
    释名 —— 名称的含义、来源
    Eclipse快捷键 今天又学会了几个不常用的 收藏了
    HDU 2527
    UVAlive 2326 Moving Tables(贪心 + 区间问题)
    STM32关于优先级设定的理解 NVIC_SetPriority()
    linux6.2安装mysql
    【PAT】1009. Product of Polynomials (25)
    Android的重力传感器(3轴加速度传感器)简单实例
    out/target/common/obj/PACKAGING/public_api.txt android.view.KeyEvent.KEYCODE_has changed value from
  • 原文地址:https://www.cnblogs.com/cckui/p/10736954.html
Copyright © 2011-2022 走看看