一、博客系统表关系
models.py
from django.contrib.auth.models import AbstractUser from django.db import models # Create your models here. 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
同步数据库:
python manage.py makemigrations
python manage.py migrate
二、基于Ajax和用户登录验证
登录页面
验证码获取:
方式一:
def get_valid_code_img(request): # 方式1: with open("lufei.jpg","rb") as f: data=f.read() return HttpResponse(data)
方式二:
from PIL import Image img = Image.new("RGB", (270, 40),color=get_random_color()) with open("validCode.png", "wb") as f: img.save(f,"png") with open("validCode.png", "rb") as f: data = f.read() return HttpResponse(data)
方式三:
from PIL import Image from io import BytesIO img = Image.new("RGB", (270, 40), color=get_random_color()) f = BytesIO() img.save(f, "png") data=f.getvalue() return HttpResponse(data)
方式四:
img = Image.new("RGB", (270, 40), color=get_random_color()) draw = ImageDraw.Draw(img) kumo_font = ImageFont.truetype("static/font/KumoFont.ttf", size=20) valid_code_str = "" for i in range(5): random_num = str(random.randint(0, 9)) random_low_alpha = chr(random.randint(95, 122)) random_upper_alpha = chr(random.randint(65, 90)) random_char = random.choice([random_num, random_low_alpha, random_upper_alpha]) draw.text((20 + i * 50, 15), random_char, get_random_color(), font=kumo_font) # 保存验证码字符串 valid_code_str += random_char # width = 270 # height = 40 # for i in range(5): # x1 = random.randint(0, width) # x2 = random.randint(0, width) # y1 = random.randint(0, height) # y2 = random.randint(0, height) # draw.line((x1, y1, x2, y2), fill=get_random_color()) # # for i in range(10): # draw.point([random.randint(0, width), random.randint(0, height)], fill=get_random_color()) # x = random.randint(0, width) # y = random.randint(0, height) # draw.arc((x, y, x+4, y+4), 0, 90, fill=get_random_color()) print("valid_code_str", valid_code_str) request.session["valid_code_str"] = valid_code_str f = BytesIO() img.save(f, "png") data = f.getvalue() return data
验证码正确,并登录之后
<li><a href="#"><span id="user_icon" class="glyphicon glyphicon-user"></span>{{ request.user.username }} </a> </li>
登录验证
<script src="/static/jquery-3.3.1.js"></script> <script> // 刷新验证码 $("#valid_code_img").click(function () { $(this)[0].src += "?" }); // 登录验证 $(".login_btn").click(function () { $.ajax({ url: "", type: "post", data: { user: $("#user").val(), pwd: $("#pwd").val(), valid_code: $("#valid_code").val(), csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(), }, success: function (data) { console.log(data); if (data.user){ if (location.search){ location.href = location.search.slice(6) } else { location.href = "/index/" } } else { $(".error").text(data.msg).css({"color": "red", "margin-left": "10px"}); setTimeout(function () { $(".error").text(""); }, 1000) } } }) })
完整的示例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 from django.contrib.auth.models import AbstractUser 2 from django.db import models 3 4 # Create your models here. 5 6 7 class UserInfo(AbstractUser): 8 """ 9 用户信息 10 """ 11 nid = models.AutoField(primary_key=True) 12 telephone = models.CharField(max_length=11, null=True, unique=True) 13 avatar = models.FileField(upload_to='avatars/', default="/avatars/default.png") 14 create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) 15 16 blog = models.OneToOneField(to='Blog', to_field='nid', null=True, on_delete=models.CASCADE) 17 18 def __str__(self): 19 return self.username 20 21 22 class Blog(models.Model): 23 """ 24 博客信息表(站点表) 25 """ 26 nid = models.AutoField(primary_key=True) 27 title = models.CharField(verbose_name='个人博客标题', max_length=64) 28 site_name = models.CharField(verbose_name='站点名称', max_length=64) 29 theme = models.CharField(verbose_name='博客主题', max_length=32) 30 31 def __str__(self): 32 return self.title 33 34 35 class Category(models.Model): 36 """ 37 博主个人文章分类表 38 """ 39 nid = models.AutoField(primary_key=True) 40 title = models.CharField(verbose_name='分类标题', max_length=32) 41 blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid', on_delete=models.CASCADE) 42 43 def __str__(self): 44 return self.title 45 46 47 class Tag(models.Model): 48 nid = models.AutoField(primary_key=True) 49 title = models.CharField(verbose_name='标签名称', max_length=32) 50 blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid', on_delete=models.CASCADE) 51 52 def __str__(self): 53 return self.title 54 55 56 class Article(models.Model): 57 nid = models.AutoField(primary_key=True) 58 title = models.CharField(max_length=50, verbose_name='文章标题') 59 desc = models.CharField(max_length=255, verbose_name='文章描述') 60 create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) 61 content = models.TextField() 62 63 comment_count = models.IntegerField(default=0) 64 up_count = models.IntegerField(default=0) 65 down_count = models.IntegerField(default=0) 66 67 user = models.ForeignKey(verbose_name='作者', to='UserInfo', to_field='nid', on_delete=models.CASCADE) 68 category = models.ForeignKey(to='Category', to_field='nid', null=True, on_delete=models.CASCADE) 69 tags = models.ManyToManyField( 70 to="Tag", 71 through='Article2Tag', 72 through_fields=('article', 'tag'), 73 ) 74 75 def __str__(self): 76 return self.title 77 78 79 class Article2Tag(models.Model): 80 nid = models.AutoField(primary_key=True) 81 article = models.ForeignKey(verbose_name='文章', to="Article", to_field='nid', on_delete=models.CASCADE) 82 tag = models.ForeignKey(verbose_name='标签', to="Tag", to_field='nid', on_delete=models.CASCADE) 83 84 class Meta: 85 unique_together = [ 86 ('article', 'tag'), 87 ] 88 89 def __str__(self): 90 v = self.article.title + "---" + self.tag.title 91 return v 92 93 94 class ArticleUpDown(models.Model): 95 """ 96 点赞表 97 """ 98 nid = models.AutoField(primary_key=True) 99 user = models.ForeignKey('UserInfo', null=True, on_delete=models.CASCADE) 100 article = models.ForeignKey("Article", null=True, on_delete=models.CASCADE) 101 is_up = models.BooleanField(default=True) 102 103 class Meta: 104 unique_together = [ 105 ('article', 'user') 106 ] 107 108 109 class Comment(models.Model): 110 """ 111 评论表 112 """ 113 nid = models.AutoField(primary_key=True) 114 user = models.ForeignKey(verbose_name='评论者', to='UserInfo', to_field='nid', on_delete=models.CASCADE) 115 article = models.ForeignKey(verbose_name='评论文章', to='Article', to_field='nid', on_delete=models.CASCADE) 116 create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) 117 content = models.CharField(verbose_name='评论时间', max_length=255) 118 119 parent_comment = models.ForeignKey("self", null=True, on_delete=models.CASCADE) 120 121 def __str__(self): 122 return self.content
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 from django.contrib import auth 2 from django.http import JsonResponse, HttpResponse 3 from django.shortcuts import render 4 5 # Create your views here. 6 from blog import models 7 from blog.Myforms import UserForm 8 from blog.models import UserInfo 9 from blog.utils import validCode 10 11 12 def login(request): 13 """ 14 登录视图函数 15 get请求响应页面 16 post(Ajax)请求响应字典 17 :param request: 18 :return 19 """ 20 if request.method == "POST": 21 response = {"user": None, "msg": None} 22 user = request.POST.get("user") 23 pwd = request.POST.get("pwd") 24 valid_code = request.POST.get("valid_code") 25 26 valid_code_str = request.session.get("valid_code_str") 27 if valid_code.upper() == valid_code_str.upper(): 28 user = auth.authenticate(username=user, password=pwd) 29 if user: 30 auth.login(request, user) 31 response["user"] = user.username 32 else: 33 response["msg"] = "用户名或密码错误" 34 35 else: 36 response["msg"] = "验证码错误!" 37 38 return JsonResponse(response) 39 return render(request, "login.html") 40 41 42 def index(request): 43 """ 44 系统首页 45 :param request: 46 :return: 47 """ 48 article_list = models.Article.objects.all() 49 return render(request, "index.html", {"article_list": article_list}) 50 51 52 def get_valid_code_img(request): 53 """ 54 基于PIL模块动态生成响应状态码图片 55 :param request: 56 :return: 57 """ 58 img_data = validCode.get_valid_code_img(request) 59 return HttpResponse(img_data) 60 61 62 def register(request): 63 """ 64 注册视图函数: 65 get请求响应注册页面 66 POst(Ajax)请求,校验字段,响应字典 67 :param request: 68 :return: 69 """ 70 if request.is_ajax(): 71 print(request.POST) 72 form = UserForm(request.POST) 73 74 response = {"user": None, "msg": None} 75 if form.is_valid(): 76 response["user"] = form.cleaned_data.get("user") 77 78 # 生成一条用户记录 79 user = form.cleaned_data.get("user") 80 print("user", user) 81 pwd = form.cleaned_data.get("pwd") 82 email = form.cleaned_data.get("email") 83 avatar_obj = request.FILES.get("avater") 84 85 extra = {} 86 if avatar_obj: 87 extra["avatar"] = avatar_obj 88 UserInfo.objects.create_user(username=user, password=pwd, email=email, **extra) 89 90 else: 91 print(form.cleaned_data) 92 print(form.errors) 93 response["msg"] = form.errors 94 95 return JsonResponse(response) 96 97 form = UserForm() 98 return render(request, "register.html", {"form": form}) 99 100 101 return render(request, "register.html")
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # -*- encoding: utf-8 -*- 2 # @Time : 2018-09-24 20:52 3 # @Author : mike.liu 4 # @File : validCode.py 5 import random 6 from io import BytesIO 7 8 from PIL import Image, ImageDraw, ImageFont 9 10 11 def get_random_color(): 12 return random.randint(0, 255), random.randint(0, 255), random.randint(0, 255) 13 14 15 def get_valid_code_img(request): 16 # 方式1: 17 # with open("lufei.jpg", "rb") as f: 18 # data = f.read() 19 20 # 方式2: # pip install pillow 21 22 # from PIL import Image 23 # img = Image.new("RGB", (270, 40),color=get_random_color()) 24 # 25 # with open("validCode.png", "wb") as f: 26 # img.save(f,"png") 27 # 28 # with open("validCode.png", "rb") as f: 29 # data = f.read() 30 31 # 方式3: 32 33 # from PIL import Image 34 # from io import BytesIO 35 # 36 # img = Image.new("RGB", (270, 40), color=get_random_color()) 37 # f = BytesIO() 38 # img.save(f, "png") 39 # data=f.getvalue() 40 41 # 方式4: 42 43 img = Image.new("RGB", (270, 40), color=get_random_color()) 44 45 draw = ImageDraw.Draw(img) 46 47 kumo_font = ImageFont.truetype("static/font/KumoFont.ttf", size=20) 48 49 valid_code_str = "" 50 for i in range(5): 51 random_num = str(random.randint(0, 9)) 52 random_low_alpha = chr(random.randint(95, 122)) 53 random_upper_alpha = chr(random.randint(65, 90)) 54 random_char = random.choice([random_num, random_low_alpha, random_upper_alpha]) 55 draw.text((20 + i * 50, 15), random_char, get_random_color(), font=kumo_font) 56 57 # 保存验证码字符串 58 valid_code_str += random_char 59 60 # width = 270 61 # height = 40 62 # for i in range(5): 63 # x1 = random.randint(0, width) 64 # x2 = random.randint(0, width) 65 # y1 = random.randint(0, height) 66 # y2 = random.randint(0, height) 67 # draw.line((x1, y1, x2, y2), fill=get_random_color()) 68 # 69 # for i in range(10): 70 # draw.point([random.randint(0, width), random.randint(0, height)], fill=get_random_color()) 71 # x = random.randint(0, width) 72 # y = random.randint(0, height) 73 # draw.arc((x, y, x+4, y+4), 0, 90, fill=get_random_color()) 74 75 print("valid_code_str", valid_code_str) 76 request.session["valid_code_str"] = valid_code_str 77 78 f = BytesIO() 79 img.save(f, "png") 80 data = f.getvalue() 81 82 return data
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>登录页面</title> 6 <link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.css"> 7 </head> 8 <body> 9 <h3>登录页面</h3> 10 <div class="container"> 11 <div class="row"> 12 <div class="col-md-push-6 col-lg-offset-3"> 13 <form> 14 {% csrf_token %} 15 <div class="form-group"> 16 <label for="user">用户名</label> 17 <input type="text" id="user" class="form-control"> 18 </div> 19 <div class="form-group"> 20 <label for="pwd">密码</label> 21 <input type="password" id="pwd" class="form-control"> 22 </div> 23 24 <div class="form-group"> 25 <label for="pwd">验证码</label> 26 <div class="row"> 27 <div class="col-md-6"> 28 <input type="text" class="form-control" id="valid_code"> 29 </div> 30 <div class="col-md-6"> 31 <img width="270" height="36" id="valid_code_img" src="/get_validCode_img/" alt=""> 32 </div> 33 </div> 34 </div> 35 <input type="button" class="btn btn-default login_btn" value="登录"><span class="error"></span> 36 <a href="/register/" class="btn btn-success pull-right">注册</a> 37 </form> 38 </div> 39 </div> 40 </div> 41 <script src="/static/jquery-3.3.1.js"></script> 42 <script> 43 // 刷新验证码 44 $("#valid_code_img").click(function () { 45 $(this)[0].src += "?" 46 47 }); 48 49 // 登录验证 50 $(".login_btn").click(function () { 51 52 $.ajax({ 53 url: "", 54 type: "post", 55 data: { 56 user: $("#user").val(), 57 pwd: $("#pwd").val(), 58 valid_code: $("#valid_code").val(), 59 csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val(), 60 }, 61 success: function (data) { 62 console.log(data); 63 64 if (data.user){ 65 if (location.search){ 66 location.href = location.search.slice(6) 67 } 68 else { 69 location.href = "/index/" 70 } 71 } 72 else { 73 $(".error").text(data.msg).css({"color": "red", "margin-left": "10px"}); 74 setTimeout(function () { 75 $(".error").text(""); 76 77 }, 1000) 78 } 79 80 } 81 }) 82 83 }) 84 </script> 85 86 </body> 87 </html>
三、form表单、Ajax文件上传
基于form表单提交数据:
[27/Sep/2018 12:27:42] "GET /upload/ HTTP/1.1" 200 487
<QueryDict: {'csrfmiddlewaretoken': ['0XhktJJdewNa5wfoRonIbGzlltmBMCASk8wG0IVb7EyR0XBQRcksDfEde3OEkrvB'], 'user': ['']}>
<MultiValueDict: {'avatar': [<InMemoryUploadedFile: 106.jpeg (image/jpeg)>]}>
[27/Sep/2018 12:29:03] "POST /upload/ HTTP/1.1" 200 2
基于Ajax提交数据:
<QueryDict: {'user': ['mike'], 'csrfmiddlewaretoken': ['jmCZMYoTXSx01aIFnQtmaBFRCz33vMlgDxRljXARQ0iHWB47nEq6CaKJv9v63BgZ']}>
<MultiValueDict: {'avatar': [<InMemoryUploadedFile: 100.jpeg (image/jpeg)>]}>
[28/Sep/2018 00:22:46] "POST /upload/ HTTP/1.1" 200 2
四、form表单的注册页面
图片预览:
头像默认图片
.avatar_img { 60px; height:60px; margin-left: 20px; } #avatar { display: none; }
<div class="form-group"> <label for="avatar"> 头像 <img class="avatar_img" src="../static/blog/img/default.png" alt=""> </label> <input type="file" id="avatar" name="avatar"> </div>
图像预览
// 头像预览 $("#avatar").change(function () { // 获取用户选中的文件对象 var file_obj = $(this)[0].files[0]; // 获取文件对象的路径 var reader = new FileReader(); reader.readAsDataURL(file_obj); // 修改img的src属性, src= 文件对象的路径 reader.onload = function () { $(".avatar_img").attr("src", reader.result) }; });
注意:
为什么 src="/static/img/default.png" 可访问到 ?
因为: settings 配置了 STATIC_URL = '/static/'
Ajax注册:
class UserForm(forms.Form): user = forms.CharField(max_length=32, error_messages={"required": "该字段不能为空!"}, label="用户名", widget=widgets.TextInput(attrs={"class": "form-control"},) ) pwd = forms.CharField(max_length=32, error_messages={"required": "该字段不能为空!"}, label="密码", widget=widgets.PasswordInput(attrs={"class": "form-control"},) ) re_pwd = forms.CharField(max_length=32, error_messages={"required": "该字段不能为空!"}, label="确认密码", widget=widgets.PasswordInput(attrs={"class": "form-control"},) ) email = forms.EmailField(max_length=32, error_messages={"required": "该字段不能为空!"}, label="邮箱", widget=widgets.EmailInput(attrs={"class": "form-control"},) ) # 局部钩子 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
def register(request): """ 注册视图函数: get请求响应注册页面 POst(Ajax)请求,校验字段,响应字典 :param request: :return: """ 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") print("user", user) pwd = form.cleaned_data.get("pwd") email = form.cleaned_data.get("email") avatar_obj = request.FILES.get("avater") extra = {} if avatar_obj: extra["avatar"] = avatar_obj UserInfo.objects.create_user(username=user, password=pwd, email=email, **extra) else: print(form.cleaned_data) print(form.errors) response["msg"] = form.errors return JsonResponse(response) form = UserForm() return render(request, "register.html", {"form": form}) return render(request, "register.html")
// 基于ajax提交数据 $(".reg_btn").click(function () { // console.log($("#form").serializeArrray()); 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) { 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"); } $("#id_" + field).next().html(error_list[0]); $("#id_" + field).parent().addClass("has-error"); }) } } }) })
注意:
1、局部钩子 与 全局钩子
def clean_user(self): pass 局部钩子只能校验某一个字段 def clean(self): 可以校验两个不同的字段,全局钩子,能得到任何一个干净的数据
局部钩子得源码: try: if isinstance(field, FileField): initial = self.get_initial_for_field(field, name) value = field.clean(value, initial) else: value = field.clean(value) self.cleaned_data[name] = value if hasattr(self, 'clean_%s' % name): value = getattr(self, 'clean_%s' % name)() self.cleaned_data[name] = value except ValidationError as e: self.add_error(name, e)
全局钩子得源码: try: cleaned_data = self.clean() except ValidationError as e: self.add_error(None, e) else: if cleaned_data is not None: self.cleaned_data = cleaned_data
2、上传文件
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) {
3、Ajax的局部刷新:
注册失败:
1、清空错误信息 $("span.error").html(""); $(".form-group").removeClass("has-error"); 2、加载错误信息 // 展示本次提交的错误信息 $.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"); } $("#id_" + field).next().html(error_list[0]); $("#id_" + field).parent().addClass("has-error"); })
4、上传文件的存放地址:media配置
前端: formdata.append("avatar", $("#avatar")[0].files[0]); // 文件对象 后台: extra = {} if avatar_obj: extra["avatar"] = avatar_obj UserInfo.objects.create_user(username=user, password=pwd, email=email, **extra)
知识点:
静态文件/static/css..js..img.. 用户可直接URL访问得到
因为:settings配置STATIC_URL = '/static/'
用户文件:
settings里面配置:
# 与用户上传相关的配置
MEDIA_ROOT=os.path.join(BASE_DIR,"media")
MEDIA_URL="/media/"
路由配置:urls.py
# media配置: re_path(r"media/(?P<path>.*)$", serve, {"document_root": settings.MEDIA_ROOT})
media配置之后:
用户可以直接访问
数据库的文件保存还是相对路径:
示例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>注册页面</title> <link rel="stylesheet" href="../static/bootstrap-3.3.7/css/bootstrap.css"> <script src="../static/jquery-3.3.1.js"></script> <style> .avatar_img { width:60px; height:60px; margin-left: 20px; } #avatar { display: none; } .error { color: red; } </style> </head> <body> <h3>注册页面</h3> <div class="container"> <div class="row"> <div class="col-md-6 col-lg-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 class="avatar_img" src="/static/blog/img/default.png" alt=""> </label> <input type="file" id="avatar" name="avatar"> </div> <input type="button" class="btn btn-default reg_btn" value="提交"> <span class="error"></span> </form> </div> </div> </div> <script> // 头像预览 $("#avatar").change(function () { // 获取用户选中的文件对象 var file_obj = $(this)[0].files[0]; // 获取文件对象的路径 var reader = new FileReader(); reader.readAsDataURL(file_obj); // 修改img的src属性, src= 文件对象的路径 reader.onload = function () { $(".avatar_img").attr("src", reader.result) }; }); // 基于ajax提交数据 $(".reg_btn").click(function () { // console.log($("#form").serializeArrray()); 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) { 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"); } $("#id_" + field).next().html(error_list[0]); $("#id_" + field).parent().addClass("has-error"); }) } } }) }) </script> </body> </html>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 def register(request): 2 """ 3 注册视图函数: 4 get请求响应注册页面 5 POst(Ajax)请求,校验字段,响应字典 6 :param request: 7 :return: 8 """ 9 if request.is_ajax(): 10 print(request.POST) 11 form = UserForm(request.POST) 12 13 response = {"user": None, "msg": None} 14 if form.is_valid(): 15 response["user"] = form.cleaned_data.get("user") 16 17 # 生成一条用户记录 18 user = form.cleaned_data.get("user") 19 print("user", user) 20 pwd = form.cleaned_data.get("pwd") 21 email = form.cleaned_data.get("email") 22 avatar_obj = request.FILES.get("avatar") 23 24 extra = {} 25 if avatar_obj: 26 extra["avatar"] = avatar_obj 27 UserInfo.objects.create_user(username=user, password=pwd, email=email, **extra) 28 29 else: 30 print(form.cleaned_data) 31 print(form.errors) 32 response["msg"] = form.errors 33 34 return JsonResponse(response) 35 36 form = UserForm() 37 return render(request, "register.html", {"form": form}) 38 39 40 return render(request, "register.html")