Django 小实例S1 简易学生选课管理系统 —— 五、实现注册功能

作者自我介绍:b站小UP主时常直播编程+红警三python1对1辅导老师

python Django实现的一个简易的教务选课系统。
介绍与演示的视频版本已发到我的b站: https://www.bilibili.com/video/BV1er4y1w7ty
项目已上传到我的github: https://github.com/BigShuang/SimpleStudentCourseManagementSystem

S1总目录:

〇、初步介绍与演示
一、项目流程梳理与数据库设计
二、新建项目(project)、设置、运行
三、创建用户模型(model)
四、实现登录页面
五、实现注册功能

======================= 大爽歌作,made by big shuang =======================

五、实现注册功能

本文涉及到的新的额外知识点:Class-based views
没有这部分基础的读者,建议一边阅读本文一边查阅相关知识
这里推荐我的专栏:Django自学笔记 相关章节内容

1 添加注册页面模板(template)

templates/user下新建register.html如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>
        Register
    </title>
</head>
<body>
    <div class="register-container">
        <div class="register-title">注册</div>
        <form method="post" class="form">
            {% csrf_token %}
            {{form.as_p}}
            <p><input type="submit" value="注册" class="submit-button"/></p>
        </form>
    </div>
</body>

2 添加对应的视图(view)方法

这里先思考,要完成注册功能,视图方法应该实现怎样的功能。

注册一个新的学生账号,就是在student数据库表中添加一个新的记录。

对应到Django项目,则是通过新建一个学生模型(model)实例。 (教师同理)

Django为 model 类实现了一些功能强大的视图类,使你能够快速完成一个为model进行增删查改等等操作的视图类,同时使用视图类的特定方法生成视图。

这样的视图类一般称为CBV(Class-based views)

在这里我们直接使用为model类进行新增实例的CreateView

方便我们直接根据指定的字段生成前端表单,该生成的表单自带检查字段格式的功能,同时也方便我们在后端接受表单请求后按照表单数据生成对应的实例。

如果不使用CBV,上面介绍的繁琐的过程都需要我们手动一步一步实现,这是很痛苦麻烦低效的。

cbv本身只能够指定根据哪些字段生成对应的前端表单。

但我们这里需要实现一个略微复杂一点的功能: 注册页面除了需要填写密码还需要确认密码(即再填写一遍密码),同时提交时,需要先检查这两个是否一致。

要完成这个功能,我们需要实现一个定制化的表单

实现定制化的表单

user/forms.py文件中,添加代码

class StuRegisterForm(forms.ModelForm):
    confirm_password = forms.CharField(widget=forms.PasswordInput(), label="确认密码")

    class Meta:
        model = Student
        fields = ('grade',
                  'name',
                  'password',
                  'confirm_password',
                  'gender',
                  'birthday',
                  'email',
                  'info')

    def clean(self):
        cleaned_data = super(StuRegisterForm, self).clean()
        password = cleaned_data.get('password')
        confirm_password = cleaned_data.get('confirm_password')
        if confirm_password != password:
            self.add_error('confirm_password', 'Password does not match.')


class TeaRegisterForm(forms.ModelForm):
    confirm_password = forms.CharField(widget=forms.PasswordInput(), label="确认密码")

    class Meta:
        model = Teacher
        fields = ('name',
                  'password',
                  'confirm_password',
                  'gender',
                  'birthday',
                  'email',
                  'info')

    def clean(self):
        cleaned_data = super(TeaRegisterForm, self).clean()
        password = cleaned_data.get('password')
        confirm_password = cleaned_data.get('confirm_password')
        if confirm_password != password:
            self.add_error('confirm_password', 'Password does not match.')

然后为学生和老师这两种model都添加下对应的视图类

更新import导入部分

user/views.py中,需要导入新的需要用到的库
更新后的导入库部分代码为:

from django.shortcuts import render, reverse, redirect
from django.http import HttpResponse
from django.views.generic import CreateView

from constants import INVALID_KIND
from user.forms import StuLoginForm, TeaLoginForm, StuRegisterForm, TeaRegisterForm

from user.models import Student, Teacher
实现CBV

导入好需要的库后,在user/views.py中,添加如下代码

class CreateStudentView(CreateView):
    model = Student
    form_class = StuRegisterForm
    # fields = "__all__"
    template_name = "user/register.html"
    success_url = "login"

    def form_valid(self, form):
        # 学生注册时选定年级自动生成学号
        grade = form.cleaned_data["grade"]
        # order_by默认升序排列,number前的负号表示降序排列
        student_set = Student.objects.filter(grade=grade).order_by("-number")
        if student_set.count() > 0:
            last_student = student_set[0]
            new_number = str(int(last_student.number) + 1)
            for i in range(6 - len(new_number)):
                new_number = "0" + new_number
        else:
            new_number = "000001"

        # Create, but don't save the new student instance.
        new_student = form.save(commit=False)
        # Modify the student
        new_student.number = new_number
        # Save the new instance.
        new_student.save()
        # Now, save the many-to-many data for the form.
        form.save_m2m()

        self.object = new_student

        uid = grade + new_number
        from_url = "register"
        base_url = reverse(self.get_success_url(), kwargs={'kind': 'student'})
        return redirect(base_url + '?uid=%s&from_url=%s' % (uid, from_url))


class CreateTeacherView(CreateView):
    model = Teacher
    form_class = TeaRegisterForm
    template_name = "user/register.html"
    success_url = "login"

    def post(self, request, *args, **kwargs):
        form = self.get_form()

        if form.is_valid():
            return self.form_valid(form)
        else:
            return self.form_invalid(form)

    def form_valid(self, form):
        # 老师注册时随机生成院系号, 院系号范围为[0,300)
        department_no = random.randint(0, 300)
        # 把非三位数的院系号转换为以0填充的三位字符串,如1转换为'001'
        department_no = '{:0>3}'.format(department_no)
        teacher_set = Teacher.objects.filter(department_no=department_no).order_by("-number")
        if teacher_set.count() > 0:
            last_teacher = teacher_set[0]
            new_number = int(last_teacher.number) + 1
            new_number = '{:0>7}'.format(new_number)
        else:
            new_number = "0000001"

        # Create, but don't save the new teacher instance.
        new_teacher = form.save(commit=False)
        # Modify the teacher
        new_teacher.department_no = department_no
        new_teacher.number = new_number
        # Save the new instance.
        new_teacher.save()
        # Now, save the many-to-many data for the form.
        form.save_m2m()

        self.object = new_teacher

        uid = department_no + new_number
        from_url = "register"
        base_url = reverse(self.get_success_url(), kwargs={'kind': 'teacher'})
        return redirect(base_url + '?uid=%s&from_url=%s' % (uid, from_url))

这里介绍下上面的业务逻辑,在本项目S1的第三章第一节说过:

学生年级号为4位数字组成的字符串,年级下子学号为6位数字组成的字符串。
这两个连接起来组成学生的唯一学号,该学号也为其登录使用的账号。
比如学生李大爽,年级号为"2020",子学号为"000001",其学号为"2020000001"

其中年级号是学生注册时自己选择的,子学号是注册时按照其年级内注册的先后顺序生成的。
同一年级,第一个注册的是"000001",第二个是"000002",依次类推。

这部分功能是在上面的CreateStudentView中的form_valid方法中实现的,该方法会返回一个HttpResponseRedirect对象,对应的效果是学生注册成功后,会返回到该重定向页面所指向的网页,这里对应的是注册详情页。

一般来说转么做一个注册成功页面会好些,不过这是个小项目,这里我就懒得去专门再搞个新页面了。

由于注册后的账号是后台生成的,注册者并不知道,所以重定向后需要将账号展示给注册者看。

这里采用的技术是通过url来传参,传到注册详情页展示给注册者看。

而对于老师

说明:老师院系号为3位数字组成的字符串,院内编号为7位数字组成的字符串。
这两个连接起来组成老师的唯一教师号,该教师号也为其登录使用的账号。
比如老师牛有力,院系号为"266",院内编号为"0000001",其教师号为"2660000001"

其中院系号目前是随机生成的(最早是想做由院系模块,后来觉得工作量大就先放弃了,如果有人想做的话可以自行拓展)
院内编号是注册时按照其院内注册的先后顺序生成的。

同一院系,第一个注册的是"0000001",第二个是"0000002",依次类推。

这部分功能是在上面的CreateTeacherView中的form_valid方法中实现的,该方法会返回一个HttpResponseRedirect对象,对应的效果是老师注册成功后,会返回到该重定向页面所指向的网页,这里对应的是注册详情页。

更新登录详情页,展示注册成功后的账号

更新user/views.py中的login方法如下

def login(request, kind):
    if kind not in ["teacher", "student"]:
        return HttpResponse(INVALID_KIND)

    if request.method == 'POST':
        if kind == "teacher":
            form = TeaLoginForm(data=request.POST)
        else:
            form = StuLoginForm(data=request.POST)

        if form.is_valid():
            uid = form.cleaned_data["uid"]

            temp_res = "hello, %s" % uid
            return HttpResponse(temp_res)
    else:
        context = {'kind': kind}
        if request.GET.get('uid'):
            uid = request.GET.get('uid')
            context['uid'] = uid
            if kind == "teacher":
                form = TeaLoginForm({"uid": uid, 'password': '12345678'})
            else:
                form = StuLoginForm({"uid": uid, 'password': '12345678'})
        else:
            if kind == "teacher":
                form = TeaLoginForm()
            else:
                form = StuLoginForm()
        context['form'] = form
        if request.GET.get('from_url'):
            context['from_url'] = request.GET.get('from_url')

        return render(request, 'user/login_detail.html', context)

再更新templates/user/login_detail.html如下

{% extends "user/background.html" %}
{% block welcome_message %}
    {% if from_url == "register" %}
        <div class="welcome-message">注册成功,你的{% if kind == "student" %}学号{% else %}账号{% endif %}是 {{ uid }}</div>
    {% else %}
        <div class="welcome-message">欢迎</div>
    {% endif %}
{% endblock %}
{% block login_container %}
    {% if kind == "student" %}
        <div class="login-kind-title">我是学生</div>
    {% else %}
        <div class="login-kind-title">我是老师</div>
    {% endif %}
    <div class = "form">
        <form method="post">
            {% csrf_token %}
            {{form.as_p}}
            <div class="submit-button">
                <input type="submit" value="登录"/>
                <a href="{% url 'register' kind%}">注册</a>
            </div>
        </form>
        <div class="return-button"><a href="">返回上一页</a></div>
    </div>
{% endblock %}
实现view方法

一般来说,实现CBV后,使用CBV自带的as_view()就可以生成需要的view方法了。
但是我们这里有些不同,由于有老师和学生两种注册,我想要用同一个视图方法来处理这两种请求。
那么视图方法

  1. 需要接收个参数,该参数需要标明是老师注册与学生注册中的哪一种
  2. 内部用条件判断语句,针对不同的种类,返回不同的视图结果

逻辑理清,在user/views.py中,继续添加代码如下

def register(request, kind):
    func = None
    if kind == "student":
        func = CreateStudentView.as_view()
    elif kind == "teacher":
        func = CreateTeacherView.as_view()

    if func:
        return func(request)
    else:
        return HttpResponse(INVALID_KIND)

好了,到这里,注册部分的视图方法就算完成了

最后,我们更新下url部分就好

3 更新url

user/urls.py文件中,
给urlpatterns列表添加一行元素:

path('register/<slug:kind>', views.register, name="register")

再修改下templates/user/login_detail.html,为注册功能添加对应的进入链接

即修改第17行,修改前应该是

                <a href="">注册</a>

修改后为

                <a href="{% url 'register' kind%}">注册</a>

然后运行项目,浏览器打开http://127.0.0.1:8000/user/register/student,效果如图

在这里插入图片描述

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值