|
一、数据库表创建及同步# 下面是关于表的具体字段和外键对应关系
from django.db import models
"""
先写普通字段
再写外键字段
"""
from django.contrib.auth.models import AbstractUser # 在settings中配置 AUTH_USER_MODEL = 'app01.UserInfo'
class UserInfo(AbstractUser):
phone=models.BigIntegerField(verbose_name='手机号',null=True,blank=True)
"""
null=True 数据库该字段可以为空
blank=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 Meta:
verbose_name_plural = '用户表' # 修改admin后台管理默认的表名
# verbose_name = '用户表' # 修改admin后台管理默认的表名,但是还是会有s后缀的
def __str__(self):
return self.username # ruturn返回必须是字符串类型
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的文件路径
def __str__(self):
return self.state_name # ruturn返回必须是字符串类型
class Category(models.Model):
name = models.CharField(verbose_name='文章发类',max_length=32)
blog= models.ForeignKey(to='Blog',null=True)
def __str__(self):
return self.name # ruturn返回必须是字符串类型
class Tag(models.Model):
name = models.CharField(verbose_name='文章标签',max_length=32)
blog= models.ForeignKey(to='Blog',null=True)
def __str__(self):
return self.name # ruturn返回必须是字符串类型
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')
)
def __str__(self):
return self.title
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) # 有些评论就是跟评论
二、注册页面的搭建、JS代码
①利用form组件搭建注册页面from django import forms
from app01 import models
class MyRegForm(forms.Form):
username = forms.CharField(label='用户名', min_length=3, max_length=8,
error_messages={
'required': '用户名不能为空',
'min_length': "用户名最少3位",
'max_length': "用户名最大8位"
},
# 还需要让标签有bootstrap样式
widget=forms.widgets.TextInput(attrs={'class': 'form-control'})
)
password = forms.CharField(label='密码', min_length=3, max_length=8,
error_messages={
'required': '密码不能为空',
'min_length': "密码最少3位",
'max_length': "密码最大8位"
},
# 还需要让标签有bootstrap样式
widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'})
)
confirm_password = forms.CharField(label='确认密码', min_length=3, max_length=8,
error_messages={
'required': '确认密码不能为空',
'min_length': "确认密码最少3位",
'max_length': "确认密码最大8位"
},
# 还需要让标签有bootstrap样式
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')
# 去数据库中校验
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
② 前端页面搭建<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.auto_id }}">{{ 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 %}
{# 只要把图片放到label标签里面,label的for指向myfile就不跳出选择文件标签#}
<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>③ 动态效果展示:文本域变化、click点击事件发送Ajax请求、input获取焦点去除报错框
<script>
// 文本域变化,头像更改
$("#myfile").change(function () {
// 文件阅读器对象
// 1 先生成一个文件阅读器对象
let myFileReaderObj = new FileReader();
// 2 获取用户上传的头像文件
let fileObj = $(this)[0].files[0;
// 3 将文件对象交给阅读器对象读取
myFileReaderObj.readAsDataURL(fileObj) // 异步操作 IO操作 会导致在attr获得的结果为空,不显示图片了
// 4 利用文件阅读器将文件展示到前端页面 修改src属性
// 等待文件阅读器加载完毕之后才执行下面代码,要不然会没有值,头像就没有了
myFileReaderObj.onload = function(){
$('#myimg').attr('src',myFileReaderObj.result)
}
})
// 点击事件,注册信息发送普通键值对,文件信息
$('#id_commit').click(function () {
// 发送ajax请求 我们发送的数据中即包含普通的键值也包含文件
let formDataObj = new FormData();
// 1.添加普通的键值对
// {#console.log($('#myform').serializeArray()) // [{},{},{},{},{}] 只包含普通键值对#}
$.each($('#myform').serializeArray(),function (index,obj) {
// obj = {}
formDataObj.append(obj.name,obj.value)
});
// 2.添加文件数据
formDataObj.append('avatar',$('#myfile')[0].files[0]);
// 3.发送ajax请求
$.ajax({
url:"",
type:'post',
data:formDataObj,
// 需要指定两个关键性的参数
contentType:false,
processData:false,
success:function (args) {
if (args.code==1000){
// 跳转到登陆页面
window.location.href = args.url
}else{
// 如何将对应的错误提示展示到对应的input框下面
// forms组件渲染的标签的id值都是 id_字段名
$.each(args.msg,function (index,obj) {
// {#console.log(index,obj) // username ["用户名不能为空"]#}
let targetId = '#id_' + index;
$(targetId).next().text(obj[0]).parent().addClass('has-error')
})
}
}
})
})
// 给所有的input框绑定获取焦点事件
$('input').focus(function () {
// 将input下面的span标签和input外面的div标签修改内容及属性
$(this).next().text('').parent().removeClass('has-error')
})
</script>
三、后端代码from django.http import JsonResponse
from app01 import models
from app01.myforms import MyRegForm
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': '123@qq.com'}
clean_data = form_obj.cleaned_data # 将校验通过的数据字典赋值给一个变量
# 将字典里面的confirm_password键值对删除
clean_data.pop('confirm_password') # {'username': 'jason', 'password': '123', 'email': '123@qq.com'}
# 用**clean_data就可以把数据打散,然后通过create(username=username,password=password,email=email的方式传入值)
# 用户头像
file_obj = request.FILES.get('avatar')
"""针对用户头像一定要判断是否传值 不能直接添加到字典里面去"""
if file_obj:
clean_data['avatar'] = file_obj # 这个键不能随便写,一定符合数据库的models字段,数据库是avatar
# 直接操作数据库保存数据
models.UserInfo.objects.create_user(**clean_data)# 不能用create保存数据,因为密码是明文的了
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())
|
|