项目开发流程
需求分析: 引导客户朝着自己想要的方向提需求
架构设计: 框架, 语言, 数据库, 缓存数据库, 表设计, 拆分功能, 项目报价(参与开发的人员个数和开发天数)
分组开发: 架构师组长会议: 按模块功能拆分任务; 小组会议: 细分功能; 小组成员写代码, 提前测试下显而易见的bug
交付测试: 测试些不是显而易见的bug, 如果是显而易见的bug, 会扣绩效
运维上线: 1. 委托项目开发公司上线和维护, 定期支付维护费; 2. 交付给对方公司
表设计
先确定表名, 然后是表字段, 最后是表关系
用户表
- 使用auth_user表
- 手机号
- 头像
- 注册时间
外键字段: blog, 一对一个人站点表
个人站点表
- 站点名称
- 站点标题
- 站点样式
文章标签表
- 标签名
外键字段: blog, 多对一个人站点表
文章分类表
- 分类名
外键字段: blog, 多对一个人站点表
文章表
- 文章标题
- 文章摘要
- 文章内容
- 文章发布时间
外键字段1: blog, 多对一个人站点表
外键字段2: tag, 多对多文章标签表, 半自动创建
外键字段3: category, 多对一文章分类表
文章点赞点踩表
- user_id: 多对一用户表
- article_id: 多对一文章表
- is_upper: BooleanField
类似于用户和文章的关系表, 加一个is_upper字段
文章的评论表
- user_id: 多对一用户表
- article_id: 多对一文章表
- content: TextField
- create_time
自关联: parent, 和自己所在的表外键关联, 多对一, 根评论/子评论
''' DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'bbs', 'USER': 'root', 'PASSWORD': 'Cql123456', 'HOST': '127.0.0.1', 'PORT': 3306, 'CHARSET': 'utf8' } } STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'static') ] AUTH_USER_MODEL = 'app01.UserInfo' from django.db import models from django.contrib.auth.models import AbstractUser # Create your models here. class UserInfo(AbstractUser): ... # 将用户上传的文件保存到avatar文件夹下, 数据库中对应的avatar字段保存文件路径 avatar = models.FileField(upload_to='avatar/', default='static/img/default.jpg') ... class Blog(models.Model): ... site_theme = models.CharField(max_length=64) # 存储用户自定义的css文件路径 ... class Article(models.Model): ... # 数据库优化的三个普通字段 up_num = models.IntegerField(default=0) down_num = models.IntegerField(default=0) comment_num = models.IntegerField(default=0) # 外键字段 ... tag = 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 Comment(models.Model): ... # 自关联字段 # parent = models.ForeignKey(to='Comment') # 方式一 parent = models.ForeignKey(to='self', null=True) # 方式二, 语义更明确 '''
注册页面搭建
更改文件创建的初始化模板: File-->Settings-->Editor-->File and Code Templates-->Files
''' # app01-->myform.py from django import forms from app01 import models class MyRegForm(forms.Form): username = forms.CharField( max_length=8, min_length=3, label='用户名', error_messages={ 'max_length': '用户名最长为8位', 'min_length': '用户名最短为3位', 'required': '用户名不能为空', }, widget=forms.widgets.TextInput(attrs={'class': 'form-control'}) ) password = forms.CharField( ... ) confirm_password = forms.CharField( ... ) email = forms.EmailField( label='邮箱', error_messages={ 'invalid': '邮箱格式不正确', 'required': '邮箱不能为空', }, widget=forms.widgets.EmailInput(attrs={'class': 'form-control'}) ) # 局部钩子函数校验用户名是否存在 def clean_username(self): username = self.cleaned_data.get('username') res = models.UserInfo.objects.filter(username=username) # app01_userinfo表中只有password字段为密文, 可以校验用户名是否已存在 if res: self.add_error('username', '用户名已存在') return username # 全局钩子校验两次密码是否一致 def clean(self): password = self.cleaned_data.get('password') confirm_data = self.cleaned_data.get('confirm_password') if not password == confirm_data: self.add_error('confirm_password', '两次密码不一致') return self.cleaned_data # views.py from django.shortcuts import render from app01 import myform def register(request): form_obj = myform.MyRegForm() return render(request, 'register.html', locals()) # register.html ... <form action="" id="myform"> {% for form_field_obj in form_obj %} <div class="form-group"> <label for="form_field_obj.auto_id">{{ form_field_obj.label }}</label> # form_field_obj.auto_id获取forms组件渲染出的input框的id # label默认获取到的是类中字段名的首字母大写形式 {{ form_field_obj }} <span class="errors pull-right" style="color: red"></span> </div> {% endfor %} <div class="form-group"> <label for="user_file"> 头像 <img src="/static/img/default.jpg" alt="" width="100" style="margin-left: 10px;" id="img"> </label> <input type="file" name="avatar" id="user_file" style="display: none"> </div> <input type="button" class="btn btn-success pull-right" value="注册" id="submit"> </form> ... <script> $('#user_file').on( 'change', // change事件 function () { let myFileReader = new FileReader(); // 文件阅读器对象, 专门读取文件并展示到页面上 let fileObj = $(this)[0].files[0]; // 获取用户上传的文件对象 myFileReader.readAsDataURL(fileObj); // 文件阅读器读取文件, 异步IO操作 myFileReader.onload = function () { // 等待文件阅读器读取完毕之后再执行函数体代码 $('#img').attr('src', myFileReader.result); // 将读取的结果替换到img标签的src属性中 } } ) </script> '''
注册功能实现
''' # views.py def register(request): form_obj = myform.MyRegForm() if request.method == 'POST': back_dic = {'code': 1000, 'msg': ''} # 响应ajax请求的信息字典 form_obj = myform.MyRegForm(request.POST) # forms组件校验ajax提交的POST请求中的数据 if form_obj.is_valid(): cleaned_data = form_obj.cleaned_data cleaned_data.pop('confirm_password') file_obj = request.FILES.get('avatar') if file_obj: cleaned_data['avatar'] = file_obj models.UserInfo.objects.create_user(**cleaned_data) back_dic['msg'] = '注册成功' 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()) # register.html <script> ... $('#submit').on( 'click', function () { let myFormData = new FormData(); $.each( $('#my_form').serializeArray(), // form标签中的serializeArray方法将普通数据序列化成数组套键值对的形式 function (index, obj) { // index为索引, obj为字典对象 myFormData.append(obj.name, obj.value) // 添加普通价值对 } ); myFormData.append('avatar', $('#user_file')[0].files[0]); // 手动添加文件数据 $.ajax({ url: '', type: 'post', data: myFormData, processData: false, // 浏览器不处理数据 contentType: false, // 浏览器不使用任何编码 success: function (data) { if (data.code == 1000) { window.location.href = data.url // 前端控制页面跳转 } else { $.each( data.msg, function (index, obj) { // index为报错字段名, obj为数字形式的错误信息 let targetId = '#id_' + index; // 拼接报错信息对应input标签的id $(targetId).next().text(obj[0]).parent().addClass('has-error') // 展示报错信息并使input框飘红 } ) } } }) } ); $('input').on( 'focus', function () { $(this).next().text('').parent().removeClass('has-error') // input框获取焦点时, 清除报错信息和飘红 } ) </script> '''
回顾添加内容
项目庞大, 需要用到多个forms组件, 将所有forms组件放到一个单独的文件夹下, 针对不同的forms组件开设不同的py文件:
文件夹: 1.py, 2.py, ...
img标签的src属性可以放的值:
- 图片的地址
- 图片的二进制数据
- 后端url, 页面渲染完毕后会自动朝该url发送get请求
window.onload = function () {}
fileReader.onload = function () {}, 解决异步文件读取的造成的问题
来源:https://www.cnblogs.com/-406454833/p/12012163.html