UTOOLS1588140215246.png

实现功能:

  • 用户登录和注册界面
  • Form表单使用

在上一节中,登录的表单,提交评论的表单都是我们自己做的

在Django中有Form表单库,可以帮我们快速实现表单提交

官方Form文章

https://docs.djangoproject.com/en/3.0/ref/forms/api/#django.forms

登录页面

在项目创建时,自带的app中,新建文件forms.py

# 导包
from django import forms

登录表单类

forms.py

class LoginForm(forms.Form):
    """登录表单"""
    username = forms.CharField()
    password = forms.CharField()

这个使用方法和models创建模型类似的

这里就有两个字段username, password

视图函数views.py

# 导入表单类
from .forms import LoginForm
def login(request):
    pass

路由设置urls.py

# ...
from .views import login
path('/login', login, name='login')
# ...

表单和模型的大致步骤是一致的

创建表单类 -> 在视图函数中导入表单类并编写视图函数 -> 设置路由规则

创建模型类 -> 在视图函数中导入模型类并编写视图函数 -> 设置路由规则

回过头来,再继续写视图函数

def login(request):
    # new一个表单示例
    login_form = LoginForm()
    context = {}
    context[login_form] = login_form
    return render(request, 'login.html', context)

新建模板文件login.html

{% extends 'base.html' %}
{% block title %}登录页{% endblock %}
{% block content %}
<div class="container">
    <div class="row">
        <div class="col-xs-4 col-xs-offset-4">
            <div class="panel panel-default">
                <div class="panel-heading">登录</div>
                <div class="panel-body">
                    <form action="" method="POST">
                        {% csrf_token %}
                        {{ login_form }}
                        <input class="btn btn-primary pull-right" type="submit" value="登录">
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
{% endblock %}

在表单中,直接用{{ login_form }}就会有一个表单了,这是django的form帮我们设置好的

UTOOLS1588141837960.png

后面会美化界面

通过这里login_form实例我们就可以直接生成表单,而不需要自己编写了

用户输入了账号密码,点击登录需要请求一个链接,难道因为这个连接,我们还需要再写视图函数,路由规则?

登录地址/login 这个也可以作为参数请求的url

<form action="" method="POST"> 表单这里action留空,点击登录就会POST方式 请求当前页

回到视图函数

首先就是判断,过来的请求方式

def login(request):
    if request.method == "POST": # 注意这个POST必须大写
        # POST请求:请求登录
        pass
    else:
        # 其他请求方式:请求登录界面
        pass

因为需要对用户提交的账号密码进行校验,所以可以在表单类中,进行数据的校验

视图函数就拿来处理逻辑就行了,不然会有点混乱

forms.py

class LoginForm(forms.Form):
    """登录表单"""
    username = forms.CharField()
    password = forms.CharField()
    
    # 校验函数
    def clean(self):
        # 获取用户传过来的账号密码
        username = self.cleaned_data['username']
        password = self.cleaned_data['password']
        
        # 验证
        user = auth.authenticate(username=username, password=password)
        if not user:
            # 验证失败,抛出异常
            raise forms.ValidationsError('用户名或密码错误')
           else:
            self.cleaned_data['user'] = user
        return self.cleaned_data

在每个字段上都调用Field.clean()之后,可以进行任何额外的表单范围内的清理工作

这个clean方法是覆写父类的方法

回到视图函数继续完善代码

def login(request):
    if request.method == "POST": # 注意这个POST必须大写
        # POST请求:请求登录
        # 实例化登录表单类
        login_form = LoginForm(data=request.POST)
        # 验证该表单示例是否有效
        if login_form.is_valid():
            # 验证成功之后,为用户设置sessionid
            user = login_form.cleaned_data['user']
            auth.login(request, user)
            return redirect(reverse('home'))
    else:
        # 其他请求方式:请求登录界面
        login_form = LoginForm()
    context = {}
    context['login_form'] = login_form
    return render(request, 'login.html', context)

看完上方代码,是不是感觉之前写的数据校验方法没有被用上啊

login_form.is_valid()这个方法在其中使用了其他方法,我们覆写的clean方法就在这些方法中

UTOOLS1588144856949.gif

至此登录页面的功能就完成了

细节优化

关于网页的渲染还可以这样写

<form action="" method="POST">
    {% csrf_token %}
    {% for field in login_form %}
    <label for="{{ field.id_for_label }}">{{ field.label }}</label>
    {{ field }}
    <p class="text-danger">{{ field.errors.as_text }}</p>
    {% endfor %}
    <span class="pull-left text-danger">{{ login_form.non_field_errors.0 }}</span>
    <input class="btn btn-primary pull-right" type="submit" value="登录">
</form>

循环遍历login_form

登录表单的两个字段还可以这样写

username = forms.CharField(label="用户名", widget=forms.TextInput(attrs={'class': 'form-control','placeholder': '请输入用户名'}))

password = forms.CharField(label="密码", widget=forms.PasswordInput(attrs={'class': 'form-control','placeholder': '请输入密码'}))

通过widget类可以定制化表单

The widget argument lets you specify a Widget class to use when rendering this Field.

widget参数允许您指定在呈现该字段时使用的widget类。

https://docs.djangoproject.com/en/3.0/ref/forms/widgets/

注册界面

  1. 创建表单类
  2. 创建视图函数
  3. 定制路由规则
  4. 修改模板界面

UTOOLS1588147160850.png

注册表单类

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

class RegForm(forms.Form):
    username = forms.CharField(min_length=6, max_length=20, widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': '用户名'}))
    email = forms.EmailField(widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': '邮箱'}))
    password = forms.CharField(min_length=6, max_length=16, widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': '输入密码'}))
    password_again = forms.CharField(min_length=6, max_length=16, 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.ValidtionError("邮箱已被注册")
        return email
    
    def clean_password(self):
        # 校验两次密码是否一致
        password = self.cleaned_data['password']
        password_again = self.cleaned_data['password_again']
        if password != password_again:
            raise ValidtionError("两次密码不一致")
        return password

视图函数

# ...
# 导入表单类
from .models import RegForm
# ...
def register(request):
    if request.method == "POST":
        # 实例化注册表单
        reg_form = RegForm(request.POST)
        username = reg_form.cleaned_data['username']
        email = reg_form.cleaned_data['email']
        password = reg_form.cleaned_data['password']
        # 通过django的User模块,可以快速创建一个用户
        User.objects.create_user(username=username, password=password, email=email)
        # 创建成功后,可以让他返回首页
        # 后面可以再做个人中心,创建完成返回个人中心
        return redirect(reverse('home'))
       else:
        reg_form = RegFrom()
    context = {}
    context['reg_form'] = reg_form
    return render(request, 'register.html', context)

关于创建用户还可以这样写

# 实例化user对象后,创建一个用户
# user = User()
# user.username = username
# user.email = email
# user.set_password(password)
# user.save()

定义路由规则

# ...
path('/reg', register, name='register')
# ...

编写模板

{% extends 'base.html' %}
{% block title %}注册{% endblock %}
{% block content %}
<div class="container">
    <div class="row">
        <div class="col-xs-4 col-xs-offset-4">
            <div class="panel panel-default">
                <div class="panel-heading">注册</div>
                <div class="panel-body">
                    <form action="" method="POST">
                        {% csrf_token %}
                        {% for field in reg_form %}
                        <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                        {{ field }}
                        <p class="text-danger">{{ field.errors.as_text }}</p>
                        {% endfor %}
                        <span class="pull-left text-danger">{{  reg_form.non_field_errors.0 }}</span>
                        <input class="btn btn-primary pull-right" type="submit" value="注册">
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
{% endblock %}
Last modification:May 1st, 2020 at 03:46 pm