Django-从0-1实现注册和登录(包括Session、密码加密等)

说明文中内容是参考here的系列教程,里面比较详细地说明了整个登录功能实现的流程。这里主要是自己整理一下,便于之后参考。

1. 项目目录

项目名为student_system

-student_system
-student_system     # 创建项目时自动生成的与项目名同名的目录
    -__init__.py
    -settings.py
    -urls.py
    -wsgi.py
-library            # 通过命令行创建的应用
    -migrations     # 数据库相关文件,不用管。
    -templates      # 手动创建的目录,用于保存html等文件
    -admin.py
    -apps.py
    -forms.py
    -models.py
    -tests.py
    -urls.py
    -views.py
    -forms.py
-templates
    -login
        -index.html
        -login.html
        -logout.html
        -register.html
-static
    -login
        -css
            -login.css
            -register.css
        -images
-manage.py
-db.sqlite3

2. Django配置

2.1. Django连接MySQL数据库

  1. 首先,在MySQL中创建名称为student_system的数据库
  2. 在项目下面的Settings.py文件中,找到DATABASES这一项,修改为下面的代码
    DATABASES = {
     'default': {
         'ENGINE': 'django.db.backends.mysql',
         'NAME': 'student_system',    #你的数据库名称,是第一步中创建的名称
         'USER': 'root',   #你的数据库用户名
         'PASSWORD': '', #你的数据库密码
         'HOST': 'localhost', #你的数据库主机,留空默认为localhost
         'PORT': '3306', #你的数据库端口
     }
    }
  3. 接着,在当前项目的Python环境中安装pymysql, pip install pymysql
  4. 在项目名同名的目录下面的init.py文件中写入下面的语句。
    import pymysql
    pymysql.install_as_MySQLdb()
    Django默认导入的mysql驱动程序是MySQLdb,但是MySQLDB对Py3支持不全,所以这里使用pymysql。pymysql.install_as_MySQLdb()是将pymysql转化为MySQL。
    此时,数据库就配置完成了

3. 实现登录功能

首先,创建一个应用library。

python manage.py startapp library

然后在settings.py文件中注册应用。

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

    'library'    # 应用名
]

3.1. 设计模型

library/models.py

from django.db import models

# Create your models here.
class User(models.Model):
    gender = (
        (1, '男'),
        (2, '女')
    )

    name = models.CharField(max_length=128, unique=True)
    password = models.CharField(max_length=256)
    email = models.EmailField(unique=True)
    sex = models.IntegerField(choices=gender, default=1)
    c_time = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.name

    class Meta:
        ordering = ['-c_time']   # 创建时间的逆序,也就是创建的越晚,排在上面
        verbose_name = verbose_name_plural = '用户'

3.2. 创建数据库表

在项目目录的终端执行下面两行代码

python manage.py makemigrations
python manage.py migrate

3.3. 搭建Admin后台

首先,在admin中注册。 library/admin.py

from django.contrib import admin
from .models import User

admin.site.register(User)

通过命令行创建超级管理员,输入以下命令,然后按照操作创建即可。

python manage.py createsuperuser

接着启动服务器 python manage.py runserver,然后在浏览器中输入 http://127.0.0.1:8000/admin/ ,使用刚创建的用户名和密码登录进去就可以了。

在后台增加数据的时候可以发现,因为email字段采用的是邮箱验证,因此不合标准的邮箱会提示。

3.4. 路由设计

为了路由结构的清晰性,在library目录下面增加一个urls.py文件,配置如下:

from django.urls import path
from . import views


app_name = 'library'
urlpatterns = [
    path('index/', views.index, name="index"),
    path('login/', views.index, name='login'),
    path('register/', views.register, name="register"),
    path('logout/', views.logout, name="logout")
]

接着,在项目目录下面的urls.py文件中配置应用的urls.py

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


urlpatterns = [
    path('admin/', admin.site.urls),
    path('lib/', include('library.urls'))
]

3.5. 视图设计

首先,在liarary/views.py下面

from django.shortcuts import render, redirect

# Create your views here.

def index(request):
    pass
    return render(request, 'login/index.html')


def login(request):
    pass
    return render(request, 'login/login.html')


def register(request):
    pass
    return render(request, 'login/register.html')


def logout(request):
    pass
    return redirect('/login/')

接着,在library应用下面创建templates文件夹,接着在templates下面创建login文件夹,接着在login下面创建上面提及的3个html文件。

截止到这里,一个整体的框架就基本完成了,接着就是实现登录、注册、登出的逻辑。

3.6. 开始登录的页面设计

login.html界面,这里没有使用使用原生态的HTML页面,因为所有前端的验证和安全机制都是不可信的,因此采用Django的Form构建表单。

3.6.1. form表单

首先,在library下面创建一个forms.py文件,然后在里面使用Python代码创建需要填写的元素:

from django import forms

class UserForm(forms.Form):
    username = forms.CharField(label='用户名', max_length=128)
    password = forms.CharField(label='密码', max_length=256, widget=forms.PasswordInput)

然后,在templates文件目录下面的login.html界面中写上如下代码

<form action="{%  url 'library:login' %}" method="post">
    {% csrf_token %}
    <h3>欢迎登录</h3>
    <div>
        {{ login_form.username.label_tag }}
        {{ login_form.username }}
    </div>
    <div>
        {{ login_form.password.label_tag }}
        {{ login_form.password }}
    </div>

    <div>
        <a href="{% url 'library:register' %}"><ins>新用户注册</ins></a>
        <button type="submit">登录</button>
    </div>
</form>

3.6.2. 增加静态文件

首先,在应用library下面创建一个与templates同级的static目录,在这个目录下面创建login目录。接着在login目录下面创建一个css和images目录。创建完成之后目录结构为

-library
    -static
        -login
            -css
            -images
    -templates
        -login
            -index.html
            -login.html
            ......

在login下面的css目录下创建login.css, 并写入下面的代码:

body {
  height: 100%;
  background: aliceblue;
}

结合,需要在login.html中引入这个css文件,文件代码如下:

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="{% static 'login/css/login.css' %}">
    <title>登录</title>
</head>
<body>

3.6.3. 登录路由实现

这里使用url解析的方式来赋值需要跳转的地址。在library下面的urls.py中的内容是

app_name = 'library'
urlpatterns = [
    path('', views.index, name="index"),
    path('index/', views.index, name="index"),
    path('login/', views.login, name='login'),
    path('register/', views.register, name="register"),
    path('logout/', views.logout, name="logout")
]

3.6.4. 登录界面逻辑实现

在下面的逻辑中增加了提示信息,这也是对应的需要在前端界面中增加的内容。

def login(request):
    if request.method == 'POST':
        login_form = UserForm(request.POST)
        if login_form.is_valid():
            username = login_form.cleaned_data.get('username')
            password = login_form.cleaned_data.get('password')
            try:
                user = User.objects.get(name=username)
            except:
                message = "用户不存在"
                return render(request, 'login/login.html', locals())
            if user.password == password:
                return redirect(reverse('library:index'))
            else:
                message = "用户密码错误"
                return render(request, 'login/login.html', locals())

    login_form = UserForm()
    return render(request, 'login/login.html', locals())

在前端显示中增加提示信息

<form action="{%  url 'library:login' %}" method="post">
    {% if message %}
    <div>{{ message }}</div>
    {% endif %}
    {% csrf_token %}
    <h3>欢迎登录</h3>
    ...

</form>

至此,整个登录界面已经完成了。

3.6.5. 前端增加类名等信息

为了能够给指定的内容进行渲染,需要使用类名信息,可以在表单中设置widget属性设置类名。还可以设置一些聚焦等属性。

from django import forms

class UserForm(forms.Form):
    username = forms.CharField(label='用户名', max_length=128, widget=forms.TextInput(attrs={'class':'form-control','placeholder':"Username", 'autofocus':''}))
    password = forms.CharField(label='密码', max_length=256, widget=forms.PasswordInput(attrs={'class':'form-control', 'placeholder':'Password'}))

然后在静态文件中就可以设置form-control类的样式。

4. session回话

参考here

4.1. 实现不允许重复登录

def login(request):
    if request.session.get('is_login', None):    # 这里
        print("已经登录...")
        return redirect(reverse('library:index'))
    if request.method == 'POST':
        login_form = UserForm(request.POST)
        if login_form.is_valid():
            username = login_form.cleaned_data.get('username')
            password = login_form.cleaned_data.get('password')
            try:
                user = User.objects.get(name=username)
            except:
                message = "用户不存在"
                return render(request, 'login/login.html', locals())
            if user.password == password:
                request.session['is_login'] = True  # 这里
                request.session['user_id'] = user.id # 这里
                request.session['user_name'] = user.name # 这里
                return redirect(reverse('library:index'))
            else:
                message = "用户密码错误"
                return render(request, 'login/login.html', locals())

    login_form = UserForm()

    return render(request, 'login/login.html', locals())

4.2. 退出系统

request.session.flush() # 删除当前的会话数据和会话cookie。经常用在用户退出后,删除会话。

def logout(request):
    if not request.session.get('is_login', None):
        # 如果没有登录的话
        return redirect(reverse('library:login'))
    request.session.flush()
    return redirect(reverse('library:login'))

index.html中增加入口.
注意标签中使用url的这种写法。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    {% if request.session.user_name %}
        <h1>welcome, {{ request.session.user_name }}</h1>
    {% endif %}


<p>
    <a href="{% url 'library:logout' %}">登出</a>
</p>
</body>
</html>

4.3. 添加首页内容

首页进行判断,如果没有登录的时候,就跳转到登录页面。

def index(request):
    if not request.session.get('is_login', None):
        return redirect('library:login')
    return render(request, 'login/index.html')

5. 注册功能

5.1. 注册表单

使用Django的Form创建表单。

class RegisterForm(forms.Form):
    gender = (
        ('male','男'),
        ('female', '女')
    )

    username = forms.CharField(label="用户名", max_length=128, widget=forms.TextInput(attrs={'class': 'form-control'}))
    password1 = forms.CharField(label="密码", max_length=256, widget=forms.PasswordInput(attrs={'class': 'form-control'}))
    password2 = forms.CharField(label="确认密码", max_length=256,
                                widget=forms.PasswordInput(attrs={'class': 'form-control'}))
    email = forms.EmailField(label="邮箱地址", widget=forms.EmailInput(attrs={'class': 'form-control'}))
    sex = forms.ChoiceField(label='性别', choices=gender)   # 下拉框。

5.2. HTML中嵌入注册表单

register.html页面内容如下

{% load static %}
</html>
<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <title>注册</title>
  </head>
  <body>

    <form action="{% url 'library:register'  %}" method="post">
        {% if message %}
             <div>{{ message }}</div>
        {% endif %}

      {% csrf_token %}
      <h3 >欢迎注册</h3>

      <div>
          {{ register_form.username.label_tag }}
          {{ register_form.username}}
      </div>
      <div>
          {{ register_form.password1.label_tag }}
          {{ register_form.password1 }}
      </div>
      <div>
          {{ register_form.password2.label_tag }}
          {{ register_form.password2 }}
      </div>
      <div>
          {{ register_form.email.label_tag }}
          {{ register_form.email }}
      </div>
      <div>
          {{ register_form.sex.label_tag }}
          {{ register_form.sex }}
      </div>
      <div>
          {{ register_form.captcha.label_tag }}
          {{ register_form.captcha }}
      </div>

      <div>
          <a href="{% url 'library:login' %}"><ins>直接登录</ins></a>
          <button type="submit">注册</button>
      </div>
    </form>
  </body>
</html>

5.3. 视图中增加注册的逻辑

需要注意,首先验证两次输入的密码的一致性;接着判断用户名邮箱是否重复。需要注意,is_valid()方法的验证,虽然前端的输入框能够一定程度上验证准确率,但是在后端还要使用is_valid验证数据输入是否符合。

def register(request):
    if request.session.get('is_login', None):
        return redirect(reverse('library:index'))

    if request.method == 'POST':
        register_form = RegisterForm(request.POST)
        if register_form.is_valid():   # 当输入的数据格式都合法的时候
            username = register_form.cleaned_data.get('username')
            password1 = register_form.cleaned_data.get('password1')
            password2 = register_form.cleaned_data.get('password2')
            email = register_form.cleaned_data.get('email')
            sex = register_form.cleaned_data.get('sex')
            # print(username, password1, password2, email, sex)

            if password1 != password2:
                message = "两次输入的密码不一致"
                return render(request, 'login/register.html', locals())

            same_name_user = User.objects.filter(name=username)
            if same_name_user:
                message = "用户名已存在"
                return render(request, 'login/register.html', locals())

            same_email_user = User.objects.filter(email=email)
            if same_email_user:
                message = "邮箱已经注册"
                return render(request, 'login/register.html', locals())

            new_user = User()
            new_user.name = username
            new_user.password = password1
            new_user.email = email
            new_user.sex = sex
            new_user.save()

            return redirect(reverse('library:login'))
        else:  # 当输入的数据存在不合法的时候。
            # 比如输入ddd@ddd这个作为邮箱,此时前端的控件不会报错,
            # 后台在验证的时候会发现这个数据不符合,因此会运行这里的代码
            return render(request, 'login/register.html', locals())

    register_form = RegisterForm()
    return render(request, 'login/register.html', locals())

6. 密码加密

使用python的hashlib模块,在views.py文件中增加下列代码.

def hash_code(s, salt='library'):
    h = hashlib.sha256()
    s += salt
    h.update(s.encode())
    return h.hexdigest()

# 在注册方面里面,需要保存的是hash之后的密码
new_user = User()
new_user.name = username
new_user.password = hash_code(password1)

# 在登录的时候,需要将密码hash之后和数据库对比
if user.password == hash_code(password): # 注意这里
    request.session['is_login'] = True
    request.session['user_id'] = user.id
    request.session['user_name'] = user.name
    return redirect(reverse('library:index'))

登录、注册功能到这里暂时告一段落

7. 该功能所有代码

7.1. views.py

from django.shortcuts import render, redirect

# Create your views here.
from django.urls import reverse
from .models import User, Book
from .forms import UserForm, RegisterForm
import hashlib


def hash_code(s, salt='library'):
    h = hashlib.sha256()
    s += salt
    h.update(s.encode())
    return h.hexdigest()


def index(request):
    if not request.session.get('is_login', None):
        return redirect('library:login')
    return render(request, 'login/index.html')


def login(request):
    if request.session.get('is_login', None):
        return redirect(reverse('library:index'))
    if request.method == 'POST':
        login_form = UserForm(request.POST)
        if login_form.is_valid():
            username = login_form.cleaned_data.get('username')
            password = login_form.cleaned_data.get('password')
            try:
                user = User.objects.get(name=username)
            except:
                message = "用户不存在"
                return render(request, 'login/login.html', locals())
            if user.password == hash_code(password):
                request.session['is_login'] = True
                request.session['user_id'] = user.id
                request.session['user_name'] = user.name
                return redirect(reverse('library:index'))
            else:
                message = "用户密码错误"
                return render(request, 'login/login.html', locals())

    login_form = UserForm()
    return render(request, 'login/login.html', locals())


def register(request):
    if request.session.get('is_login', None):
        return redirect(reverse('library:index'))

    if request.method == 'POST':
        register_form = RegisterForm(request.POST)
        if register_form.is_valid():   # 当输入的数据格式都合法的时候
            username = register_form.cleaned_data.get('username')
            password1 = register_form.cleaned_data.get('password1')
            password2 = register_form.cleaned_data.get('password2')
            email = register_form.cleaned_data.get('email')
            sex = register_form.cleaned_data.get('sex')
            # print(username, password1, password2, email, sex)

            if password1 != password2:
                message = "两次输入的密码不一致"
                return render(request, 'login/register.html', locals())

            same_name_user = User.objects.filter(name=username)
            if same_name_user:
                message = "用户名已存在"
                return render(request, 'login/register.html', locals())

            same_email_user = User.objects.filter(email=email)
            if same_email_user:
                message = "邮箱已经注册"
                return render(request, 'login/register.html', locals())

            new_user = User()
            new_user.name = username
            new_user.password = hash_code(password1)
            new_user.email = email
            new_user.sex = sex
            new_user.save()

            return redirect(reverse('library:login'))
        else:  # 当输入的数据存在不合法的时候。
            # 比如输入ddd@ddd这个作为邮箱,此时前端的控件不会报错,
            # 后台在验证的时候会发现这个数据不符合,因此会运行这里的代码
            return render(request, 'login/register.html', locals())

    register_form = RegisterForm()
    return render(request, 'login/register.html', locals())


def logout(request):
    if not request.session.get('is_login', None):
        # 如果没有登录的话
        return redirect(reverse('library:login'))
    request.session.flush()
    return redirect(reverse('library:login'))

7.2. urls.py

from django.urls import path
from . import views


app_name = 'library'
urlpatterns = [
    path('', views.index, name="index"),
    path('index/', views.index, name="index"),
    path('login/', views.login, name='login'),
    path('register/', views.register, name="register"),
    path('logout/', views.logout, name="logout")
]

7.3. models.py

from django.db import models

# Create your models here.
class User(models.Model):
    gender = (
        ('male', '男'),
        ('female', '女')
    )

    name = models.CharField(max_length=128, unique=True)
    password = models.CharField(max_length=256)
    email = models.EmailField(unique=True)
    sex = models.CharField(max_length=32, choices=gender, default=1)
    c_time = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.name

    class Meta:
        ordering = ['-c_time']   # 创建时间的逆序,也就是创建的越晚,排在上面
        verbose_name = verbose_name_plural = '用户'
全部评论

相关推荐

点赞 收藏 评论
分享
牛客网
牛客企业服务