1 auth组件:django内置的一套用户,认证系统本质是一个app(admin,auth)
2 方法
-authenticate(username=lqz,password=123,phone=12222)
-login:写了session,把当前登录用户写入了request.user request.user.print_name()
-logout:退出
-is_authenticate :判断用户是否登录(视图,模板)
-login_require:登录认证装饰器(基于session的)
-creat_user/superuser:直接存表,密码是明文,借助这俩方法创建用户
-check_password:通过明文密码校验
-set_password:一定要记住保存
-is_staff,is_active,is_superuser(User对象的属性,表中的字段)
-扩展auth_user表:一对一,继承(在setting中配置)
3 多对多关系,第三张表的创建
-手动创建:book author 没有直接的关联字段
-中介模型:手动创建中间表,book和author写上关联字段
-authors=models.ManyToManyField(to='Author',through='AuthorToBook',through_fields=('book_id','author_id'))
-基于对象的跨表查询:正常使用
-基于双下滑下的连表查询:正常使用
-book.authors.add,set,remove,clear (不能用了, AuthorToBook:表)
-自动创建第三张表
1 项目开发流程
1 互联网项目(产品经理提需求),传统行业项目(客户提需求)
2 项目开发流程
-需求分析(组长,项目经理,产品经理)
-原型设计(产品经理)
-美工UI切图
-设计程序,数据库(上面连个跟设计数据库可以同步操作)
-分任务开发(张三写用户相关,李四写订单相关),多人协同开发(git,svn)
-过了三个月所有任务开发完了
-测试(专门的测试)
-上线
3 项目开发模式
-瀑布模式:
-敏捷开发:
4 博客项目需求
-多人博客
-博客首页
-登录:图片验证码
-注册:上传头像
-自己有自己的个人站点(根据分类,标签,时间,过滤文章)
-自己的后台管理
-发表博客(富文本编辑器,xss攻击处理)
-查看,删除
-文章分类
-随笔档案
-文章标签
-文章
-文章详情
-评论(根评论,子评论)
-点赞点踩
2 bbs项目表设计
# 表设计(8张)
1 用户表:UserInfo表
2 博客表:Blog表
3 分类表:Category
4 标签表:Tag
5 文章:Article(文章和详情一个表)
6 评论:Commit
7 点赞点踩表:UpAndDown
# 表关系
见图
# 表中字段
1 用户表:UserInfo表(通过继承auth_user扩展)
-telephone
-avatar(头像)
-blog 关联字段
2 博客表:Blog表
-site_title
-site_name
-site_style
3 分类表:Category
-name
-blog
4 标签表:Tag
-name
-blog
5 文章:Article(文章和详情一个表)
-title
-desc
-content
-create_time
-blog
-category
-tag(在数据没有字段)
-#最后更新时间
6 评论:Commit
-user
-article
-create_time
-content
7 点赞点踩表:UpAndDown
-user
-article
-is_up
-create_time
# 假设加一张关注表
user
atricle
create_time
3 数据库表创建及同步
# 根评论和子评论分析
id user articel content commit
1 1 1 写的真好 null
2 1 2 写的真烂 null
3 2 2 你放屁,人家写的很好 2
4 1 2 你怎么骂人呢? 3
5 2 2 就骂你怎么了 4
6 3 2 你这个嘴太碎了 2
7 4 2 碎嘴子 2
from django.db import models
from django.contrib.auth.models import AbstractUser
class UserInfo(AbstractUser):
phone = models.CharField(max_length=32)
# upload_to文件上传以后存放的路径
# FileField本质是varchar类型
# 坑()
avatar = models.FileField(upload_to='avatar/', default='avatar/default.png')
blog = models.OneToOneField(to='Blog',on_delete=models.CASCADE)
class Blog(models.Model):
site_title = models.CharField(max_length=32)
site_name = models.CharField(max_length=32)
# 每个人样式不同(文件地址)
site_style = models.CharField(max_length=32)
class Tag(models.Model):
name = models.CharField(max_length=32)
blog = models.ForeignKey(to='Blog',on_delete=models.CASCADE)
class Category(models.Model):
name = models.CharField(max_length=32)
blog = models.ForeignKey(to='Blog',on_delete=models.CASCADE)
class Article(models.Model):
title = models.CharField(max_length=32)
desc = models.CharField(max_length=128)
# 大文本
content = models.TextField()
create_time = models.DateTimeField(auto_now_add=True)
# 关联关系
blog = models.ForeignKey(to='Blog',on_delete=models.CASCADE)
category = models.ForeignKey(to='Category',on_delete=models.CASCADE)
# 多对多关系
tag = models.ManyToManyField(to='Tag', through='TagToArticle', through_fields=('article', 'tag'))
class TagToArticle(models.Model):
tag = models.ForeignKey(to='Tag',on_delete=models.CASCADE)
article = models.ForeignKey(to='Article',on_delete=models.CASCADE)
class UpAndDown(models.Model):
user = models.ForeignKey(to='UserInfo',on_delete=models.CASCADE)
article = models.ForeignKey(to='Article',on_delete=models.CASCADE)
# 实质存的时候,是0和1
is_up = models.BooleanField()
create_time = models.DateTimeField(auto_now_add=True)
class Commit(models.Model):
user = models.ForeignKey(to='UserInfo',on_delete=models.CASCADE)
article = models.ForeignKey(to='Article',on_delete=models.CASCADE)
content = models.CharField(max_length=256)
create_time = models.DateTimeField(auto_now_add=True)
# 存父评论的id号
# commit_id=models.IntegerField()
# commit=models.ForeignKey(to='Commit')
# 自关联(不能叫表明小写)
commit_id=models.ForeignKey(to='self',on_delete=models.CASCADE)
# 踩了两个坑
avatar = models.FileField(upload_to='avatar/', default='avatar/default.png')
commit_id=models.ForeignKey(to='self',on_delete=models.CASCADE)
# 迁移(两条命令)
1 用户名,密码, 修改个人信息:邮箱,手机号,头像
2 用户名,密码,邮箱,头像
from django import forms
from django.forms import widgets
from blog import models
from django.forms import ValidationError
class RegisterForm(forms.Form):
username = forms.CharField(required=True, max_length=18, min_length=3, label='用户名',
error_messages={'required': '该字段必填',
'max_length': '最大长度为18',
'min_length': '最短为3'},
widget=widgets.TextInput(attrs={'class': 'form-control'}))
password = forms.CharField(required=True, max_length=18, min_length=3, label='密码',
error_messages={'required': '该字段必填',
'max_length': '最大长度为18',
'min_length': '最短为3'},
widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
re_password = forms.CharField(required=True, max_length=18, min_length=3, label='确认密码',
error_messages={'required': '该字段必填',
'max_length': '最大长度为18',
'min_length': '最短为3'},
widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
email = forms.EmailField(required=True, label='邮箱',
error_messages={'required': '该字段必填'},
widget=widgets.EmailInput(attrs={'class': 'form-control'}))
# 用户名如果存在了,就不能注册
def clean_username(self):
username = self.cleaned_data.get('username')
# 去数据库查询
user = models.UserInfo.objects.filter(username=username).count()
if user: # 不合法
raise ValidationError('用户名已经存在')
else:
return username
# 校验两次密码是否一致
def clean(self):
password = self.cleaned_data.get('password')
re_password = self.cleaned_data.get('re_password')
if re_password == password:
return self.cleaned_data
else:
raise ValidationError('两次密码不一致')
5 注册功能页面搭建
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
<script src="/static/jquery-3.3.1/jquery-3.3.1.min.js"></script>
<title>注册</title>
</head>
<body>
<div class="container-fluid">
<div class="row">
<h1 class="text-center">注册功能</h1>
<div class="col-md-6 col-md-offset-3">
<form id="form">
{% csrf_token %}
{% for foo in form %}
<div class="form-group">
<label for="{{ foo.auto_id }}">{{ foo.label }}</label>
{{ foo }}
</div>
{% endfor %}
<div class="form-group">
<label for="id_myfile">头像
<img src="/static/img/default.png" alt="" id="id_img" height="80" width="80"
style="margin-left: 10px">
</label>
<input type="file" name="myfile" id="id_myfile" style="display: none">
</div>
<div class="text-center">
{#注意这个坑#}
<input type="button" value="注册" id="id_submit" class="btn btn-danger">
</div>
</form>
</div>
</div>
</div>
</body>
<script>
//放文件的标签发生变化,我们把文件搞出来,放到img标签中
$("#id_myfile").change(function () {
//借助于文件阅读器
const filereader = new FileReader()
//把图片读到filereader对象中
//$('#id_myfile')[0].files[0]
filereader.readAsDataURL($('#id_myfile')[0].files[0])
//$('#id_img').attr('src','https://account.cnblogs.com/images/registersideimg.png')
//$('#id_img').attr('src',filereader.result) //这样不行,文件没读完
filereader.onload = function () {
//文件完全读到文件阅读器以后,再执行
$('#id_img').attr('src', filereader.result)
}
})
$("#id_submit").click(function () {
var formdata = new FormData()
formdata.append('avatar', $('#id_myfile')[0].files[0])
//第一种方式:
/*
formdata.append('username', $('#id_name').val())
formdata.append('password', $('#id_password').val())
formdata.append('re_password', $('#id_re_password').val())
formdata.append('email', $('#id_email').val())
*/
//第二种方式:
var ser = $('#form').serializeArray()
//[{name:name,value:lqz}, {name:password],value:123}, {name:re_password],value:123}, {name:email],value:123@qq.com}]
//console.log(ser)
$.each(ser, function (k, v) {
//console.log(v.name)
//console.log(v.value)
formdata.append(v.name, v.value)
})
$.ajax({
url: '/register/',
method: 'post',
processData: false,
contentType: false,
data: formdata,
success: function (data) {
if (data.code == 100) {
console.log(data.msg)
//js控制的跳转
location.href=data.url
} else {
//有错误,需要渲染页面
}
}
})
})
</script>
</html>
6 头像实时显示
$("#id_myfile").change(function () {
//借助于文件阅读器
const filereader = new FileReader()
//把图片读到filereader对象中
//$('#id_myfile')[0].files[0]
filereader.readAsDataURL($('#id_myfile')[0].files[0])
//$('#id_img').attr('src','https://account.cnblogs.com/images/registersideimg.png')
//$('#id_img').attr('src',filereader.result) //这样不行,文件没读完
filereader.onload = function () {
//文件完全读到文件阅读器以后,再执行
$('#id_img').attr('src', filereader.result)
}
})
7 注册功能完成
后端
def register(request):
if request.method == 'GET':
form = RegisterForm()
return render(request, 'register.html', context={'form': form})
elif request.method == 'POST':
# 校验数据是否合法
res = {'code': 100, 'msg': '注册成功'}
form = RegisterForm(data=request.POST)
if form.is_valid():
# 保存到数据库
data = form.cleaned_data # {username:lqz,password:123,re_password:123,email:3@qq.com}
data.pop('re_password')# {username:lqz,password:123,email:3@qq.com}
file=request.FILES.get('avatar')
if file:
data['avatar']=file # {username:lqz,password:123,email:3@qq.com,'avatar':文件对象}
print(data)
models.UserInfo.objects.create_user(**data)
# res['url']='/index/'
res['url']='http://www.baidu.com'
'''
FileField自动干了这个事
with open('media/avatar/%s'%file.name,'wb') as f:
for line in file:
f.write(line)
path='media/avatar/%s'%file.name
'''
# models.UserInfo.objects.create_user(username=username,password=password,email=email,avatar=path)
# 返回,会被ajax接收到
return JsonResponse(res)
else:
# 数据校验不通过
res['code'] = 101 # 101表示注册失败
res['msg'] = '数据验证失败'
res['error'] = form.errors
return JsonResponse(res)
前端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
<script src="/static/jquery-3.3.1/jquery-3.3.1.min.js"></script>
<title>注册</title>
</head>
<body>
<div class="container-fluid">
<div class="row">
<h1 class="text-center">注册功能</h1>
<div class="col-md-6 col-md-offset-3">
<form id="form">
{% csrf_token %}
{% for foo in form %}
<div class="form-group">
<label for="{{ foo.auto_id }}">{{ foo.label }}</label>
{{ foo }}
</div>
{% endfor %}
<div class="form-group">
<label for="id_myfile">头像
<img src="/static/img/default.png" alt="" id="id_img" height="80" width="80"
style="margin-left: 10px">
</label>
<input type="file" name="myfile" id="id_myfile" style="display: none">
</div>
<div class="text-center">
{#注意这个坑#}
<input type="button" value="注册" id="id_submit" class="btn btn-danger">
</div>
</form>
</div>
</div>
</div>
</body>
<script>
//放文件的标签发生变化,我们把文件搞出来,放到img标签中
$("#id_myfile").change(function () {
//借助于文件阅读器
const filereader = new FileReader()
//把图片读到filereader对象中
//$('#id_myfile')[0].files[0]
filereader.readAsDataURL($('#id_myfile')[0].files[0])
//$('#id_img').attr('src','https://account.cnblogs.com/images/registersideimg.png')
//$('#id_img').attr('src',filereader.result) //这样不行,文件没读完
filereader.onload = function () {
//文件完全读到文件阅读器以后,再执行
$('#id_img').attr('src', filereader.result)
}
})
$("#id_submit").click(function () {
var formdata = new FormData()
formdata.append('avatar', $('#id_myfile')[0].files[0])
//第一种方式:
/*
formdata.append('username', $('#id_name').val())
formdata.append('password', $('#id_password').val())
formdata.append('re_password', $('#id_re_password').val())
formdata.append('email', $('#id_email').val())
*/
//第二种方式:
var ser = $('#form').serializeArray()
//[{name:name,value:lqz}, {name:password],value:123}, {name:re_password],value:123}, {name:email],value:123@qq.com}]
//console.log(ser)
$.each(ser, function (k, v) {
//console.log(v.name)
//console.log(v.value)
formdata.append(v.name, v.value)
})
$.ajax({
url: '/register/',
method: 'post',
processData: false,
contentType: false,
data: formdata,
success: function (data) {
if (data.code == 100) {
console.log(data.msg)
//js控制的跳转
location.href=data.url
} else {
//有错误,需要渲染页面
}
}
})
})
</script>
</html>