数据库表创建及同步
'''
表的关系如下(models.py)
1、用户表
继承AbstractUser (既具有auto_user表字段,也有扩展字段)
扩展
phone 电话号码
avatar 用户头像
creat_time 创建时间
外键字段
一对一个人站点表(查询频率较高,建在用户表)
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/default.png
'''
create_time = models.DateField(auto_now_add=True,verbose_name='创建时间')
blog = models.OneToOneField(to='Blog',null=True,on_delete=models.CASCADE)
(站点)
2.个人站点表
site_name 站点名称
site_title 站点标题
site_theme 站点样式
class Blog(models.Model):
site_name = models.CharField(verbose_name='站点名称',max_length=32)
site_title = models.CharField(verbose_name='站点标题',max_length=32)
#site_theme存放的是css/js的文件路径
site_theme = models.CharField(verbose_name='站点样式',max_length=64)
3.文章标签表
name 标签名
外键字段
一对多个人站点
class Tag(models.Model):
name = models.CharField(verbose_name='文章标签',max_length=32)
blog = models.ForeignKey(to='Blog', null=True,on_delete=models.CASCADE)
4.文章分类表
name 分类名
外键字段
一对多个人站点
class Category(models.Model):
name = models.CharField(verbose_name='文章分类',max_length=32)
blog = models.ForeignKey(to='Blog',null=True,on_delete=models.CASCADE)
5.文章表
title 文章标题
desc 文章简介
content 文章内容
create_time 发布时间
数据库字段设计优化(******)(设计为普通字段,然后同步的将对应的普通字段更新即可)
(虽然下述的三个字段可以从其他表里面跨表查询计算得出,但是频繁跨表效率)
up_num 点赞数
down_num 点踩数
comment_num 评论数
外键字段
一对多个人站点
多对多文章标签
一对多文章分类
class Article(models.Model):
title = models.CharField(verbose_name='文章标题',max_length=64)
desc = models.CharField(verbose_name='文章简介',max_length=255)
#文章内容由很多,一般情况下使用TextFiled
content = models.TextField(verbose_name='文章内容')
create_time = models.DateField(auto_now_add=True)
#数据库字段设计优化
up_num = models.BigIntegerField(verbose_name='点赞数',default=0)
down_num = models.BigIntegerField(verbose_name='点踩数',default=0)
comment_num = models.BigIntegerField(verbose_name='评论数',default=0)
blog = models.ForeignKey(to='Blog',null=True,on_delete=models.CASCADE)
category = models.ForeignKey(to='Category',null=True,on_delete=models.CASCADE)
#多对多文章标签(三种方式,其中我们用半自动)
tags = models.ManyToManyField(to='Tag',
through='Article2Tag',
through_fields=('article','tag'),
)
#第三张关系表
class Article2Tag(models.Model):
article = models.ForeignKey(to='Article',on_delete=models.CASCADE)
tag = models.ForeignKey(to='Tag',on_delete=models.CASCADE)
6.点赞点踩表
记录哪个用户给哪篇文章点了赞还是点了踩
user ForeignKey(to="User")
article ForeignKey(to="Article")
is_up BooleanField()
class UpAndDown(models.Model):
user = models.ForeignKey(to='UserInfo',on_delete=models.CASCADE)
article = models.ForeignKey(to='Article',on_delete=models.CASCADE)
is_up = models.BooleanField() #存布尔值0/1
7.文章评论表
记录哪个用户给哪篇文章写了哪些评论内容
user ForeignKey(to="User")
article ForeignKey(to="Article")
content CharField()
comment_time DateField()
# 自关联
parent ForeignKey(to="Comment",null=True)
# ORM专门提供的自关联写法
parent ForeignKey(to="self",null=True)
class Comment(models.Model):
user = models.ForeignKey(to='UserInfo',on_delete=models.CASCADE)
article = models.ForeignKey(to='Article',on_delete=models.CASCADE)
content = models.CharField(verbose_name='评论内容',max_length=255)
comment_time = models.DateTimeField(verbose_name='评论时间',auto_now_add=True)
#自关联
parent = models.ForeignKey(to='self',null=True,on_delete=models.CASCADE)
#记得要加null=True
'''
views.py导的模块
from django.shortcuts import render,HttpResponse,redirect from app01.myforms import MyRegForm from app01 import models from django.http import JsonResponse
forms组件
myform.py
# 书写针对用户表的forms组件代码
from django import forms
from app01 import models
class MyRegForm(forms.Form):
username = forms.CharField(label='用户名',
max_length=8,
min_length=3,
error_messages={
'min_length': '用户名最小3位',
'max_length': '用户名最大8位',
'required': '用户名不能为空'
},
# 还需要让标签有bootstrap样式
widget=forms.widgets.TextInput(
attrs={
'class': 'form-control'})
)
password = forms.CharField(label='密码',
max_length=8,
min_length=3,
error_messages={
'min_length': '密码最小3位',
'max_length': '密码最大8位',
'required': '密码不能为空'
},
# 还需要让标签有bootstrap样式
widget=forms.widgets.PasswordInput(
attrs={
'class': 'form-control'})
)
confirm_password = forms.CharField(
label='确认密码', max_length=8, min_length=3,
error_messages={
'min_length': '确认密码最小3位',
'max_length': '确认密码最大8位',
'required': '确认密码不能为空'
},
# 还需要让标签有bootstrap样式
widget=forms.widgets.PasswordInput(
attrs={'class': 'form-control'})
)
email = forms.EmailField(label='邮箱',
error_messages={
'required': '邮箱不能为空',
'invalid': '邮箱格式不正确',
},
# 还需要让标签有bootstrap样式
widget=forms.widgets.EmailInput(
attrs={
'class': 'form-control'})
)
# 钩子函数
# 局部钩子:当你需要给单个字段增加校验规则的时候使用(校验用户名是否存在)
def clean_username(self):
username = self.cleaned_data.get(
'username')
# 去数据库校验
is_exist = models.UserInfo.objects.filter(
username=username)
if is_exist:
self.add_error('username', '用户已存在!')
return username
# 全局钩子:当需要给多个字段增加校验规则的时候使用(校验两次密码是否一致)
def clean(self):
password = self.cleaned_data.get(
'password')
confirm_password = self.cleaned_data.get(
'confirm_password')
if not password == confirm_password:
self.add_error('confirm_password',
'两次密码不一致')
return self.cleaned_data
注册功能
def register(request):
form_obj = MyRegForm()
back_dic = {'code': 1000, 'msg': ''}
if request.method == 'POST':
#校验数据是否合法
form_obj = MyRegForm(request.POST)
#判断数据是否合法
if form_obj.is_valid():
# print(form_obj.cleaned_data) #{'username': 'tom', 'password': '1234', 'confirm_password': '1234', 'email':'111@qq.com'}
#将校验通过的数据字典赋值给一个变量
clean_data = form_obj.cleaned_data
#将字典里面的confirm_password键值对删除
clean_data.pop('confirm_password')
#用户头像
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())
登录功能
def login(request):
return render(request,'login.html')
登录功能
- 实现图片验证码
'''
图片相关的模块
pip 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',(500,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)
#文件存储繁琐IO操作效率低
#推导步骤3:借助内存管理器模块
# img_obj = Image.new('RGB', (500, 35),
# get_random())
# io_obj = BytesIO() #生成一个内存管理器对象,可以看成是文件句柄
# img_obj.save(io_obj,'png')
# return HttpResponse(io_obj.getvalue())
#从内存管理器读取二进制的图片数据返回给前端
#最终步骤4:写图片验证码
img_obj = Image.new('RGB', (500, 35),get_random())
img_draw = ImageDraw.Draw(img_obj) #产生一个画笔对象
img_font = ImageFont.truetype('static/font/222.ttf',30) #字体样式和大小
#随机验证码,5位数字,小写字母,大写字母
code = ''
for i in range(5):
random_upper = chr(random.randint(65,90))
random_lower = chr(random.randint(97,122))
random_int = str(random.randint(0,9))
#从上面三个里面随机选择一个
tmp = random.choice([random_upper,random_lower,random_int])
#将产生的随机字符串写入到图片上
'''
为什么是一个个写而不是生成5个后再写?
因为一个个写可以控制每个字体的间隙,而生成后就无法控制了
'''
img_draw.text((i*60+60,0),tmp,get_random(),img_font)
#拼接随机字符串
code += tmp
# print(code)
#随机验证码在登录的视图函数需要用到,要比对,所以要找地方存起来并且其他视图函数
#也能拿到
request.session['code'] = code
io_obj = BytesIO()
img_obj.save(io_obj,'png')
return HttpResponse(io_obj.getvalue())
register.html(注册)
Title
注册
login.html(登录)
Title
{% load static %}
登录
urls
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('register/',views.register,name='reg'),
path('login/',views.login,name='login'),
path('get_code/',views.get_code,name='gc'),
]



