模仿BBS的注册功能,先谈一谈项目需求分析
项目分析
表设计
用户表(UserInfo)
用户电话phone
用户头像avatar
用户创建时间create_time
blog 》》》site 一对一个人站点表
个人站点表(Blog)
站点名称site_name
站点标题site_title
站点样式site_theme
文章标签表(Tag)
标签名称name
blog >>> Blog 一对多个人站点表
文章分类表
分类名称name
blog >>> Blog 一对多个人站点表
文章表
文章标题title
文章简介desc
文章详情content
文章发布时间create_time
# 数据库查询优化
文章评论数comment_num
文章点赞数up_num
文章点踩数down_num
blog 》》》 Blog 一对多个人站点表
tags >>> Tag 多对多标签表
category 》》》 Category 一对多分类表
点赞点踩表
用户名字段user 一对多 个人站点/用户
文章字段article 一对多 文章表
点赞点踩is_up 0/1
文章评论表
用户名字段user 一对多 个人站点/用户
文章字段article 一对多 文章表
评论内容content
父评论parent(自己跟自己关联) 一对多自身
models.py文件
from django.db import models from django.contrib.auth.models import AbstractUser class UserInfo(AbstractUser): phone = models.BigIntegerField(null=True) create_time = models.DateField(auto_now_add=True) avatar = models.FileField(upload_to='avatar/',default='avatar/default.png') 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=64) 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=64) desc = models.CharField(max_length=255) content = models.TextField() create_time = models.DateField(auto_now_add=True) comment_num = models.IntegerField() up_num = models.IntegerField() down_num = models.IntegerField() blog = models.ForeignKey(to='Blog',null=True) category = models.ForeignKey(to='Category',null=True) tags = models.ManyToManyField(to='Tag',through='Article2Tags',through_fields=('article','tag')) class Article2Tags(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) create_time = models.DateField(auto_now_add=True) parent = models.ForeignKey(to='self',null=True)
我们使用forms组件来限制前后端数据的传输
forms.py文件
from django import forms from django.forms import widgets from app01 import models class MyForm(forms.Form): username = forms.CharField(max_length=8,min_length=3,label='用户名:',error_messages={ 'required':'用户名不能为空', 'max_length':'用户名最长8位', 'min_length':'用户名最短3位', },widget = widgets.TextInput(attrs={'class':'form-control'})) password = forms.CharField(max_length=8,min_length=3,label='密码',error_messages={ 'required':"密码不能为空", 'max_length':'密码最长为8位', 'min_length':'密码最短为3位', },widget =forms.widgets.PasswordInput(attrs={'class':'from-control'})) confirm_password = forms.CharField(max_length=8,min_length=3,label='确认密码',error_messages={ 'required':'确认密码不能为空', 'max_length':'确认密码最大8位', 'min_length':'确认密码最小3位' },widget =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') 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
使用views.py视图函数来对路由过来的路径进行匹配执行
from django.shortcuts import render from app01 import myforms from app01 import models from django.http import JsonResponse # Create your views here. def register(request): back_dic= {'code':100,'msg':''} form_obj = myforms.MyForm() if request.method =='POST': form_obj =myforms.MyForm(request.POST) if form_obj.is_valid(): data = form_obj.cleaned_data data.pop('confirm_password') file_obj = request.FILES.get('myfile') if file_obj: data['avatar']= file_obj models.UserInfo.objects.create_user(**data) back_dic['msg']='注册成功' back_dic['url']='/login/' else: back_dic['code']=101 back_dic['mag']=form_obj.errors return JsonResponse(back_dic) return render(request,'register.html',locals())
我们不妨来看看前端页面经过渲染是什么样子吧
其实对于用户名,密码以及确认密码和邮箱这种键值对来说我们用ajax来进行交互式时,只是常规操作
对于头像,我们该怎么如何进行传输呢?
$('#d1').click(function () { $.ajax({ // 提交的地址,不写默认提交至当前页面,同form表单的action url:'/index/', // 提交的方式 type:'post', // 提交的数据,一般以键值对的形式出现 data:{'name':'jason','password':'123'}, // 回调函数 success:function (data) { // data接收的就是异步提交返回的结果 alert(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> </head> <body> <div class="container-fluid"> <div class="row"> <div class="col-md-6 col-md-offset-3"> <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/img/default.png" alt="" width="80" style="margin-left: 20px" id ='id_img'> </label> <input type="file" name="myfile" id="id_myfile" style="display:none"> </div> <button class="btn btn-primary pull-right" id="id_submit">注册</button> </div> </div> </div> <script> $('#id_myfile').change(function () { let fileObj = this.fiels[0]; let fileReader= new FileReader() fileReader.readAsDataURL(fileObj); fileReader.onload = function () { $('#id_img').attr('src',fileReader.result) } }); $('#id_submit').click(function () { let formData = new FormData(); $.each($('#myform').serializeArray(),function (index, obj) { formData.append(obj.name, obj.value) }); formData.append('myfile',$('id_myfile')[0].files[0]); $.ajax({ url:'', type:'post', data:formData, processData:false, contentType:false, success:function(data){ if (data.code==100){ location.href=data.url }else{ $.each(data.msg,function (index,obj) { let targetId ='#id_'+index; $(targetId).next().html(obj[0].parent().addClass('has-error')) }) } } }) }); $('input').focus(function () { $(this).next().html('').parent().removeClass('has-error') }) </script> </body> </html>
这是经过Javascript的处理后端h5文件
在写一个注册页面时,想要实现用户选择头像时,直接就能将选择的头像显示出来,就必须使用到JS的FileReader对象。
FileReader 对象允许Web应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 File 或 Blob 对象指定要读取的文件或数据。 其中File对象可以是来自用户在一个元素上选择文件后返回的FileList对象(上面提到的使用files属性得到的文件对象),也可以来自拖放操作生成的 DataTransfer对象,还可以是来自在一个HTMLCanvasElement上执行mozGetAsFile()方法后返回结果。
可以用window对象的方法查看浏览器对FileReader的支持
if(window.FileReader) { var fr = new FileReader(); // add your code here } else { alert("Not supported by your browser!"); }