BBS - 表、登录、文件上传、注册
一、博客系统得表关系
models.py
from django.db import models from django.contrib.auth.models import 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 = models.CharField(verbose_name='个人博客后缀', max_length=32, unique=True) 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='创建时间') category = models.ForeignKey(to='Category', to_field='nid', null=True, on_delete=models.CASCADE) user = models.ForeignKey(verbose_name='作者', to='UserInfo', to_field='nid', on_delete=models.CASCADE) tags = models.ManyToManyField( to='Tag', through='Article2Tag', through_fields=('article','tag'), ) def __str__(self): return self.title class ArticleDetail(models.Model): """ 文章详细表 """ nid = models.AutoField(primary_key=True) content = models.TextField() article = models.OneToOneField(to='Article', to_field='nid', on_delete=models.CASCADE) 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) article = models.ForeignKey(verbose_name='评论文章', to='Article', to_field='nid', on_delete=models.CASCADE) user = models.ForeignKey(verbose_name='评论者', to='UserInfo', to_field='nid', on_delete=models.CASCADE) content = models.CharField(verbose_name='评论内容', max_length=255) create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) parent_comment = models.ForeignKey('self', null=True, on_delete=models.CASCADE) def __str__(self): return self.content
操作配置:
AUTH_USER_MODEL = "blog.UserInfo" create database cnblog; DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'cnblog', 'USER': 'root', 'PASSWORD': '123', 'HOST': '127.0.0.1', 'PORT': 3306, } } import pymysql pymysql.install_as_MySQLdb() Tools/Run manage.py Task makemigrations migrate
。。。
结果:
二、基于Ajax和用户登录验证
验证码得获取:
方式一:
<img width="260" height="35" src="/get_valid_img/" alt="">
def get_valid_img(request): # 方式一 with open('girl.jpg','rb') as f: data = f.read() return HttpResponse(data)
方式二:
def get_valid_img(request): # 方式二 """ pip install pillow """ import PIL from PIL import Image import random def get_random_color(): return (random.randint(0,255),random.randint(0,255),random.randint(0,255)) image = Image.new('RGB',(260,35),get_random_color()) f = open('valid_code.png','wb') # 存在磁盘上 image.save(f,'png') f = open('valid_code.png','rb') data = f.read() f.close() return HttpResponse(data)
方式三:
def get_valid_img(request): # 方式三 import PIL from PIL import Image import random def get_random_color(): return (random.randint(0,255),random.randint(0,255),random.randint(0,255)) image = Image.new('RGB',(260,35),get_random_color()) from io import BytesIO f = BytesIO() # 在内存中创建一张图片 直接返回 image.save(f,'png') data = f.getvalue() return HttpResponse(data)
方式四:
def get_valid_img(request): # 方式四 import PIL from PIL import Image,ImageDraw,ImageFont import random def get_random_color(): return (random.randint(0,255),random.randint(0,255),random.randint(0,255)) image = Image.new('RGB',(260,35),get_random_color()) draw = ImageDraw.Draw(image) font = ImageFont.truetype('app01/static/font/kumo.ttf',size=32) temp = [] # 生成5个随机字符 for i in range(5): random_num = str(random.randint(0,9)) random_low_alpha = chr(random.randint(97,122)) random_upper_alpha = chr(random.randint(65,90)) random_char = random.choice([random_num,random_low_alpha,random_upper_alpha]) draw.text((24+i*36,0),random_char,get_random_color(),font=font) # 保存随机数 temp.append(random_char) # 造点 造线 造圆 width = 210 height = 35 for i in range(2): 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(2): 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+10,y+5),0,360,fill=get_random_color()) # 在内存中生成图片 from io import BytesIO f = BytesIO() image.save(f,'png') data = f.getvalue() f.close() valid_str = ''.join(temp) print('valid_str:',valid_str) request.session['valid_str'] = valid_str return HttpResponse(data)
登录验证:
<body> <h3>hello: {{ request.user.username }} , {{ request.user.email }}</h3> </body>
<input type="button" class="btn btn-default login-btn" value="提交"> <span class="error" style="color: red; margin-left: 10px;"></span>
<script src="/static/js/jquery-3.2.1.min.js"></script> <script type="text/javascript"> $('.login-btn').click(function () { $.ajax({ url:'', type:'post', data:{ csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val(), user:$('#user').val(), pwd:$('#pwd').val(), valid_code:$('#valid_code').val() }, success:function (data) { if(data.state){ location.href = '/index/' }else{ $('.error').text(data.msg) } } }) })
from django.shortcuts import render,HttpResponse,redirect from django.http import JsonResponse from django.contrib import auth def login(request): if request.is_ajax(): user = request.POST.get('user') pwd = request.POST.get('pwd') valid_code = request.POST.get('valid_code') valid_str = request.session.get('valid_str') res = {"state": False, "msg": None} if valid_code.upper() == valid_str.upper(): user = auth.authenticate(username = user,password = pwd) if user: res["state"] = True auth.login(request, user) else: res["msg"] = "username or pwd error" else: res["msg"] = "验证码错误" return JsonResponse(res) return render(request,'login.html') def index(request): if not request.user.username: return redirect('/login/') return render(request,'index.html')
验证码点击刷新:
<img id="valid_img" width="260" height="35" src="/get_valid_img/" alt="">
//验证码点击刷新 $('#valid_img').click(function () { $(this)[0].src += '?' })
示例:
from django.db import models from django.contrib.auth.models import 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 = models.CharField(verbose_name='个人博客后缀', max_length=32, unique=True) 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='创建时间') category = models.ForeignKey(to='Category', to_field='nid', null=True, on_delete=models.CASCADE) user = models.ForeignKey(verbose_name='作者', to='UserInfo', to_field='nid', on_delete=models.CASCADE) tags = models.ManyToManyField( to='Tag', through='Article2Tag', through_fields=('article','tag'), ) def __str__(self): return self.title class ArticleDetail(models.Model): """ 文章详细表 """ nid = models.AutoField(primary_key=True) content = models.TextField() article = models.OneToOneField(to='Article', to_field='nid', on_delete=models.CASCADE) 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) article = models.ForeignKey(verbose_name='评论文章', to='Article', to_field='nid', on_delete=models.CASCADE) user = models.ForeignKey(verbose_name='评论者', to='UserInfo', to_field='nid', on_delete=models.CASCADE) content = models.CharField(verbose_name='评论内容', max_length=255) create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) parent_comment = models.ForeignKey('self', null=True, on_delete=models.CASCADE) def __str__(self): return self.content
from django.shortcuts import render,HttpResponse,redirect from django.http import JsonResponse from django.contrib import auth def login(request): if request.is_ajax(): user = request.POST.get('user') pwd = request.POST.get('pwd') valid_code = request.POST.get('valid_code') valid_str = request.session.get('valid_str') res = {"state": False, "msg": None} if valid_code.upper() == valid_str.upper(): user = auth.authenticate(username = user,password = pwd) if user: res["state"] = True auth.login(request, user) else: res["msg"] = "username or pwd error" else: res["msg"] = "验证码错误" return JsonResponse(res) return render(request,'login.html') def index(request): if not request.user.username: return redirect('/login/') return render(request,'index.html') def get_valid_img(request): # 方式四 import PIL from PIL import Image,ImageDraw,ImageFont import random def get_random_color(): return (random.randint(0,255),random.randint(0,255),random.randint(0,255)) image = Image.new('RGB',(260,35),get_random_color()) draw = ImageDraw.Draw(image) font = ImageFont.truetype('app01/static/font/kumo.ttf',size=32) temp = [] # 生成5个随机字符 for i in range(5): random_num = str(random.randint(0,9)) random_low_alpha = chr(random.randint(97,122)) random_upper_alpha = chr(random.randint(65,90)) random_char = random.choice([random_num,random_low_alpha,random_upper_alpha]) draw.text((24+i*36,0),random_char,get_random_color(),font=font) # 保存随机数 temp.append(random_char) # 造点 造线 造圆 width = 210 height = 35 for i in range(2): 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(2): 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+10,y+5),0,360,fill=get_random_color()) # 在内存中生成图片 from io import BytesIO f = BytesIO() image.save(f,'png') data = f.getvalue() f.close() valid_str = ''.join(temp) print('valid_str:',valid_str) request.session['valid_str'] = valid_str return HttpResponse(data) # 方式三 # import PIL # from PIL import Image # # import random # # def get_random_color(): # return (random.randint(0,255),random.randint(0,255),random.randint(0,255)) # # image = Image.new('RGB',(260,35),get_random_color()) # # from io import BytesIO # f = BytesIO() # 在内存中创建一张图片 直接返回 # image.save(f,'png') # # data = f.getvalue() # # return HttpResponse(data) # 方式二 # """ # pip install pillow # """ # import PIL # from PIL import Image # # import random # # def get_random_color(): # return (random.randint(0,255),random.randint(0,255),random.randint(0,255)) # # image = Image.new('RGB',(260,35),get_random_color()) # # f = open('valid_code.png','wb') # 存在磁盘上 # image.save(f,'png') # # f = open('valid_code.png','rb') # data = f.read() # f.close() # # return HttpResponse(data) # 方式一 # with open('girl.jpg','rb') as f: # data = f.read() # # return HttpResponse(data)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>index</title> </head> <body> <h3>hello: {{ request.user.username }} , {{ request.user.email }}</h3> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>login</title> <link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.css"> <style type="text/css"> .container{ margin-top: 100px;} </style> </head> <body> <div class="container"> <div class="row"> <div class="col-md-6 col-md-offset-3"> <form> {% csrf_token %} <div class="form-group"> <label for="user">用户名</label> <input type="text" class="form-control" id="user" placeholder="Username"> </div> <div class="form-group"> <label for="pwd">密码</label> <input type="password" class="form-control" id="pwd" placeholder="Password"> </div> <div class="form-group"> <label for="pwd">验证码</label> <div class="row"> <div class="col-md-6"> <input type="text" class="form-control" id="valid_code"> </div> <div class="col-md-6"> <img id="valid_img" width="260" height="35" src="/get_valid_img/" alt=""> </div> </div> </div> <input type="button" class="btn btn-default login-btn" value="提交"> <span class="error" style="color: red; margin-left: 10px;"></span> </form> </div> </div> </div> <script src="/static/js/jquery-3.2.1.min.js"></script> <script type="text/javascript"> //提交 $('.login-btn').click(function () { $.ajax({ url:'', type:'post', data:{ csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val(), user:$('#user').val(), pwd:$('#pwd').val(), valid_code:$('#valid_code').val() }, success:function (data) { if(data.state){ location.href = '/index/' }else{ $('.error').text(data.msg) } } }) }); //验证码点击刷新 $('#valid_img').click(function () { $(this)[0].src += '?' }) </script> </body> </html>
注:
css js 静态文件
css js 存在静态文件中 修改之后,client(浏览器)如果没有修改;
需设置一些 浏览器得setting Disable cache
三、form表单、ajax文件上传
django-ajax http://www.cnblogs.com/yuanchenqi/articles/7638956.html
基于form表单提交数据:
默认值:Content—Type = urlencoded
<form action="" method="post" enctype="application/x-www-form-urlencoded">
print(request.POST)
<QueryDict: {'csrfmiddlewaretoken': ['azZWQlmRhdPPXxtRf9e04B31VZLyVJN8Dc1eAuWEMIxnWShqNP5IyxbozXVRjx8A'],
'user': ['alex'],
'avatar': ['girl.jpg']}> # 其实文件并没有传上来,只是个名称!
print(request.FILES)
<MultiValueDict: {}>
涉及到上传文件时:Content—Type = form-data
<form action="" method="post" enctype="multipart/form-data">
print(request.POST)
<QueryDict: {'csrfmiddlewaretoken': ['gsAIE42u5Io47YttQO9ExBEFuZiVZ965J5C0odChAd6C6jh2ou0m1xM28XsenXrx'],
'user': ['alex']}>
print(request.FILES)
<MultiValueDict: {'avatar': [<InMemoryUploadedFile: girl.jpg (image/jpeg)>]}>
基于Ajax提交数据:
$('#btn').click(function () {
$.ajax({
url:'',
type:'get',
data:{name:'alex',pwd:'123'},
success:function (data) {
console.log(data)
}
})
})
涉及到上传文件时:
<script src="http://cdn.static.runoob.com/libs/jquery/1.10.2/jquery.min.js"></script>
<script type="text/javascript">
$('#btn').click(function () {
formdate = new FormData(); // 加var 局部, 不加var就是 全局.
formdate.append("user",$('#user').val());
formdate.append("avatar",$('#avatar')[0].files[0]);
formdate.append("csrfmiddlewaretoken",$('input[name="csrfmiddlewaretoken"]').val());
$.ajax({
url:'',
type:'post',
data:formdate,
contentType:false,
processData:false,
success:function (data) {
console.log(data)
}
})
})
</script>
<QueryDict: {'user': ['alice'], 'csrfmiddlewaretoken': ['9ipnZT2GROgz1BFiCZzsYTE32MCBPXKWCVrFJ2CtmjY70WtRaFqasPMqGKMUdL5o']}>
<MultiValueDict: {'avatar': [<InMemoryUploadedFile: girl.jpg (image/jpeg)>]}>
def upload(request): if request.method == 'POST': print(request.POST) print(request.FILES) obj = request.FILES.get('avatar') with open(obj.name,'wb') as f: for line in obj: f.write(line) return HttpResponse('OK') return render(request,'upload.html')
upload.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h3>基于form表单提交数据</h3> {#<form action="" method="post" enctype="application/x-www-form-urlencoded">#} <form action="" method="post" enctype="multipart/form-data"> {% csrf_token %} <p>用户名 <input type="text" name="user"></p> <p>头像 <input type="file" name="avatar"></p> <input type="submit"> </form> <hr> <h3>基于Ajax提交文件数据</h3> <form> {% csrf_token %} <p>用户名 <input type="text" id="user"></p> <p>头像 <input type="file" id="avatar"></p> <input type="button" value="submit" id="btn"> </form> <script src="http://cdn.static.runoob.com/libs/jquery/1.10.2/jquery.min.js"></script> <script type="text/javascript"> $('#btn').click(function () { formdate = new FormData(); // 加var 局部, 不加var就是 全局. formdate.append("user",$('#user').val()); formdate.append("avatar",$('#avatar')[0].files[0]); formdate.append("csrfmiddlewaretoken",$('input[name="csrfmiddlewaretoken"]').val()); $.ajax({ url:'', type:'post', data:formdate, contentType:false, processData:false, success:function (data) { console.log(data) } }) }) </script> </body> </html>
四、form表单得注册页面
头像默认图片
#avatar{ display: none} .avatar{ width: 60px; height: 60px;margin-left: 15px;cursor: pointer;}
<div class="form-group"> <label for="avatar">头像 <img class="avatar" src="/static/img/default.png" alt=""> </label> <input type="file" id="avatar"> </div>
图像预览
//图像预览 $('#avatar').change(function () { var choose_file = $(this)[0].files[0]; //文件对象 var reader = new FileReader(); // 阅读器对象 reader.readAsDataURL(choose_file); reader.onload = function (ev) { $('.avatar').attr('src',reader.result) }; {#$('.avatar').attr('src',reader.result) // 还没读完,就执行了这句了 出问题了!!#} //解决办法 reader.onload });
注意:
为什么 src="/static/img/default.png" 可访问到 ?
因为: settings 配置了 STATIC_URL = '/static/'
STATIC_URL = '/static/'
/static/... 对外得文件可以直接访问
Ajax 注册:
from django import forms from django.forms import widgets,ValidationError from app01.models import UserInfo class RegForm(forms.Form): user = forms.CharField(max_length=8,label='用户名', widget=widgets.TextInput(attrs={'class':'form-control'})) pwd = forms.CharField(min_length=4,label='密码', widget=widgets.PasswordInput(attrs={'class':'form-control'})) repeat_pwd = forms.CharField(min_length=4,label='确认密码', widget=widgets.PasswordInput(attrs={'class': 'form-control'})) email = forms.EmailField(label='邮箱', widget=widgets.EmailInput(attrs={'class': 'form-control'})) # 局部钩子 def clean_user(self): val = self.cleaned_data.get('user') ret = UserInfo.objects.filter(username = val) if not ret: return val else: raise ValidationError('该用户已存在') # 校验局部钩子得时候 没有办法 拿到 所有得干净数据, # 如何 校验 两个 字段呢! 全局钩子 # 全局钩子 能得到 任何一个干净得 数据 def clean(self):return self.cleaned_data def clean(self): if self.cleaned_data.get('pwd') == self.cleaned_data.get('repeat_pwd'): return self.cleaned_data else: raise ValidationError('两次密码不一致') from django.http import JsonResponse def reg(request): if request.method == 'POST': res = {'user':None,'error_dict':None} form = RegForm(request.POST) if form.is_valid(): print("clean:",form.cleaned_data) # {"user":'yuan','pwd':"123"} print(request.FILES) user = form.cleaned_data.get('user') pwd = form.cleaned_data.get('pwd') email = form.cleaned_data.get('email') avatar = request.FILES.get('avatar') if avatar: # 接受文件对象 将avatar文件对象下载到avatar字段对应的upload_to指定路径 没指定在 根目录下 user = UserInfo.objects.create_user(username=user,password=pwd,email=email,avatar=avatar) else: user = UserInfo.objects.create_user(username=user, password=pwd, email=email) res['user'] = user.username else: print('error:',form.errors) #{"repear_pwd":['',''],"email":['',''],} res['error_dict'] = form.errors return JsonResponse(res) form = RegForm() return render(request,'reg.html',locals())
// 注册事件 $('.reg_btn').click(function () { formdata = new FormData(); formdata.append("user",$('#id_user').val()); formdata.append("pwd",$('#id_pwd').val()); formdata.append("repeat_pwd",$('#id_repeat_pwd').val()); formdata.append("email",$('#id_email').val()); formdata.append("avatar",$('#avatar')[0].files[0]); formdata.append("csrfmiddlewaretoken",$('input[name="csrfmiddlewaretoken"]').val()); $.ajax({ url:'', type:'post', contentType:false, processData:false, data:formdata, success:function (data) { if(data.user){ //注册成功 location.href = "/login/" }else{ //注册失败 //清空错误信息 $('form span').html(""); $('form .form-group').removeClass('has-error'); // 加载错误信息 $.each(data.error_dict,function (field,error_list) { // 全局 if(field == "__all__"){ $('#id_repeat_pwd').next().text(error_list[0]).css('color','red'); $('#id_repeat_pwd').parent().addClass('has-error') } $('#id_'+field).next().text(error_list[0]).css('color','red'); $('#id_'+field).parent().addClass('has-error') }) } } }) })
注意点:
1. 局部钩子 与 全局钩子:
def clean_user(self): pass 局部钩子 只能校验某一个字段
def clean(self): pass 可以校验两个不同得字段,全局钩子,能得到任何一个干净得数据!!
局部钩子得源码:
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. 上传文件:
formdata = new FormData();
formdata.append("avatar",$('#avatar')[0].files[0]); # 文件对象 formdata.append("csrfmiddlewaretoken",$('input[name="csrfmiddlewaretoken"]').val());contentType:false, processData:false, data:formdata,
。。。
3. ajax得局部刷新:
注册失败:
1. 清空错误信息
$('form span').html("");
$('form .form-group').removeClass('has-error');
2. 加载错误信息 each$.each(data.error_dict,function (field,error_list) { // 全局 全局得错误信息 field == '__all__' if(field == "__all__"){ $('#id_repeat_pwd').next().text(error_list[0]).css('color','red'); $('#id_repeat_pwd').parent().addClass('has-error') } $('#id_'+field).next().text(error_list[0]).css('color','red'); $('#id_'+field).parent().addClass('has-error') })
4.上传文件 得存放地址:media 配置
avatar = models.FileField(upload_to='avatars/', default='avatars/default.png')FileField ImgField 传什么数据呢? 文件对象
前端:
formdata.append("avatar",$('#avatar')[0].files[0]); # 文件对象
后台:
avatar = request.FILES.get('avatar') # 文件对象
if avatar:
# 接收文件对象 将avatar文件对象下载到avatar字段对应的upload_to(upload_to='avatars/')指定路径下
user = UserInfo.objects.create_user(username=user,password=pwd,email=email,avatar=avatar)
else:
user = UserInfo.objects.create_user(username=user, password=pwd, email=email)
真实文件:
upload_to='' 存在项目得根目录下 girl.jpg
upload_to='avatars/' 存在项目得根目录下 /avatars/girl.jpg 注意: 相对路径 不是绝对路径,如果upload_to='/avatars/'会报错!!
userinfo:
库里存得是 相对路径!! avatars/girl.jpg
BUT:
用户个人得文件 不应该存在项目得根目录下: ??
知识点:
静态文件 /static/css..js..img.. 用户可直接 url 访问得到
因为:settings 配置 STATIC_URL = '/static/'
用户文件:存用户得个人信息 头像 简历。。。 存在哪里呢???
media 配置 存用户信息 针对用户上传得文件
avatar = models.FileField(upload_to='avatars/', default='avatars/default.png')
settings
1.MEDIA_ROOT = os.path.join(BASE_DIR,'app01','media') # 针对 upload_to 得相对位置得!!
2.MEDIA_URL = '/media/' # 针对 用户通过 url 访问得!!
3.urls.py
from django.views.static import serve
from bbs import settings
re_path(r'media/(?P<path>.*)$',serve,{'document_root':settings.MEDIA_ROOT}) # 针对 用户通过 url 访问得!!
media 配置之后:
用户可直接访问类似于访问/static/img/...
upload_to='avatars/' 用户上传得文件就会存在 /media/avatars/girl.jpg 库里存得是,相对路径! avatars/girl.jpg
注意:
# avatar = models.FileField(upload_to='avatars/', default='avatars/default.png')
# 如果用户没有上传图片,使用默认得!!avatar 不需要传!!
avatar = request.FILES.get('avatar')
if avatar:
user = UserInfo.objects.create_user(username=user,password=pwd,email=email,avatar=avatar)
else:
user = UserInfo.objects.create_user(username=user, password=pwd, email=email)
示例:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>reg</title> <link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.css"> <style type="text/css"> .container{ margin-top: 100px;} #avatar{ display: none} .avatar{ width: 60px; height: 60px;margin-left: 15px;cursor: pointer;} </style> </head> <body> <div class="container"> <div class="row"> <div class="col-md-6 col-md-offset-3"> <form action=""> {% for field in form %} <div class="form-group"> <label for="{{ field.id_for_label }}">{{ field.label }}</label> {{ field }} <span class="error pull-right"></span> </div> {% endfor %} <div class="form-group"> <label for="avatar">头像 <img class="avatar" src="/static/img/default.png" alt=""> </label> <input type="file" id="avatar"> </div> <input type="button" class="btn btn-default login-btn reg_btn pull-right" value="提交"> </form> </div> </div> </div> {% csrf_token %} <script src="/static/js/jquery-3.2.1.min.js"></script> <script type="text/javascript"> //图像预览 $('#avatar').change(function () { var choose_file = $(this)[0].files[0]; //文件对象 var reader = new FileReader(); // 阅读器对象 reader.readAsDataURL(choose_file); reader.onload = function (ev) { $('.avatar').attr('src',reader.result) }; {#$('.avatar').attr('src',reader.result) // 还没读完,就执行了这句了 出问题了!!#} //解决办法 reader.onload }); // 注册事件 $('.reg_btn').click(function () { formdata = new FormData(); formdata.append("user",$('#id_user').val()); formdata.append("pwd",$('#id_pwd').val()); formdata.append("repeat_pwd",$('#id_repeat_pwd').val()); formdata.append("email",$('#id_email').val()); formdata.append("avatar",$('#avatar')[0].files[0]); formdata.append("csrfmiddlewaretoken",$('input[name="csrfmiddlewaretoken"]').val()); $.ajax({ url:'', type:'post', contentType:false, processData:false, data:formdata, success:function (data) { if(data.user){ //注册成功 location.href = "/login/" }else{ //注册失败 //清空错误信息 $('form span').html(""); $('form .form-group').removeClass('has-error'); // 加载错误信息 $.each(data.error_dict,function (field,error_list) { // 全局 if(field == "__all__"){ $('#id_repeat_pwd').next().text(error_list[0]).css('color','red'); $('#id_repeat_pwd').parent().addClass('has-error') } $('#id_'+field).next().text(error_list[0]).css('color','red'); $('#id_'+field).parent().addClass('has-error') }) } } }) }) </script> </body> </html>
from django import forms from django.forms import widgets,ValidationError from app01.models import UserInfo class RegForm(forms.Form): user = forms.CharField(max_length=8,label='用户名', widget=widgets.TextInput(attrs={'class':'form-control'})) pwd = forms.CharField(min_length=4,label='密码', widget=widgets.PasswordInput(attrs={'class':'form-control'})) repeat_pwd = forms.CharField(min_length=4,label='确认密码', widget=widgets.PasswordInput(attrs={'class': 'form-control'})) email = forms.EmailField(label='邮箱', widget=widgets.EmailInput(attrs={'class': 'form-control'})) # 局部钩子 def clean_user(self): val = self.cleaned_data.get('user') ret = UserInfo.objects.filter(username = val) if not ret: return val else: raise ValidationError('该用户已存在') # 校验局部钩子得时候 没有办法 拿到 所有得干净数据, # 如何 校验 两个 字段呢! 全局钩子 # 全局钩子 能得到 任何一个干净得 数据 def clean(self):return self.cleaned_data def clean(self): if self.cleaned_data.get('pwd') == self.cleaned_data.get('repeat_pwd'): return self.cleaned_data else: raise ValidationError('两次密码不一致') from django.http import JsonResponse def reg(request): if request.method == 'POST': res = {'user':None,'error_dict':None} form = RegForm(request.POST) if form.is_valid(): print("clean:",form.cleaned_data) # {"user":'yuan','pwd':"123"} print(request.FILES) user = form.cleaned_data.get('user') pwd = form.cleaned_data.get('pwd') email = form.cleaned_data.get('email') avatar = request.FILES.get('avatar') if avatar: # 接受文件对象 将avatar文件对象下载到avatar字段对应的upload_to指定路径 没指定在 根目录下 user = UserInfo.objects.create_user(username=user,password=pwd,email=email,avatar=avatar) else: user = UserInfo.objects.create_user(username=user, password=pwd, email=email) res['user'] = user.username else: print('error:',form.errors) #{"repear_pwd":['',''],"email":['',''],} res['error_dict'] = form.errors return JsonResponse(res) form = RegForm() return render(request,'reg.html',locals())
知识点1: jquery对象[0] jquery对象------------------>DOM <------------------ $(DOM) 知识点2: 请求形式: form get form post ajax get ajax post a标签 get 地址栏 get 注意: form只有 get post ajax 有 get post put delete ... 查看 get 修改 post (增 post 删 delete 更新 put) 知识点3: url_encoded http请求协议:{"user":"alex","pwd":123}-------------->"user=alex&pwd=123" form-data {"user":"alex","avatar":obj}-----------> "GET HTTP1.1 /upload/\r\n content—type='url_encoded'\r\n\r\nuser=alex&pwd=123" 请求头: Content—Type:告诉服务器这次请求数据的格式,默认值:url_encoded if 上传文件:Content—Type=form-data 知识点4: form对象渲染样式: #方式1: {{form.as_p}} #方式2: {{form.user}} #方式3: {% for field in form %} <div> <label for="">{{ field.label }}</label> <div> {{ field }} </div> </div> {% endfor %} 博客系统的登录 ---- 基于PIL模块实现的验证码图片 ---- 基于session保存验证码 ---- 刷新验证码 博客系统的注册 基于ajax和form组件实现注册 ---设计注册页面 self.fields:{"字段":字段规则} form.is_valid(): self.errors={} self.clean_data={} UserInfo.objects.create_user(username=user,password=pwd,email=email,avatar=avatar) 将avatar文件对象下载到avatar字段对应的upload_to指定路径 (1) media配置: 针对: avatar = models.FileField(upload_to='avatars/', default="/avatars/default.png") avatar = models.ImageField(upload_to='avatars/', default="/avatars/default.png") MEDIA_ROOT=os.path.join(BASE_DIR,"blog","media") (2) MEDIA_URL="/media/" # media 配置 url(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}),