2.1博客系统 |基于form组件和Ajax实现注册登录
http://www.cnblogs.com/yuanchenqi/articles/7638956.html
1、设计注册页面
views.py
from django import forms class UserForm(forms.Form): user=forms.CharField(max_length=32) pwd=forms.CharField(max_length=32) re_pwd=forms.CharField(max_length=32) email=forms.EmailField(max_length=32) def register(request): # 实例化 form 对象 form=UserForm() return render(request,'register.html',locals())
运行后样式不好看,需要加样式
增加 django.forms import widgets
继续修改名字
博客注册压面头像功能
1、点击头像相当于点击上传文件input标签
把用户名密码登陆框业设计成这种模式,更有利于交互
------
隐藏上传文件的input标签
效果图:
2、头像预览功能
(1)获取文件对象,(2)获取文件对象的路径,(3)修改img的src属性,src=文件对象的路径
img绑定一个change事件
views.py
# 注册功能 from django import forms from django.forms import widgets class UserForm(forms.Form): user=forms.CharField(max_length=32,widget=widgets.TextInput(attrs={'class':'form-control'}), label='用户名') pwd=forms.CharField(max_length=32,widget=widgets.PasswordInput(attrs={'class':'form-control'}), label='密码') re_pwd=forms.CharField(max_length=32,widget=widgets.PasswordInput(attrs={'class':'form-control'}), label='确认密码') email=forms.EmailField(max_length=32,widget=widgets.EmailInput(attrs={'class':'form-control'}), label='邮箱') def register(request): # 实例化 form 对象 form=UserForm() return render(request,'register.html',locals())
模板层
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/blog/bootstrap-3.3.7/css/bootstrap.css"> <style type="text/css"> #avatar{display: none} #avatar_img{margin-left: 20px} </style> </head> <body> <div class="container"> <div class="row"> <h3>注册页面</h3> <div class="col-md-6 col-md-offset-3"> <form> {% csrf_token %} {% for field in form %} <div class="form-group"> <label for="{{ field.auto_id }}">{{ field.label }}</label> {{ field }} <span class="error pull-right"></span> </div> {% endfor %} {# 上传头像功能#} <div class="form-group"> <label for="avatar"> 头像 <img id="avatar_img" width="60" height="60" src="/static/img/default.png" alt=""> </label> <input type="file" id="avatar" name="avatar" > </div> <div class="form-group"> <div class="row"> <a href="/register/" class="btn btn-success pull-right">注册</a> </div> </div> </form> </div> </div> </div> <script type="text/javascript" src="/static/JS/jquery-3.2.1.min.js"></script> <script type="text/javascript"> $('#avatar').change(function () { // 1 获取用户选中的文件对象 var file_obj=$(this)[0].files[0]; // 2 获取文件路径 var reader= new FileReader(); reader.readAsDataURL(file_obj); // 3 修改img的src属性,src=文件对象的路径 // 等图片文档加载完后才执行,,等上一步读完才执行 reader.onload=function(){ $('#avatar_img').attr('src',reader.result) } }) </script> </body> </html>
Ajax提交formdata数据
(1)点击注册按钮--FormData 对象的使用:
FormData 对象的使用: 1.用一些键值对来模拟一系列表单控件:即把form中所有表单元素的name与value组装成 一个queryString 2. 异步上传二进制文件。
Django之JsonResponse
https://www.cnblogs.com/guoyunlong666/p/9099397.html
register
// Ajax提交数据 $('.reg_btn').click(function () { var formdata=new FormData(); formdata.append('user',$('#id_user').val()); formdata.append('pwd',$('#id_pwd').val()); formdata.append('re_pwd',$('#id_re_pwd').val()); formdata.append('email',$('#id_email').val()); formdata.append('avatar',$('#avatar')[0].files[0]); formdata.append('csrfmiddlewaretoken',$("[name='csrfmiddlewaretoken']").val()); $.ajax({ url:'', type:'post', contentType:false, processData:false, data:formdata, success:function (data) { console.log(data) } }) })
试图函数
def register(request): # 实例化 form 对象 if request.is_ajax(): print(request.POST) # 对传来的数据进行验证 form = UserForm(request.POST) response={'user':None,'msg':None} if form.is_valid(): #注册成功要把数据添加进数据库 response['user']=form.cleaned_data.get('user') else: print(form.cleaned_data) print(form.errors) response['msg']=form.errors return JsonResponse(response) form=UserForm() return render(request,'register.html',locals())
formdata上传数据优化
// 基于Ajax提交数据 $(".reg_btn").click(function () { //console.log($("#form").serializeArray()); var formdata = new FormData(); var request_data = $("#form").serializeArray(); $.each(request_data, function (index, data) { formdata.append(data.name, data.value) }); # 把特殊的单独拿出来写 formdata.append("avatar", $("#avatar")[0].files[0]);
注册时展示错误信息---ajax的success做判断
效果一、没有填写信息按提交,现实提示错误
$.ajax({ url:'', type:'post', contentType:false, processData:false, data:formdata, success:function (data) { console.log(data); if(data.user){ // 注册成功 } else { $.each(data.msg,function (filed,error_list) { console.log(filed,error_list); //jQuery next() 获得匹配元素集合中每个元素紧邻的同胞元素。 $("#id_"+filed).next().html(error_list[0]) }) } } })
效果改进
输入信息后,提交,之前的错误提醒消失
展示错误标签--添加class='has-error',错误的出现红框
forms组件的局部钩子,全局钩子
代码解耦--将UserForm
把UserForm代码放入到Myforms.py模块中
在原来的views.py中引入模块
from blog.Myforms import UserForm
class UserForm(forms.Form):封装了很多函数用来检验form表单输入的值
Myforms.py
Myforms
from django import forms from django.forms import widgets from django.core.exceptions import NON_FIELD_ERRORS, ValidationError from blog.models import UserInfo class UserForm(forms.Form): user=forms.CharField(max_length=32,error_messages={'required':'该字段不能为空'}, widget=widgets.TextInput(attrs={'class':'form-control'}), label='用户名') pwd=forms.CharField(max_length=32,widget=widgets.PasswordInput(attrs={'class':'form-control'}), label='密码') re_pwd=forms.CharField(max_length=32,widget=widgets.PasswordInput(attrs={'class':'form-control'}), label='确认密码') email=forms.EmailField(max_length=32,widget=widgets.EmailInput(attrs={'class':'form-control'}), label='邮箱') def clean_user(self): val = self.cleaned_data.get("user") user = UserInfo.objects.filter(username=val).first() if not user: return val else: raise ValidationError("该用户已注册!") def clean(self): pwd=self.cleaned_data.get('pwd') re_pwd=self.cleaned_data.get('re_pwd') if pwd==re_pwd: return self.cleaned_data else: raise ValidationError('两次密码不一致')
register.html错误中加一个显示密码不一致的信息
$.each(data.msg,function (field,error_list) { console.log(field,error_list); // 全局错误在视图中取出来 if (field=='__all__'){ $('#id_re_pwd').next().html(error_list[0]).parent().addClass("has-error"); }
上面全局钩子存在bug如果只写密码,不写确认密码也出现两次密码不一致
改进:加个 判断
注册成功,添加数据
register模板层 跳转到view视图
注册成功跳转到登录页面
if(data.user){ // 注册成功 location.href="/login/" }
---
def register(request): # 实例化 form 对象 if request.is_ajax(): print(request.POST) # 对传来的数据进行验证 form = UserForm(request.POST) response={'user':None,'msg':None} if form.is_valid(): response['user'] = form.cleaned_data.get('user') #注册成功要把数据添加进数据库 # 生成一条用户记录 user=form.cleaned_data.get('user') pwd=form.cleaned_data.get('pwd') email=form.cleaned_data.get('email') avatar_obj=request.FILES.get('avatar') # 创建数据到数据库 user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email, avatar=avatar_obj) else: print(form.cleaned_data) print(form.errors) response['msg']=form.errors
# 返回给Ajax return JsonResponse(response) form=UserForm() return render(request,'register.html',locals())
FieldFile字段
# 4 FileField与ImageFiled 区别是: fileField是可以任何文件 ImageFile只能是图片文件
avatar_obj=request.FILES.get('avatar')
user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email, avatar=avatar_obj)
写上这两行代码
Django 会将文件对象下载到项目的根目录中avatars文件夹中(如果没有avatar文件夹,Django会自动创建),user_obj的avatar存的是文件的相对路径。
media配置
在主目录下创建media
Dajngo有两种静态文件: /static/ : js,css,img /media/ : 用户上传文件
settings配置
# MEDIA配置:与用户上传相关的配置 # 配置1:用户上传头像的文件 MEDIA_ROOT = os.path.join(BASE_DIR, "media") # 配置2:开放media目录给用户 MEDIA_URL = "/media/"
解决一个小问题当用户没有上传图片:
def register
avatar_obj=request.FILES.get('avatar') if avatar_obj: user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email, avatar=avatar_obj) else: user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email)
没有上传就用默认的头像
开放media目录给用户
在路由下配置
re_path(r'media/(?P<path>.*)$',serve,{'document_root':settings.MEDIA_ROOT})
settings.py
MEDIA_URL = "/media/" MEDIA_ROOT=os.path.join(BASE_DIR,'media')
代码优化:
代码规范:
完整代码:
urls
"""cnblog URL Configuration The `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/2.1/topics/http/urls/ Examples: Function views 1. Add an import: from my_app import views 2. Add a URL to urlpatterns: path('', views.home, name='home') Class-based views 1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') Including another URLconf 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.contrib import admin from django.urls import path,re_path from django.views.static import serve from cnblog import settings from blog import views urlpatterns = [ path('admin/', admin.site.urls), path('login/', views.login), path('index/', views.index), path('register/', views.register), path('get_validCode_img/', views.get_validCode_img), re_path(r'media/(?P<path>.*)$',serve,{'document_root':settings.MEDIA_ROOT}) ]
View
from django.shortcuts import render # Create your views here. from django.shortcuts import render,HttpResponse # Create your views here. # 用户认证模块 from django.contrib import auth from django.http import JsonResponse from blog.Myforms import UserForm from blog.models import UserInfo def login(request): if request.method=='POST': response={'user':None,'msg':None} user=request.POST.get('user') pwd=request.POST.get('pwd') # 读取验证码 valid_code=request.POST.get('valid_code') # 回话跟踪技术,保存验证码 valid_code_str=request.session.get('valid_code_str') # 如果书写的验证码和生成的验证码一致 # 不区分大小写---可以统一变成大写 if valid_code.upper()==valid_code_str.upper(): user=auth.authenticate(username=user,password=pwd) if user: auth.login(request,user)# request.user==当前登录对象 response['user']=user.username else: response['msg']='username or password error' else: response['msg']='valid code error!' return JsonResponse(response) # ajax返回一个响应字符串 return render(request,'login.html') def get_validCode_img(request): from blog.utils.validCode import get_valid_Code_img data=get_valid_Code_img(request) return HttpResponse(data) def index(request): return render(request,'index.html',locals()) def register(request): # 实例化 form 对象 if request.is_ajax(): print(request.POST) # 对传来的数据进行验证 form = UserForm(request.POST) response={'user':None,'msg':None} if form.is_valid(): response['user'] = form.cleaned_data.get('user') #注册成功要把数据添加进数据库 # 生成一条用户记录 user=form.cleaned_data.get('user') pwd=form.cleaned_data.get('pwd') email=form.cleaned_data.get('email') avatar_obj=request.FILES.get('avatar') if avatar_obj: user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email, avatar=avatar_obj) else: user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email) else: print(form.cleaned_data) print(form.errors) response['msg']=form.errors # 返回给Ajax return JsonResponse(response) form=UserForm() return render(request,'register.html',locals())
models
from django.db import models # Create your models here. from django.contrib.auth.models import User, AbstractUser class UserInfo(AbstractUser): """ 用户信息 """ nid = models.AutoField(primary_key=True) telephone = models.CharField(max_length=11, null=True, unique=True) avatar = models.FileField(upload_to='avatars/', default="/avatars/default.png") create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) blog = models.OneToOneField(to='Blog', to_field='nid', null=True, on_delete=models.CASCADE) def __str__(self): return self.username class Blog(models.Model): """ 博客信息表(站点表) """ nid = models.AutoField(primary_key=True) title = models.CharField(verbose_name='个人博客标题', max_length=64) site_name = models.CharField(verbose_name='站点名称', max_length=64) theme = models.CharField(verbose_name='博客主题', max_length=32) def __str__(self): return self.title class Category(models.Model): """ 博主个人文章分类表 """ nid = models.AutoField(primary_key=True) title = models.CharField(verbose_name='分类标题', max_length=32) blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid', on_delete=models.CASCADE) def __str__(self): return self.title class Tag(models.Model): nid = models.AutoField(primary_key=True) title = models.CharField(verbose_name='标签名称', max_length=32) blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid', on_delete=models.CASCADE) def __str__(self): return self.title class Article(models.Model): nid = models.AutoField(primary_key=True) title = models.CharField(max_length=50, verbose_name='文章标题') desc = models.CharField(max_length=255, verbose_name='文章描述') create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) content = models.TextField() comment_count = models.IntegerField(default=0) up_count = models.IntegerField(default=0) down_count = models.IntegerField(default=0) user = models.ForeignKey(verbose_name='作者', to='UserInfo', to_field='nid', on_delete=models.CASCADE) category = models.ForeignKey(to='Category', to_field='nid', null=True, on_delete=models.CASCADE) tags = models.ManyToManyField( to="Tag", through='Article2Tag', through_fields=('article', 'tag'), ) def __str__(self): return self.title class Article2Tag(models.Model): nid = models.AutoField(primary_key=True) article = models.ForeignKey(verbose_name='文章', to="Article", to_field='nid', on_delete=models.CASCADE) tag = models.ForeignKey(verbose_name='标签', to="Tag", to_field='nid', on_delete=models.CASCADE) class Meta: unique_together = [ ('article', 'tag'), ] def __str__(self): v = self.article.title + "---" + self.tag.title return v class ArticleUpDown(models.Model): """ 点赞表 """ nid = models.AutoField(primary_key=True) user = models.ForeignKey('UserInfo', null=True, on_delete=models.CASCADE) article = models.ForeignKey("Article", null=True, on_delete=models.CASCADE) is_up = models.BooleanField(default=True) class Meta: unique_together = [ ('article', 'user'), ] class Comment(models.Model): """ 评论表 """ nid = models.AutoField(primary_key=True) user = models.ForeignKey(verbose_name='评论者', to='UserInfo', to_field='nid', on_delete=models.CASCADE) article = models.ForeignKey(verbose_name='评论文章', to='Article', to_field='nid', on_delete=models.CASCADE) create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) content = models.CharField(verbose_name='评论内容', max_length=255) parent_comment = models.ForeignKey("self", null=True, on_delete=models.CASCADE) def __str__(self): return self.content
Myform
# 注册功能 from django import forms from django.forms import widgets from django.core.exceptions import NON_FIELD_ERRORS, ValidationError from blog.models import UserInfo class UserForm(forms.Form): user=forms.CharField(max_length=32,error_messages={'required':'该字段不能为空'}, widget=widgets.TextInput(attrs={'class':'form-control'}), label='用户名') pwd=forms.CharField(max_length=32,widget=widgets.PasswordInput(attrs={'class':'form-control'}), label='密码') re_pwd=forms.CharField(max_length=32,widget=widgets.PasswordInput(attrs={'class':'form-control'}), label='确认密码') email=forms.EmailField(max_length=32,widget=widgets.EmailInput(attrs={'class':'form-control'}), label='邮箱') def clean_user(self): val = self.cleaned_data.get("user") user = UserInfo.objects.filter(username=val).first() if not user: return val else: raise ValidationError("该用户已注册!") def clean(self): pwd=self.cleaned_data.get('pwd') re_pwd=self.cleaned_data.get('re_pwd') if pwd and re_pwd: if pwd==re_pwd: return self.cleaned_data else: raise ValidationError('两次密码不一致') else: return self.cleaned_data
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/blog/bootstrap-3.3.7/css/bootstrap.css"> </head> <body> <div class="container"> <div class="row"> <div class="col-md-6 col-md-offset-3"> <form> {% csrf_token %} <h3 class="col-md-offset-5">登录页面</h3> <div class="form-group "> <label for="username">username</label> <input type="text" class="form-control " placeholder="username" id="username"> </div> <div class="form-group "> <label for="password">Password</label> <input type="password" class="form-control " id="password" placeholder="Password"> </div> <div class="form-group"> <label for="id_valid_code_str">验证码</label> <div class="row "> <div class="col-md-6"> <input type="text" id="id_valid_code_str" class="form-control"> </div> <div class="col-md-6"> <img src="/get_validCode_img/" id="id_valid_code_img" width="270" height="34" title="验证码" alt=""> </div> </div> </div> <div class="form-group"> <div class="row"> <div class="col-md-6"> <input type="button" class="btn btn-default login-btn pull-left" value="登录"><span class="error"></span> </div> <div class="col-md-6"> <a href="/register/" class="btn btn-success pull-right">注册</a> </div> </div> </div> </form> </div> </div> </div> <script type="text/javascript" src="/static/JS/jquery-3.2.1.min.js"></script> <script type="text/javascript"> //刷新验证码 $(function () { $("#id_valid_code_img").click(function () { $(this)[0].src+="?" }) }); // 登录验证 $('.login-btn').click(function () { $.ajax( { url:'', type:'post', data:{user:$('#username').val(),pwd:$('#password').val(),valid_code:$('#id_valid_code_str').val(), {# {% csrf_token %}#} csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val()}, success:function (data) { console.log(data); if(data.user){ location.href='/index' } else { $('.error').text(data.msg).css({'color':'red','margin-left':'10px'}) setTimeout(function () { $('.error').text('') },1000) } } }) }) </script> </body> </html>
register.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/blog/bootstrap-3.3.7/css/bootstrap.css"> <style type="text/css"> #avatar{display: none} #avatar_img{margin-left: 20px} .error { color: red; } </style> </head> <body> <div class="container"> <div class="row"> <h3>注册页面</h3> <div class="col-md-6 col-md-offset-3"> <form id="form"> {% csrf_token %} {% for field in form %} <div class="form-group"> <label for="{{ field.auto_id }}">{{ field.label }}</label> {{ field }} <span class="error pull-right"></span> </div> {% endfor %} {# 上传头像功能#} <div class="form-group"> <label for="avatar"> 头像 <img id="avatar_img" width="60" height="60" src="/static/img/default.png" alt=""> </label> <input type="file" id="avatar" name="avatar" > </div> <div class="form-group"> <div class="row"> <input type="button" class="btn btn-default reg_btn" value="submit"><span class="error"></span> </div> </div> </form> </div> </div> </div> <script type="text/javascript" src="/static/JS/jquery-3.2.1.min.js"></script> <script type="text/javascript"> $('#avatar').change(function () { // 1 获取用户选中的文件对象 var file_obj=$(this)[0].files[0]; // 2 获取文件路径 var reader= new FileReader(); reader.readAsDataURL(file_obj); // 3 修改img的src属性,src=文件对象的路径 // 等图片文档加载完后才执行,,等上一步读完才执行 reader.onload=function(){ $('#avatar_img').attr('src',reader.result) } }); // Ajax提交数据 {#$('.reg_btn').click(function () {#} {# var formdata=new FormData();#} {# formdata.append('user',$('#id_user').val());#} {# formdata.append('pwd',$('#id_pwd').val());#} {# formdata.append('re_pwd',$('#id_re_pwd').val());#} {# formdata.append('email',$('#id_email').val());#} {# formdata.append('avatar',$('#avatar')[0].files[0]);#} {# formdata.append('csrfmiddlewaretoken',$("[name='csrfmiddlewaretoken']").val());#} // 基于Ajax提交数据 $(".reg_btn").click(function () { //console.log($("#form").serializeArray()); var formdata = new FormData(); var request_data = $("#form").serializeArray(); $.each(request_data, function (index, data) { formdata.append(data.name, data.value) }); formdata.append("avatar", $("#avatar")[0].files[0]); $.ajax({ url:'', type:'post', contentType:false, processData:false, data:formdata, success:function (data) { console.log(data); if(data.user){ // 注册成功 location.href="/login/" } else { //console.log(data.msg) // 清空错误信息---先清空所有--移除所有样式 $("span.error").html(""); $(".form-group").removeClass("has-error"); // 再把含有错误的标签 赋值,加样式--达到相应的效果 $.each(data.msg,function (field,error_list) { console.log(field,error_list); // 全局错误在视图中取出来 if (field=='__all__'){ $('#id_re_pwd').next().html(error_list[0]).parent().addClass("has-error"); } //jQuery next() 获得匹配元素集合中每个元素紧邻的同胞元素。 $("#id_"+ field).next().html(error_list[0]); $("#id_"+ field).parent().addClass("has-error"); }) } } }) }) </script> </body> </html>
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {{ request.user}} <h3>this is my index</h3> </body> </html>
8、总结
基于forms组件和Ajax实现注册功能 # 1 基于forms组件设计注册页面 ---点击头像===点击input ---头像预览: 1 获取用户选中的文件对象 2 获取文件对象的路径 3 修改img的src属性 ,src=文件对象的路径 # 2 错误信息: views: form.erorrs # {"user":[......]} Ajax.success: $.each(data.msg, function (field, error_list) { $("#id_" + field).next().html(error_list[0]); $("#id_" + field).parent().addClass("has-error"); }) # 3 局部钩子和全局钩子校验 user字段不能重复 两次密码不一致 # 4 FileField与ImageFiled 区别是:
fileField是可以任何文件
ImageFile只能是图片文件 class UserInfo(AbstractUser): nid = models.AutoField(primary_key=True) telephone = models.CharField(max_length=11, null=True, unique=True) avatar = models.FileField(upload_to='avatars/', default="/avatars/default.png") avatar_obj=request.FILES.get("avatar") user_obj=UserInfo.objects.create_user(username=user,password=pwd,email=email,avatar=avatar_obj) Dajngo实现: 会将文件对象下载到项目的根目录中avatars文件夹中(如果没有avatar文件夹,Django会自动创建),user_obj的avatar存的是文件的相对路径。 # 5 Media 配置之MEDIA_ROOT: Dajngo有两种静态文件: /static/ : js,css,img /media/ : 用户上传文件 class UserInfo(AbstractUser): nid = models.AutoField(primary_key=True) telephone = models.CharField(max_length=11, null=True, unique=True) avatar = models.FileField(upload_to='avatars/', default="/avatars/default.png") avatar_obj=request.FILES.get("avatar") user_obj=UserInfo.objects.create_user(username=user,password=pwd,email=email,avatar=avatar_obj) 一旦配置了 MEDIA_ROOT=os.path.join(BASE_DIR,"media") Dajngo实现: 会将文件对象下载到MEDIA_ROOT中avatars文件夹中(如果没有avatar文件夹,Django会自动创建),user_obj的avatar存的是文件的相对路径。 # 6 Media 配置之MEDIA_URl: 浏览器如何能直接访问到media中的数据 settings.py: MEDIA_URL="/media/" urls.pt: # media配置: re_path(r"media/(?P<path>.*)$",serve,{"document_root":settings.MEDIA_ROOT})