|
# 数据库表创建及同步
由于django自带的sqlite数据库对日期不敏感,所有我们换成MySQL
# 建表
from django.contrib.auth.models import AbstractUser
class UserInfo(AbstractUser):
phone=models.BigIntegerField(verbose_name='手机号',null=True)
# 头像
avatar = models.FileField(upload_to='avatar/',default='avatar/default.png',verbose_name='用户头像')
"""
给avatar字段传文件对象 该文件会自动存储到avatar文件下 然后avatar字段只保存文件路径avatar/default.png
"""
create_time = models.DateField(auto_now_add=True)
# 与站点是一对一
blog = models.OneToOneField(to='Blog',null=True)
class Blog(models.Model):
state_name = models.CharField(max_length=32,verbose_name='站点名称')
state_title = models.CharField(max_length=32,verbose_name='站点标题')
state_theme = models.CharField(max_length=64,verbose_name='站点样式') # 存css和js的文件路径
class Category(models.Model):
name = models.CharField(verbose_name='文章发类',max_length=32)
blog= models.ForeignKey(to='Blog',null=True)
class Tag(models.Model):
name = models.CharField(verbose_name='文章标签',max_length=32)
blog= models.ForeignKey(to='Blog',null=True)
class Article(models.Model):
title = models.CharField(verbose_name='文章标题',max_length=64)
desc = models.CharField(verbose_name='文章简介',max_length=255)
# 文章内容有很多 一般情况下用text
content = models.TextField(verbose_name='文章内容')
create_time = models.DateField(auto_now_add=True)
# 数据库字段设计优化
up_num = models.BigIntegerField(default=0,verbose_name='点赞数')
down_num = models.BigIntegerField(default=0,verbose_name='点踩数')
comment_num = models.BigIntegerField(default=0,verbose_name='评论数')
# 外键字段
blog= models.ForeignKey(to='Blog',null=True)
category = models.ForeignKey(to='Category',null=True)
tags = models.ManyToManyField(to='Tag',
through='Article2Tag',
through_fields=('article','tag')
)
class Article2Tag(models.Model):
article= models.ForeignKey(to='Article')
tag= models.ForeignKey(to='Tag')
class UpAndDown(models.Model):
user = models.ForeignKey(to='UserInfo')
article = models.ForeignKey(to='Article')
is_up = models.BooleanField() # 传布尔值 存0/1
class Comment(models.Model):
user = models.ForeignKey(to='UserInfo')
article = models.ForeignKey(to='Article')
content = models.CharField(verbose_name='评论内容',max_length=255)
comment_time= models.DateTimeField(auto_now_add=True,verbose_name='评论时间') # 这个是记录修改时间,DateField记录创建时间
# 自关联
parent = models.ForeignKey(to='self',null=True) # 有些评论就是跟评论
# 注册功能
我们之前按直接在views.py中书写的forms组件代码
但是为了解耦合 我们应该将所有的forms组件代码单独写道一个地方
如果你的项目自始至终只用到一个forms组件那么你可以直接建一个py文件使用即可
myforms.py
但是如果你的项目需要使用多个forms组件,那么你可以创建一个文件夹在在文件夹内根据
forms组件的功能的不同创建不同的py文件
myforms文件夹
regform.py
loginform.py
userform.py
orderform.py
...
[后端]
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': 'jason', 'password': '123', 'confirm_password': '123', 'email': '122@qq.com'}
clean_data = form_obj.cleaned_data # 将校验通过的数据赋值给一个变量
# 将字段里面的confirm_password键值对删除
clean_data.pop('confirm_password') # {'username': 'jason', 'password': '123', 'email': '122@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())
[前端]
-[HTML部分]
<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.label }}</label>
{{ form }}
<span style="color: red" class="pull-right">{{ form.errors.0 }}</span>
</div>
{% endfor %}
<div class="form-group">
<label for="myfile">头像
{% load static %}
<img src="{% static 'img/default.png' %}" alt="" width="100" style="padding-left: 20px" id="myimg">
</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>
-[事件1 文本域变化,获取头像显示]
$('#myfile').change(function (){
// 文件阅读器对象
// 1、先生成一个文件阅读器对象
let myFileReaderObj= new FileReader();
// 2、获取用户上传的文件头像
let fileObj = $(this)[0].files[0]
// 3、将文件对象交给阅读读取
myFileReaderObj.readAsDataURL(fileObj) // 异步操作 IO操作
// 4、利用文件阅读器将文件展示到前端页面 修改src属性
// 等待文件阅读器加载完毕之后在执行
myFileReaderObj.onload = function () {
$('#myimg').attr('src', myFileReaderObj.result)
}
})
-[事件2 点击事件 ajax发送文件、接收报错]
$('#id_commit').click(function (){
// 发送ajax请求 我们发送请求既包含普通键值对,也包含文件
let forDataObj = new FormData();
// 1、添加普通键值对
{#console.log($('#myform').serializeArray())#} // [{},{},{},{} 只包含普通键值对 ]
$.each($('#myform').serializeArray(),function (index,obj){
// console.log(index,obj) obj={} 前端字典又称为对象
forDataObj.append(obj.name,obj.value)
})
// 2、添加文件数据
forDataObj.append('avatar',$('#myfile')[0].files[0])
// 3、发送ajax请求
$.ajax({
url:'',
type:'post',
data:forDataObj,
// 需要指定两个关键性参数
contentType:false,
processData:false,
success:function (args){
if(args.code == 1000){
// 跳转到登录页面
window.location.href = args.url
}else{
// 如何将对应的报错信息提示到对应的input框
$.each(args.msg,function (index,obj){
let targetId= '#id_'+index;
$(targetId).next().text(obj[0]).parent().addClass('has-error')
})
}
}
})
})
-[事件3 获取焦点事件]
// 给所有的input框获取焦点事件
$('input').focus(function (){
// 将input下面的span标签的文本和input的has-error的class属性消除
$(this).next().text(' ').parent().removeClass('has-error')
})
# 登录功能
img标签的src属性
1、图片路径
2、url
3、图片的二进制数据
我们的计算机上面之所以能够敲出各种字体样式
内部其实对应的是一个个.ttf结尾文件
# 后端代码获取验证码操作
def login(requerst):
return render(requerst,'login.html')
[图片相关模块]
pip3 install pillow
from PIL import Image,ImageDraw,ImageFont
-[Image:生成图片]
-[ImageDraw:能够在图片上乱涂乱画]
-[ImageFont:控制字体的样式]
from io import BytesIO,StringIO
-[BytesIO:临时存储数据,返回格式二进制]
-[StringIO:临时存储数据,返回格式字符串]
import random
def get_random():
return random.randint(0,255),random.randint(0,255),random.randint(0,255)# [自动会将这些用元组组织起来]
def get_code(request):
# 推到步骤1:直接获取够短现成的图片二进制数据发送给前端
# with open(r'static/img/111.png','rb')as f:
# data=f.read()
# return HttpResponse(data)
# 推到步骤2:利用pillow模块动态生成图片
# img_obj = Image.new('RGB',(430,35),get_random())
# 将图片存进去
# with open('xx.png','wb')as f:
# img_obj.save(f,'png')
# 将图片取出来
# with open('xx.png','rb')as f:
# data= f.read()
# return HttpResponse(data)
# 推导步骤3:文件存储繁琐IO操作效率低 借助于内存管理器模块
# img_obj = Image.new('RGB', (430, 35), get_random())
# io_obj = BytesIO() # 生成一个内置管理对象 你可以看成文件句柄
# img_obj.save(io_obj,'png')
# return HttpResponse(io_obj.getvalue()) # 从内存管理器中读取二进制的图片数据返回给前端
[最终步骤4:写图片验证码]
img_obj = Image.new('RGB', (430, 35), get_random())
img_draw = ImageDraw.Draw(img_obj) # 产生画笔对象
img_font = ImageFont.truetype('static/font/222.ttf',30) # 字体样式 大小
[随机验证码 五位数的随机验证码 数字 小写字符 大写字母]
code=''
for i in range(5):
randon_upper= chr(random.randint(65,90))
randon_lower= chr(random.randint(95,122))
randon_int= chr(random.randint(0,99))
# 从上面三个随机选择一个
tmp=random.choice([randon_int,randon_upper,randon_lower])
# 将随机生成字符串写入到图片上面
img_draw.text((i*60+60,-2),tmp,get_random(),img_font)
# 拼接到code里面
code += tmp
print(code)
[随机验证码在登录的视图函数里面需要用到 要比对 所以找地方存起来并且其他视图函数也能拿到]
request.session['code']=code
io_obj=BytesIO()
img_obj.save(io_obj,'png')
return HttpResponse(io_obj.getvalue())
# 前端
<div class="container">
<div class="row">
<div class="col-md-8 col-offset-2">
<h1 class="text-center">登录</h1>
<div class="form-group">
<label for="username">用户名</label>
<input type="text" name="username" id="username" class="form-control">
</div>
<div class="form-group">
<label for="passowrd"></label>
<input type="password" name="password" id="password" class="form-control">
</div>
<div class="form-group">
<label for="">验证码</label>
<div class="row">
<div class="col-md-6 ">
<input type="text" name="code" id="id-code" class="form-control">
</div>
<div class="col-md-6" >
<img src="/get_code/" alt="" width="380" height="35" id="id_img">
</div>
</div>
</div>
<input type="button" class="btn btn-success" value="登录">
</div>
</div>
</div>
-[图片点击事件]
<script>
$('#id_img').click(function (){
// 先获取到标签的src
let img_obj = $(this).attr('src');
// 在src后面加任意字符
$(this).attr('src',img_obj+='?')
})
</script>
|
|