一、表创建及同步
from django.db import models
from django.contrib.auth.models import AbstractUser
# 用户信息表
class UserInfo(AbstractUser):
# 新增phone,avatar,create_time,blog字段
phone = models.BigIntegerField()
avatar = models.ImageField(upload_to='avatar/',default='avatar/default.png')
create_time = models.DateTimeField(auto_now_add=True)
blog = models.OneToOneField(to='Blog',null=True)
# 个人站点表
class Blog(models.Model):
# 创建站点标题,名称,样式
site_title = models.CharField(max_length=32)
site_name =models.CharField(max_length=32)
# 存放的只是样式的路径
site_theme = models.CharField(max_length=254)
# 分类表
class Category(models.Model):
name = models.CharField(max_length=32)
blog = models.ForeignKey(to=Blog,null=True)
# 标签表
class Tag(models.Model):
name = models.CharField(max_length=32)
blog =models.ForeignKey(to=Blog,null=True)
# 文章表
class Article(models.Model):
title = models.CharField(max_length=32)
# 摘要
desc = models.CharField(max_length=254)
content = models.TextField()
create_time = models.DateField(auto_now_add=True)
# 总点赞
up_num = models.IntegerField(default=0)
# 总点踩
dowm_num = models.IntegerField(default=0)
# 总评论
comment_num = models.IntegerField(default=0)
# 和分类的一对多关系
category = models.ForeignKey(to='Category',null=True)
# 和站点的一对多关系
blog = models.ForeignKey(to='Blog')
# 和标签的多对多关系
tag = 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 UpAndDowm(models.Model):
user = models.ForeignKey(to='UserInfo')
article = models.ForeignKey(to='Article')
is_up = models.BooleanField()
# 评论表
class Comment(models.Model):
user = models.ForeignKey(to='UserInfo')
article = models.ForeignKey(to='Article')
content = models.CharField(max_length=32)
comment_time = models.DateField(auto_now_add=True)
parent = models.ForeignKey(to='self',null=True)
二、注册功能
注册功能要注意的点:
- forms验证数据
- views内对不同请求和不同类型数据的处理
- 前端对后端响应数据的处理
- 前端对头像文件的处理
views.py
def register(request):
form_obj = forms.MyRegForm()
if request.method == 'POST':
# 这个dic用来存储数据状态,如果成功直接跳转url,如果失败展示errors,这个逻辑在前端书写
back_dic = {'code':1000,'url':''}
# 前端传来的formdata对象会直接把普通数据分配到request.POST中,把文件对象分配到request.FILES内
# 实例化form对象,把我们的普通数据都传进去
form_obj = forms.MyRegForm(request.POST)
# 判断数据是否合法
if form_obj.is_valid():
# 获取头像文件
file_obj = request.FILES.get('avatar')
form_obj.cleaned_data.pop('password_again')
cleaned_data = form_obj.cleaned_data
# 这里没有头像也没关系,我们在设计表的时候头像就已经有一个默认值了
if file_obj:
cleaned_data['avatar'] = file_obj
# 因为cleaned_data是一个字典,所以可以通过**打散数据成xx=xx的形式,灵活运用
models.UserInfo.objects.create_user(**cleaned_data)
back_dic['url']='/login'
else:
back_dic['code']=2000
# 如果数据有误需要在前端展示错误信息
back_dic['msg'] = form_obj.errors
return JsonResponse(back_dic)
forms.py
from django import forms
from app01 import models
class MyRegForm(forms.Form):
username = forms.CharField(label='用户名',min_length=3,max_length=8,
error_messages={
'max_length':'8个字符太多了!',
'min_length':'3个字符太少了!',
'required':'不能为空!'
},
widget=forms.widgets.TextInput(attrs={'class':'form-control'})
)
password = forms.CharField(label='密码',max_length=8,min_length=3,
error_messages={
'max_length':'8个字符太多了!',
'min_length':'3个字符太少了!',
'required':'不能为空!'
},
widget=forms.widgets.PasswordInput(attrs={'class':'form-control'})
)
password_again = forms.CharField(label='再次确认密码',max_length=8,min_length=3,
error_messages={
'max_length':'8个字符太多了!',
'min_length':'3个字符太少了!',
'required':'不能为空'
},
widget=forms.widgets.PasswordInput(attrs={'class':'form-control'})
)
email = forms.EmailField(label='邮箱',error_messages={'required':'不能为空!','invalid':'邮箱格式不正确'},
widget = forms.widgets.EmailInput(attrs={'class':'form-control'}))
def clean_username(self):
username = self.cleaned_data.get('username')
obj = models.UserInfo.objects.filter(username=username)
if obj :
self.add_error('username','用户名已存在!')
return username
def clean(self):
password = self.cleaned_data.get('password')
password_again = self.cleaned_data.get('password_again')
if password != password_again:
self.add_error('password_again','两次密码不一致')
return self.cleaned_data
register.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<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>
{% load static %}
<div class="container-fluid">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<h1 class="text-center">注册</h1>
<form id="myform">
{% csrf_token %}
{% for form in form_obj %}
<div class="form-group">
<<! form.auto_id可以获取当前标签的id>>
<label for="{{ form.auto_id }}">{{ form.label }}</label>
{{ form }}
<span style="color: red" class="pull-right"></span>
</div>
{% endfor %}
<div class="form-group">
<<! 这里要注意在label内部点击任何东西都会跳转到for指定的标签内!!!!!>>
<label for="myfile">头像
{% load static %}
<img src="{% static 'img/default.png' %}" id='myimg' alt="" width="100" 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 () {
// 生成一个文件阅读器对象,可以用来读取文件内容
let myFileReaderObj = new FileReader();
// 获取文件对象
let fileObj = $(this)[0].files[0];
// 把文件对象交给文件阅读器处理,这里的返回结果是文件的二进制数据
// 异步操作,所以我们要等他加载完毕再执行后面的代码
myFileReaderObj.readAsDataURL(fileObj);
myFileReaderObj.onload = function () {
$('#myimg').attr('src',myFileReaderObj.result)
}
})
$('#id_commit').click(function () {
// 通过formdata对象发送ajax对象
let formDataObj = new FormData();
// 通过each循环的serializeArray方法,得到的index是索引,obj是具体的对象,重要方法
$.each($('#myform').serializeArray(),function (index,obj) {
formDataObj.append(obj.name,obj.value)
});
formDataObj.append('avator',$('#myfile')[0].files[0])
$.ajax({
url:'',
type:'post',
data:formDataObj,
contentType:false,
processData:false,
success:function (args) {
if (args.code==1000){
window.location.href = args.url
}else {
// 如果状态码没对上说明有错误数据
// 要把错误信息展示到对应标签
$.each(args.msg,function (index,obj) {
// 因为forms创建的标签的id都是id_字段名
let targetId = '#id_' + index;
$(targetId).next().text(obj[0]).parent().addClass('has-error')
})
}
}
})
})
// 给所有input框绑定获取焦点事件
$('input').focus(function () {
$(this).next().text('').parent().removeClass('has-error')
})
</script>
</body>
</html>
二、登录页面搭建
这里只实现了登录页面的搭建并没有写后端验证登录
views.py
from PIL import Image,ImageDraw,ImageFont
"""
Image:生成图片
ImageDraw:能够在图片上乱涂乱画
ImageFont:控制字体样式
"""
from io import BytesIO
"""
内存管理器模块
BytesIO:临时帮你存储数据 返回的时候数据是二进制
StringIO:临时帮你存储数据 返回的时候数据是字符串
"""
import random
# Create your views here.
# 登录功能
def login(request):
return render(request,'login.html')
# 产生rgb随机颜色
def get_random():
return random.randint(0,255),random.randint(0,255),random.randint(0,255)
# 获取验证码
def get_code(request):
# 方式一:直接打开文件
# with open(r'static/img/default.png','rb') as f:
# data = f.read()
# return HttpResponse(data)
# 推导步骤2:利用pillow模块动态产生图片
# img_obj = Image.new('RGB',(430,35),'green')
# img_obj = Image.new('RGB',(430,35),get_random())
# # 先将图片对象保存起来
# with open('xxx.png','wb') as f:
# img_obj.save(f,'png')
# # 再将图片对象读取出来
# with open('xxx.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()) # 从内存管理器中读取二进制的图片数据返回给前端
# 最终成型
# 产生一个随机颜色的图片
img_obj = Image.new('RGB',(620,37),get_random())
# 产生一个画笔对象
img_draw = ImageDraw.Draw(img_obj)
# 设置字体样式,大小
img_font = ImageFont.truetype('static/font/111.ttf',28)
# 产生5位随机验证码
code=''
for i in range(5):
random_upper = chr(random.randint(65,90))
random_lower = chr(random.randint(97,122))
random_int = chr(random.randint(0,9))
tmp = random.choice([random_lower,random_upper,random_int])
# 产生一个字符就要写到图片上,这样可以控制位置
img_draw.text((i*60+60,-2),tmp,get_random(),img_font)
code += tmp
request.session['code'] = code
io_obj = BytesIO()
img_obj.save(io_obj,'png')
return HttpResponse(io_obj.getvalue())
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<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>
<div class="form-group">
<label for="">用户名</label>
<input type="text" name="username" class="form-control">
</div>
<div class="form-group">
<label for="">密码</label>
<input type="text" name="password" class="form-control">
</div>
<div class="form-group">
<label for="">验证码</label>
<div class="row">
<div class="col-md-6">
<input type="text" class="form-control">
</div>
<div class="col-md-6">
<img src="/get_code/" alt="" width="620px" height="37px" id="myimg">
</div>
</div>
</div>
<input type="button" class="btn btn-success" value="登录">
</div>
</div>
</div>
<script>
$('#myimg').click(function () {
let src = $(this).attr('src')
$(this).attr('src',src += '?')
})
</script>
</body>
</html>