Django实现迷你淘宝(三) --- 基于django的用户验证系统实现

欢迎查看本系列的其他文章:

  1. postgres安装与入门
  2. django安装与入门
  3. 基于django的用户验证系统实现
  4. 基于Bootstrap的商品页面设计与美化
  5. 基于jQuery和AJax的购物车设计与实现

Django实现迷你淘宝(三) — 基于django的用户验证系统实现

用户验证基本是一个网站的必备功能,至少包含几个功能:

  • 用户注册与登录
  • 用户验证(有些网页只有登录用户才能访问)
  • 用户基本信息修改

Django本身就自带了用户验证系统,但针对不同类型的网站我们可能需要不同的信息,如迷你淘宝,需要知道该用户是否为卖家等,所以自带的验证系统无法完全满足我们的需求,但是我们可以以此为基础,进行开发,更安全也更方便。

本文代码

1. 数据对象

因为django内置了一个ORM层,我们不需要直接设计数据库的table,我们只需要创建一个用户对象进行操作即可。在第二篇文章里面,我们已经创建了一个Profile类,具体代码如下:

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

# Create your models here.
class Profile(models.Model):
    user = models.OneToOneField(User, primary_key=True, on_delete=models.CASCADE)
    is_seller = models.BooleanField(default=False)

    def __str__(self):
        return f'{self.user.username} Profile'

这里我们通过from django.contrib.auth.models import User导入了django自带的User对象,该对象已经包含了用户名,密码,邮箱等常用字段,并且包含了密码相关的所有内容(如,加密储存,安全性验证等),在此基础上,针对我们自己的应用增加了一个is_seller字段。

这就是我们需要的所有信息了,下面我们来看看如何把这个类和前端页面结合起来。

2. 用户注册

2.1 django表单

django对于每一种类型的变量(Boolean,Integer等等)都内置了有一个对应的html样式,想要使用内置的样式,就需要创建一个表单继承django的内置表单。在users文件夹内创建一个forms.py文件,写入如下内容:

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


class UserRegisterForm(UserCreationForm):
    email = forms.EmailField()

    class Meta:
        model = User
        fields = ['username', 'email', 'password1', 'password2']

这里我们创建了一个UserRegisterForm继承自django自带的UserCreationFormclass Meta里面,model用于指定该表单对应的“模型”是什么(每个表单都应该有一个对应的“模型”),fields用于指定我们需要该模型内的哪些字段。

2.2 视图函数

users/views.py里面增加注册函数

def register(request):
    if request.method == 'POST':
        form = UserRegisterForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect(reverse("home"))
    else:
        form = UserRegisterForm()
    return render(request, 'users/register.html', {'form': form})

这里首先判断request是GET还是POST,如果是GET我们就创建一个空白的表单传给html模板,假如是POST我们就从request中读取表单信息,并调用内置的is_valid()函数对表单数据进行验证,验证成功的话就保存数据。由于我们在UserRegisterForm里面声明了该表单关联的对象是User,所以form.save()会创建并保存一个User对象。注册成功之后,我们将用户重定向回主页面。

同时别忘了去users/url.py里面注册该视图函数:

from django.urls import path

from . import views

urlpatterns = [
    path('', views.home, name="home"),
    path('register/', views.register, name='register'),
]

2.3 HTML模板文件

2.3.1 基础模板文件

首先在users文件夹下面创建一个templates文件夹并在里面创建users文件夹,这个文件夹将会用来存放我们所有的HTML模板文件。这里为了以后能更方便的在每个页面使用Bootstrap(下一篇文章会详细讲解),我们首先新建一个base.html,写入如下内容:

<!DOCTYPE html>
<html lang="en">
<head>
    {% load static %}
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <title>迷你淘宝</title>
</head>
<body>

<main role="main">
    {% block content %}
    {% endblock %}
</main>

{% block script %}
{% endblock %}
</body>
</html>

这个文件会作为其他所有html文件的“容器“,后期我们可以通过这个“容器”,快速将Bootstrap应用到所有界面。

2.3.2 注册页面模板

创建一个register.html用于用户注册

{% extends "users/base.html" %}
{% block content %}
    <form method="POST">
        {% csrf_token %}
        <fieldset>
            <legend>Join Today</legend>
            {{ form.as_p }}
        </fieldset>
        <button class="btn btn-outline-info" type="submit">Sign Up</button>
    </form>
{% endblock content %}

模板文件里面通过{{ form }}即可取得传入的form对象,想要将其渲染出来,只需要用{{ form.as_p }}即可完成(还有{{ form.as_table }}等其他样式,详见这里),十分的方便。

此时文件目录如下

.
├── __init__.py
├── admin.py
├── apps.py
├── forms.py
├── migrations
│   ├── 0001_initial.py
│   ├── __init__.py
├── models.py
├── templates
│   └── users
│       ├── base.html
│       └── register.html
├── tests.py
├── urls.py
└── views.py

运行程序,输入网址http://127.0.0.1:8000/register/,你应该能看到如下页面
在这里插入图片描述
我们不需要写一行具体的代码,就可以生成整个表单。不过现在这个表单还很丑,别急,下一篇文章会教你如何使用Bootstrap美化表单。

2.4 利用signals创建Profile

细心的读者可能已经发现了一点问题,就是我们通过表单创建的是一个User对象,但是我们实际需要的是一个Profile对象(包含了我们自己的field),为了解决这个问题,我们又引入一个新的概念,信号(signals),django提供各种各样的信号,你可以通过捕捉这些信号,干一些有趣的事情。
在users文件夹下面新建signals.py,并添加如下代码:

from django.db.models.signals import post_save
from django.contrib.auth.models import User
from django.dispatch import receiver
from .models import Profile


@receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
    if created:
        Profile.objects.create(user=instance)


@receiver(post_save, sender=User)
def save_profile(sender, instance, **kwargs):
    instance.profile.save()

这里我们捕获了post_save信号(使用annotation),这个信号会在save()的最后被调用,所以每一次创建一个新的User对象,就会调用这个函数并创建一个对应的Profile对象。
为了方便管理所有信号,我们单独新建了一个文件,所以我们需要去“注册”一下这些信号,修改users/apps.py文件,增加一个ready函数(会在该app运行起来后被调用),并在函数内导入signals文件:

class UsersConfig(AppConfig):
    name = 'users'

    def ready(self):
        # import all signals
        import users.signals

常见问题:

  1. 如果现在还是无法捕获信号,尝试在users/__init__.py里面添加一行default_app_config = 'users.apps.UsersConfig'

2.5 结果验证

为了验证我们是否注册成功,我们在主页面加一些内容,把所有已注册用户的名字显示出来。

2.5.1 修改views.pyhome函数
def home(request):
    profiles = Profile.objects.all()
    context = {"profiles": profiles}
    return render(request, 'users/home.html', context)

这里我们首先通过Profile.objects.all()获得所有的已注册用户信息,再将其传入home.html

2.5.2 新建home.html函数

users/templates/users目录下新建home.html文件,并写入如下内容

{% extends "users/base.html" %}
{% block content %}
    <h1>Hello world</h1>
    {% if profiles %}
        <ul>
            <h2>User list</h2>
            {% for p in profiles %}
                <li>{{ p.user.username }}</li>
            {% endfor %}
        </ul>
    {% else %}
        <h2>No user yet.</h2>
    {% endif %}
    <a href="{% url "register" %}">Register</a>
{% endblock %}

这里用{% for p in profiles %}循环取出所有的已注册用户

成功运行之后,大家访问127.0.0.1就可以看到类似的界面了(我这里已经有两个用户)
在这里插入图片描述

2.5.3 其他验证方法

有的小伙伴可能会说,我只是想验证一下数据是否成功插入,你让我写一个页面,太麻烦了吧。不要急,也有更简单的方法:

  • 首先python manage.py shell进入django的shell
  • 然后from users.models import Profile导入我们的类
  • 最后Profile.objects.all()

结果如下
在这里插入图片描述

3. 用户登录

用户注册后面紧接着肯定是用户登录功能了

3.1 登录页面模板

新建login.html,并写入

{% extends "users/base.html" %}
{% block content %}
    <div class="content-section">
        <form method="POST">
            {% csrf_token %}
            <fieldset class="form-group">
                <legend class="border-bottom mb-4">Log In</legend>
                {{ form.as_p }}
            </fieldset>

            <div class="form-group">
                <button class="btn btn-outline-info" type="submit">Login</button>
            </div>
        </form>
        <div class="border-top pt-3">
            <small class="text-muted">
                Need An Account? <a class="ml-2" href="{% url 'register' %}">Sign Up Now</a>
            </small>
        </div>
    </div>
{% endblock content %}

同样的,这里还是用到django的表单来简化代码,核心代码就是{{ form.as_p }}
这个页面没有太多内容,就是一个表单,用于给用户输入登录信息,然后登录按钮。

3.2 登录页面视图函数

由于登录页面的处理逻辑十分简单,而且也相对固定(基本逻辑就是获得用户数据,验证,返回结果),所以django连这个都帮我们写好了,在django.contrib.auth包下面,我们打开urls.py添加一行

path('login/', auth_views.LoginView.as_view(template_name='users/login.html'), name='login'),

同时别忘了导入包

from django.contrib.auth import views as auth_views

这时候的urls.py应该长这样

from django.urls import path
from django.contrib.auth import views as auth_views

from . import views

urlpatterns = [
    path('', views.home, name="home"),
    path('register/', views.register, name='register'),
    path('login/', auth_views.LoginView.as_view(template_name='users/login.html'), name='login'),
    path('logout/', auth_views.LogoutView.as_view(template_name='users/logout.html'), name='logout'),
]

细心的小伙伴可能发现了,我们没有指定登陆成功后跳转到哪里,假如你试一下你会发现,django默认会跳转到127.0.0.1;8000/account/profile页面,但是我们可能想让他跳转到我们的首页,此时你需要修改一下setting.py文件,在文件最后面增加一行

LOGIN_REDIRECT_URL = 'home'

3.3 登出功能

登出和登录十分类似,django也给我们内置好了,直接写一个html并调用内置函数即可,这里就不详细讲解了。

4. 用户验证

前面说了这么多,核心的目的还是为了区分“合法用户”和“非法用户”,因为有些页面只有已登录的用户才能访问(如:个人信息,购物车,历史订单等)。用户验证主要有两方面的需求

  1. 某个页面只有已登录用户才能访问,未登录的无法访问
  2. 某个页面大家都可以访问,但是已登录用户和未登录用户的界面不一样

4.1 拒绝未登录用户

这里我们增加一个profile页面,模仿任意只能被已登录用户访问的页面,细节就不再赘述,大致过程:新建一个html —> 新建一个视图函数 —> 注册视图函数 —> 在主页面增加一个链接,跳转到新页面。
此时首页应该长这样:
在这里插入图片描述
profile页面的html如下:

{% extends "users/base.html" %}
{% block content %}
    <h2>{{ user.username }}</h2>
{% endblock content %}

这个页面,我们只是简单的显示一下用户的用户名。
注意,这里我们不需要传入user对象也可以访问,因为django默认的在每个request里面都会有一个user对象,这也是使用django自带验证系统的一大好处。

为了拒绝未登录用户,我们只需要在profile的视图函数里面加一个annotation就好

from django.contrib.auth.decorators import login_required

@login_required
def profile(request):
    return render(request, 'users/profile.html')

未登录用户访问这个页面的“正确”操作应该是把他导向登录页面,所以我们在setting.py文件里面增加一行,告诉django我们的登录页面URL

LOGIN_URL = 'login'

此时,运行程序,直接访问该页面会被重定向到登录界面,登录后再次访问,就会看到你的用户名。

4.2 根据用户身份动态加载内容

有时候我们会需要在html里面判断当前用户是否已经登录,并根据结果展示不同内容。这里以首页为例,未登录用户只展示“登录”和“注册”,已登录用户展示“登出”和“Profile”。
修改home.html

{% if user.is_authenticated %}
    <a href="{% url "logout" %}">Logout</a>
    <br>
    <a href="{% url "profile" %}">Profile</a>
{% else %}
    <a href="{% url "register" %}">Register</a>
    <br>
    <a href="{% url "login" %}">Login</a>
{% endif %}

我们可以直接通过调用user.is_authenticated来判断当前用户是否为已登录(已验证)。

本文代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值