一、数据库表分析
1、首先是用户表,用户表继承Django自带的auth_user表,目的是可以使用Django封装好的方法,不用自己手动写cookie和session操作,还可以自定义字段
2、个人站点表,和用户是一对一关系,一个用户都有一个站点
3.文章表,字段有标题和内容,并且还应该有文章的分类和标签,文章只能有一个分类,比如说这篇文章是Python,那篇是前端,但是一个文章可以有多个标签,所以应该还有文章分类表(和文章表是一对多的关系),文章标签表(和文章表是一对多的关系)
4.文章分类表
5.文章标签表
6.评论表,和用户是一对一的关系
7.论坛怎么能没有点赞评论,所以加一个点赞点踩表,一个用户一篇文章只能点一次赞或点一次踩,所以是一对多关系
关系图
二、表的建立
使用的是mysql
from django.db import models from django.contrib.auth.models import AbstractUser #导入AbstractUser类,继承之后,Django自带的Auth_user就被替换了 # Create your models here. class UserInfo(AbstractUser): # 自定义字段 phone = models.CharField(max_length=32) avatar = models.FileField(upload_to='avatar/',default='avatar/default.png') # 用户上传头像,upload_to是头像保存的位置,default是用户默认的头像 create_time = models.DateField(auto_now_add=True) # 关联个人页表 blog = models.OneToOneField(to='Blog',null=True) class Blog(models.Model): site_name = models.CharField(max_length=32) site_title = models.CharField(max_length=64) # 个人站点的样式文件 存该样式文件的路径 theme = models.CharField(max_length=64) #种类表 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=255) # 存大段文本 content = models.TextField() create_time = models.DateField(auto_now_add=True) # 文章的评论数,点赞数,点踩数,虽然有评论表,点赞表,点踩表,但是每次获取数据都是一次查询,速度会很慢,所以直接在文章表里添加 comment_num = models.IntegerField(default=0) up_num = models.IntegerField(default=0) down_num = models.IntegerField(default=0) 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() class Comment(models.Model): user = models.ForeignKey(to='UserInfo') article = models.ForeignKey(to='Article') content = models.CharField(max_length=255) # 自己是自己的外键,回复如果有其他回复,那么他的parent就是第一个楼层,否则默认是null parent = models.ForeignKey(to='self',null=True)
还要在setting文件里:添加你继承AbstractUser的表名
AUTH_USER_MODEL='app01.UserInfo'
执行数据库迁移命令
python manage.py makemigrations
python mange.py migrate
三、注册页面
完成效果:
forms类的创建
from django import forms from . import models class MyForm(forms.Form): username = forms.CharField(label='用户名',max_length=14,min_length=3,error_messages={ 'max_length':'用户名不能超过14位!', 'min_length':'用户名不能少于3位!', 'required':'用户名不能为空!' },widget=forms.TextInput(attrs={'class':'form-control'})) password = forms.CharField(label='密码', max_length=14, min_length=3, error_messages={ 'max_length': '密码不能超过14位!', 'min_length': '密码不能少于3位!', 'required': '密码不能为空!' }, widget=forms.PasswordInput(attrs={'class': 'form-control'})) confirm_password = forms.CharField(label='确认密码', max_length=14, min_length=3, error_messages={ 'max_length': '确认密码不能超过14位!', 'min_length': '确认密码不能少于3位!', 'required': '确认密码不能为空!' }, widget=forms.PasswordInput(attrs={'class': 'form-control'})) email = forms.EmailField(label='邮箱',error_messages={ 'required':'邮箱不能为空!', 'invalid':'邮箱格式错误!' },widget=forms.EmailInput(attrs={'class':'form-control'})) #局部钩子 def clean_username(self): #获取用户传来的已通过form组件验证的username username = self.cleaned_data.get('username') #在数据库的UserInfo表内的username做匹配,取到对象 user_obj = models.UserInfo.objects.filter(username=username).first() #判断对象存不存在,不存在则创建,存在则返回 if user_obj: 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
前端页面的搭建
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> <link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.min.css"> <script src="/static/bootstrap-3.3.7/js/bootstrap.min.js"></script> <style> body { background-color: #eeeeee; } </style> </head> <body> <div class="container-fluid"> <div class="row"> <div class="col-md-4 col-md-offset-4"> <h2 class="text-center">注册</h2> <hr> <form id="myform"> {% csrf_token %} {% for form in form_obj %} <div class="form-group"> <label for="{{ form.auto_id }}">{{ form.label }}</label> {{ form }} <span class="errors pull-right" style="color: red"></span> </div> {% endfor %} </form> <div class="form-group"> <label for="id_myfile">头像 <img src="/static/image/default.jpg" alt="" width="60" style="margin-left: 20px" id="id_img"> </label> <input type="file" name="myfile" id="id_myfile" style="display: none"> </div> <button class="pull-right btn btn-primary btn-block" id="id_submit">注册</button> </div> </div> </div>
//背景动态效果 可以直接复制使用 <script type="text/javascript" color="87,0,86" opacity='1' zIndex="-2" count="99" src="//cdn.bootcss.com/canvas-nest.js/1.0.1/canvas-nest.min.js"></script>
后端代码
def register(request): #自定义状态码,等会ajax要用到 respone_code={'code':100,'msg':''} #初始化forms组件 form_obj=myforms.RegForm() #渲染注册页面,locals()把from_obj,response_code传到了前端,起码目前是这样 return render(request,'register.html',locals())
这只是get请求的代码
上传头像
//上传头像并替换 $('#id_myfile').change(function () { // 先获取用户上传的文件对象 let fileObj = this.files[0]; // 生成一个内置对象 let fileReader = new FileReader(); // 将文件对象传递给内置对象 fileReader.readAsDataURL(fileObj); // 将读取出文件对象替换到img标签 fileReader.onload = function () { // 等待文件阅读器读取完毕再渲染图片 $('#id_img').attr('src', fileReader.result) } });
ajax异步提交
$('#id_submit').click(function () { //生成一个Formdata对象 let formData = new FormData(); //$('#myform').serializeArray()是form表单中的所有数据,自动获取form表单中所有input框键值对,用each循环取出来 $.each($('#myform').serializeArray(), function (index, obj) { //只是添加了普通的键值对,文件对象需要你手动添加 formData.append(obj.name, obj.value) }); {#console.log($('#myform').serializeArray())#} //手动添加文件对象 formData.append('myfile', $('#id_myfile')[0].files[0]); $.ajax({ url: '', type: 'post', data: formData, processData: false, // 告诉浏览器不要处理我的数据 contentType: false, // 不要用任何的编码,就用我formdata自带的编码格式,django能够自动识别改formdata对象 success: function (data) { if (data.code == 100) { /*跳转到登录界面 location.href 获取URL location.href="URL" // 跳转到指定页面 location.reload() 重新加载页面*/ location.href = data.url } else { $.each(data.msg, function (index, obj) { let targetId = '#id_' + index; //字符串拼接 id_username,id_password... $(targetId).next().html(obj[0]).parent().addClass('has-error') }) } } }) }); $('input').focus(function () { $(this).next().html('').parent().removeClass('has-error') })
$('#myform').serializeArray()是form表单中的所有数据,自动获取form表单中所有input框键值对,用each循环取出来
console.log($('#myform').serializeArray())
post请求发送后端代码
from django.shortcuts import render,HttpResponse from . import myforms from . import models from django.http import JsonResponse # Create your views here. #注册 def register(request): back_dic = {'code':100,'msg':''} #实例化一个form对象 form_obj = myforms.MyForm() if request.method == 'POST': form_obj = myforms.MyForm(request.POST) if form_obj.is_valid(): # 拿到通过验证的用户信息字典,里面还有confirm_password cleaned_data = form_obj.cleaned_data # 由于创建用户不需要confirm_password这个字段,需要删除 cleaned_data.pop('confirm_password') # 获取用户上传的头像文件 file_obj = request.FILES.get('myfile') # 如果用户上传头像,则加入cleaned_data里并已通过创建到用户表里 if file_obj: cleaned_data['avatar'] = file_obj #因为我们需要创建的用户信息和值都在cleaned_data里面这里,cleaned_data又是一个字典,可以**打撒成?=?的方法传值 # 注意的是前端的name的值必须跟数据库里的字段一直才可以**传值 models.UserInfo.objects.create_user(**cleaned_data) back_dic['msg'] = '注册成功' back_dic['url'] = '/login/' else: back_dic['code'] = 101 back_dic['msg'] = form_obj.errors # 因为给前端传的是字典,需要用JsonResponse return JsonResponse(back_dic) return render(request,'register.html',locals())