注册页面及注册功能实现

项目开发流程

需求分析: 引导客户朝着自己想要的方向提需求

架构设计: 框架, 语言, 数据库, 缓存数据库, 表设计, 拆分功能, 项目报价(参与开发的人员个数和开发天数)

分组开发: 架构师组长会议: 按模块功能拆分任务; 小组会议: 细分功能; 小组成员写代码, 提前测试下显而易见的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属性可以放的值:

  1. 图片的地址
  2. 图片的二进制数据
  3. 后端url, 页面渲染完毕后会自动朝该url发送get请求

window.onload = function () {}

fileReader.onload = function () {}, 解决异步文件读取的造成的问题

原文地址:https://www.cnblogs.com/-406454833/p/12012163.html