3.注册功能
Forms组件代码书写
""" 我们之前直接在views.py中书写forms组件代码 但是为了解耦合,应当将所有的forms组件代码单独写到一个地方 如果项目自始至终只用到一个forms组件,那么可以直接建一个py文件即可(bbs项目使用该方法,在应用下创建) myforms.py 如果你的项目需要用到多个forms组件,那么可以创建一个文件夹并在该文件夹内根据forms组件功能的不同创建不同的py文件 myforms文件夹 regform.py loginform.py userform.py """

# 书写针对用户表的forms组件代码 from django import forms class MyRegForm(forms.Form): username = forms.CharField(label='用户名', min_length=3, max_length=8, error_messages={ 'required': '用户名不能为空', 'min_length': '用户名最少3位', 'max_length': '用户名最多8位' }, # 还要让标签有bootstrap样式 widget=forms.widgets.TextInput(attrs={'class': 'form-control'}) ) password = forms.CharField(label='密码', min_length=3, max_length=8, error_messages={ 'required': '密码不能为空', 'min_length': '密码最少3位', 'max_length': '密码最多5位' }, # 还要让标签有bootstrap样式 widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'}) ) confirm_password = forms.CharField(label='确认密码', min_length=3, max_length=8, error_messages={ 'required': '密码不能为空', 'min_length': '密码最少3位', 'max_length': '密码最多8位' }, # 还要让标签有bootstrap样式 widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'}) ) email = forms.CharField(label='邮箱', error_messages={ 'required': '邮箱不能为空', 'invalid': '邮箱格式不正确' }, ) # 局部钩子 def clean_username(self): # 获取到用户名 username = self.cleaned_data.get('username') # 数据通过了form组件条件的数据都会存储在cleaned_data里,所以我们拿出这里面(过了第一道关卡)的数据,进行添加第二道关卡 if 'yuan' in username: # 提示前端展示错误信息 self.add_error('username', '用户名不能存在yuan') # 将钩子函数勾出来的数据再放回去 return username # 全局钩子 def clean(self): password = self.cleaned_data.get('password') confirm_password = self.cleaned_data.get('confirm_password') if not password == confirm_password: self.add_error('confirm_password', '两次密码不一致') return self.cleaned_data
注册页面简易搭建

<body> <div class="container-fluid"> <div class="row"> <div class="col-xs-8 col-xs-offset-2"> <form id="myform"> {% csrf_token %} <h1 class="text-center">注册</h1> {% for form in form_obj %} <div class="form-group"> <label for="">{{ form.label }}</label> {{ form }} <!--form_obj是forms组件渲染的html页面--> <span>{{ form.errors.0 }}</span> </div> {% endfor %} <div class="form-group"> <label for="">头像</label> <input type="file" id="myfile" name="avatar"> {% load static %} <img src="{% static '/img/default.png' %}" alt="" id="myimg" width="50px" style="margin-left: 10px"> </div> <input type="button" class="btn btn-primary pull-right" value="注册" id="id_commit"> </form> </div> </div> </div> </body>
用户头像实时展示
优化点一
# 创建一个static文件夹,该文件夹下创建一个img文件夹,在该文件夹下存放一张默认的头像图片 --static --img default.png # 添加代码 {% load static %} <img src="{% static 'img/default.png' %}" alt="" id='myimg' width="50px" style="margin-left: 10px">
优化点二
优化点三
当更换头像时,会在浏览器上实时更新你所更换的图片

<script> $('#myfile').change(function(){ // 文件阅读器对象 // 1.先生成一个文件阅读器对象 let myFileReaderObj = new FileReader() // 2.获取用户上传的头像文件 let fileObj = $(this)[0].files[0]; // 3.将文件对象交给阅读器对象读取 myFileReaderObj.readAsDataURL(fileObj) // 4.利用文件阅读器将文件展示到前端页面 修改图片img的src属性 $('#myimg').attr('src',myFileReaderObj.result) }) </script>
由于myFileReaderObj.readAsDataURL(fileObj)是一个异步操作,该代码执行完之后,不会等待文件阅读完毕就执行下面的操作,所以展示的图片为空
解决方案:等待文件阅读器加载之后再执行(等待某某执行之后再执行在js中使用 onload )

<script> $('#myfile').change(function(){ // 文件阅读器对象 // 1.先生成一个文件阅读器对象 let myFileReaderObj = new FileReader() // 2.获取用户上传的头像文件 let fileObj = $(this)[0].files[0]; // 3.将文件对象交给阅读器对象读取 myFileReaderObj.readAsDataURL(fileObj) // 异步操作 IO操作 // 4.利用文件阅读器将文件展示到前端页面 修改图片的src属性 // 等待文件阅读器加载完毕之后再执行(等待某某执行之后再执行 使用onload) myFileReaderObj.onload = function(){ $('#myimg').attr('src',myFileReaderObj.result) } }) </script>
注册功能的初步实现

<script> $('#myfile').change(function(){ // 文件阅读器对象 // 1.先生成一个文件阅读器对象 let myFileReaderObj = new FileReader() // 2.获取用户上传的头像文件 let fileObj = $(this)[0].files[0]; // 3.将文件对象交给阅读器对象读取 myFileReaderObj.readAsDataURL(fileObj) // 异步操作 IO操作 // 4.利用文件阅读器将文件展示到前端页面 修改图片的src属性 // 等待文件阅读器加载完毕之后再执行(等待某某执行之后再执行 使用onload) myFileReaderObj.onload = function(){ $('#myimg').attr('src',myFileReaderObj.result) } }) $('#id_commit').click(function(){ // 发送ajax请求 我们发送的数据中既包含普通的键值对也包含文件 let FormDataObj = new FormData() // 1.添加普通的键值对 {#console.log($('#myform').serializeArray()) // [{},{},{},{},{}]只包含普通键值对#} $.each($('#myform').serializeArray(),function (index,obj){ {#console.log(index,obj) // index是索引 obj为一个个对象{name: 'username', value: 'jiang'}#} FormDataObj.append(obj.name,obj.value) }) // 2.添加文件数据 FormDataObj.append('avatar',$('#myfile')[0].files[0]) // 3.发送ajax请求 $.ajax({ url:'', type:'post', data:FormDataObj, // 需要指定两个关键性的参数 contentType:false, processData:false, success:function(args){ if (args.code==1000){ // 跳转到登陆界面 window.location.href = args.url }else{ // 如何将对应的错误提示展示到对应的input框下面 // forms组件渲染的标签的id值都是 id_字段名 $.each(args.msg,function (index,obj) { {#console.log(index,obj) // username ["用户名不能为空"]#} let targetId = '#id_' + index; $(targetId).next().text(obj[0]).parent().addClass('has-error') }) } } }) }) </script>

from django.shortcuts import render from app01.myforms import MyRegForm from app01 import models from django.http import JsonResponse def register1(request): form_obj = MyRegForm() if request.method == 'POST': back_dic = {'code':1000,'msg':''} # 校验数据是否合法 form_obj = MyRegForm(request.POST) # 判断数据是否合法 if form_obj.is_valid(): # print(form_obj.cleaned_data) # {'username': 'jiang', 'password': '123', 'confirm_password': '123', 'email': '2573013863@qq.com'} clean_data = form_obj.cleaned_data # 将校验通过的数据字典赋值给一个变量 # 将字典里面的confirm_password键值对删除(是为了将数据保存在数据中,所以confirm_password不需要) clean_data.pop('confirm_password') # {'username': 'jiang', 'password': '123', 'email': '2573013863@qq.com'} # 用户头像 file_obj = request.FILES.get('avatar') """针对用户头像一定要判断是否传值 不能直接添加到字典里面去""" if file_obj: clean_data['avatar'] = file_obj # 直接操作数据库保存数据 models.UserInfo.objects.create_user(**clean_data) back_dic['url'] = '/login/' else: back_dic['code'] = 2000 back_dic['msg'] = form_obj.errors return JsonResponse(back_dic) return render(request,'register1.html',locals())
给所有的input框绑定获取焦点事件

<script> // 给所有的input框绑定获取焦点事件 $('input').focus(function () { // 将input下面的span标签和input外面的div标签修改内容及属性 $(this).next().text('').parent().removeClass('has-error') }) </script>
注册功能

from django.contrib import admin from django.urls import path,re_path from app01 import views urlpatterns = [ path('admin/', admin.site.urls), # 注册页面 re_path(r'^register1/', views.register), ]

# 书写针对用户表的forms组件代码 from django import forms from app01 import models class MyRegForm(forms.Form): username = forms.CharField(label='用户名', min_length=3, max_length=8, error_messages={ 'required': '用户名不能为空', 'min_length': '用户名最少3位', 'max_length': '用户名最多8位' }, # 还要让标签有bootstrap样式 widget=forms.widgets.TextInput(attrs={'class': 'form-control'}) ) password = forms.CharField(label='密码', min_length=3, max_length=8, error_messages={ 'required': '密码不能为空', 'min_length': '密码最少3位', 'max_length': '密码最多5位' }, # 还要让标签有bootstrap样式 widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'}) ) confirm_password = forms.CharField(label='确认密码', min_length=3, max_length=8, error_messages={ 'required': '密码不能为空', 'min_length': '密码最少3位', 'max_length': '密码最多8位' }, # 还要让标签有bootstrap样式 widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'}) ) email = forms.CharField(label='邮箱', error_messages={ 'required': '邮箱不能为空', 'invalid': '邮箱格式不正确' }, ) # 局部钩子 def clean_username(self): # 获取到用户名 username = self.cleaned_data.get('username') # 数据通过了form组件条件的数据都会存储在cleaned_data里,所以我们拿出这里面(过了第一道关卡)的数据,进行添加第二道关卡 if 'yuan' in username: # 提示前端展示错误信息 self.add_error('username', '用户名不能存在yuan') # 将钩子函数勾出来的数据再放回去 return username # 全局钩子 def clean(self): password = self.cleaned_data.get('password') confirm_password = self.cleaned_data.get('confirm_password') if not password == confirm_password: self.add_error('confirm_password', '两次密码不一致') return self.cleaned_data

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script> <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script> </head> <body> <div class="container-fluid"> <div class="row"> <div class="col-md-8 col-md-offset-2"> <h1 class="text-center">注册</h1> <form id="myform"> <!--这里我们不用form表单提交数据 知识单纯的用一下form标签而已--> {% csrf_token %} {% for form in form_obj %} <div class="form-group"> <label for="{{ form.auto_id }}">{{ form.label }}</label> {{ form }} <span style="color: red">{{ form.errors.0 }}</span> <span style="color: red"></span> </div> {% endfor %} <div class="form-group"> <label for="myfile">头像 {% load static %} <img src="{% static 'img/default.png' %}" alt="" id='myimg' alt="" width="50" style="margin-left: 10px"> </label> <input type="file" id="myfile" name="avatar" style="display: none"> </div> <input type="button" class="btn btn-primary pull-right" value="注册" id="id_commit"> </form> </div> </div> </div> <script> $('#myfile').change(function(){ // 文件阅读器对象 // 1.先生成一个文件阅读器对象 let myFileReaderObj = new FileReader() // 2.获取用户上传的头像文件 let fileObj = $(this)[0].files[0]; // 3.将文件对象交给阅读器对象读取 myFileReaderObj.readAsDataURL(fileObj) // 异步操作 IO操作 // 4.利用文件阅读器将文件展示到前端页面 修改图片的src属性 // 等待文件阅读器加载完毕之后再执行(等待某某执行之后再执行 使用onload) myFileReaderObj.onload = function(){ $('#myimg').attr('src',myFileReaderObj.result) } }) $('#id_commit').click(function(){ // 发送ajax请求 我们发送的数据中既包含普通的键值对也包含文件 let FormDataObj = new FormData() // 1.添加普通的键值对 {#console.log($('#myform').serializeArray()) // [{},{},{},{},{}]只包含普通键值对#} $.each($('#myform').serializeArray(),function (index,obj){ {#console.log(index,obj) // index是索引 obj为一个个对象{name: 'username', value: 'jiang'}#} FormDataObj.append(obj.name,obj.value) }) // 2.添加文件数据 FormDataObj.append('avatar',$('#myfile')[0].files[0]) // 3.发送ajax请求 $.ajax({ url:'', type:'post', data:FormDataObj, // 需要指定两个关键性的参数 contentType:false, processData:false, success:function(args){ if (args.code==1000){ // 跳转到登陆界面 window.location.href = args.url }else{ // 如何将对应的错误提示展示到对应的input框下面 // forms组件渲染的标签的id值都是 id_字段名 $.each(args.msg,function (index,obj) { {#console.log(index,obj) // username ["用户名不能为空"]#} let targetId = '#id_' + index; $(targetId).next().text(obj[0]).parent().addClass('has-error') }) } } }) }) // 给所有的input框绑定获取焦点事件 $('input').focus(function () { // 将input下面的span标签和input外面的div标签修改内容及属性 $(this).next().text('').parent().removeClass('has-error') }) </script> </body> </html>

from django.shortcuts import render from app01.myforms import MyRegForm from app01 import models from django.http import JsonResponse # Create your views here. def register(request): form_obj = MyRegForm() if request.method == 'POST': back_dic = {'code':1000,'msg':''} # 校验数据是否合法 form_obj = MyRegForm(request.POST) # 判断数据是否合法 if form_obj.is_valid(): # print(form_obj.cleaned_data) # {'username': 'jiang', 'password': '123', 'confirm_password': '123', 'email': '2573013863@qq.com'} clean_data = form_obj.cleaned_data # 将校验通过的数据字典赋值给一个变量 # 将字典里面的confirm_password键值对删除(是为了将数据保存在数据中,所以confirm_password不需要) clean_data.pop('confirm_password') # {'username': 'jiang', 'password': '123', 'email': '2573013863@qq.com'} # 用户头像 file_obj = request.FILES.get('avatar') """针对用户头像一定要判断是否传值 不能直接添加到字典里面去""" if file_obj: clean_data['avatar'] = file_obj # 直接操作数据库保存数据 models.UserInfo.objects.create_user(**clean_data) back_dic['url'] = '/login/' else: back_dic['code'] = 2000 back_dic['msg'] = form_obj.errors return JsonResponse(back_dic) return render(request,'register.html',locals())
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现