BBS(仿博客园系统)项目01(项目分析、表结构设计、注册功能实现)

摘要:css

  • 需求分析
  • 表结构设计
  • 注册功能实现

1、需求分析:

  • 项目需求(产品经理、架构师、开发组组长与客户谈该项目相关要求)
  • 项目设计 (架构师须要思考:框架选择,数据库选择,主要功能模块,报价:包括工期、开发人员工资...)
  • 任务分发(开发组长开会,拆分项目各个任务,分发到各组)
  • 测试(本身测试/本地测试,测试员测试,白盒测试--开发人员、黑盒测试--测试人员)
  • 交付上线(运维配合上线)

2、项目分析:

表设计:

  1. 用户表(UserInfo)
    首先咱们使用Django Web框架,因此用户表继承Django的auth_user表的结构,咱们自定义建立UserInfo表,在原Django自动建立的auth_user表基础上

    补充添加一些字段:
    用户电话(phone)BigIntegerField类型
    用户头像(avatar)FileField类型(指定上传路径upload_to=‘’,默认头像文件default='avatar/xxx.jpg')
    用户建立时间(create_time) DateField类型(设置时间为新增记录时候自动设置为当前时间auto_now_add=True)
    一对一关系的blog字段(blog)OneToOneField(to='Blog', null=True)
    须要知道:用户user会有点赞点踩(UpAndDown)、评论(Comment)、站点(Blog)html

  2. 我的站点表(Blog)
    站点名称(blog_name) CharField类型,建议长度32
    站点标题(blog_title)CharField类型,建议长度64
    站点样式主题(blog_theme) CharField类型,存储css文件路径,建议长度64
    须要知道:站点blog上会有标签(Tag)、分类(Category)、文章(Article)
  3. 文章标签表(Tag)
    标签名称(name)CharField类型,建议长度32
    一对多关系:Blog(一)>>>Tag(多):
    blog外键字段 ForeignKey(to='Blog',null=True)
  4. 文章分类表(Category)
    分类名称(name)CharField类型,建议长度32
    一对多关系:Blog(一)>>>Category(多):
    blog外键字段 ForeignKey(to='Blog',null=True)
  5. 文章表(Article)
    文章标题(title) CharField类型 建议长度64
    文章摘要(desc)CharField类型 建议长度255
    文章内容(content)TextField类型
    文章发布时间(create_time) DateField类型(设置时间为新增记录时候自动设置为当前时间auto_now_add=True)
    # 优化查询建立的字段:
    评论数(comment_num)IntegerField类型
    点赞数(up_num)IntegerField类型
    踩数(down_num)IntegerField类型
    # Article表与我的站点Blog、标签Tag、分类Category都有关联:
    一对多关系:
    Blog(一)>>>Article(多):blog外键字段 ForeignKey(to='Blog',null=True)
    Category(一)>>>Article(多):category外键字段 ForeignKey(to='Category',null=True)
    多对多关系:(这里咱们手动建立第三张多对多关联表Article2Tag)
    Tag >>> Article :tag多对多外键字段 :ManyToManyField(to='Tag', through='Article2Tag', through_fields=('article', 'tag'))
  6. 点赞点踩表(UpAndDown)
    是否点赞(is_up)BooleanField类型,0表明点踩,1表明点赞
    点赞点踩会与用户表UserInfo、文章表Article关联:
    一对多关系:
    UserInfo(一)>>>UpAndDown(多):user外键字段 ForeignKey(to='UserInfo')  一个用户能够点多个赞和踩,一个踩或者赞只多是一个用户点的
    Article(一)>>>UpAndDown(多):user外键字段 ForeignKey(to='Article')  一篇文章能够被点多个赞和踩,一个踩或者赞只可能对应在一片文章上
  7. 评论表(Comment)
    评论内容(content)CharField类型 建议长度255
    建立时间(create_time) DateField类型(设置时间为新增记录时候自动设置为当前时间auto_now_add=True)
    评论表会与用户表UserInfo、文章表Article关联:
    一对多关系:
    UserInfo(一)>>>Comment(多):user外键字段 ForeignKey(to='UserInfo')  一个用户能够发布多个评论,一个评论只多是一个用户发布的
    Article(一)>>>Comment(多):user外键字段 ForeignKey(to='Article')  一篇文章能够有多个评论,一个评论只可能对应在一片文章上
    父评论(parent)ForeignKey类型 ForeignKey(to='self') 这个特殊,跟本身关联

    总结:BBS表直接的关系图:

 注册功能实现:

  1.  配置环境
    setting.py文件
    1、设置静态文件 2、告诉Django,自定义建立用户表 3、使用MySQL数据库,配置数据库参数 STATIC_URL = '/static/' STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'static'), os.path.join(BASE_DIR, 'app01/static') ] AUTH_USER_MODEL = 'app01.UserInfo' DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'bbs01', 'HOST': '127.0.0.1', 'PORT': 3306, 'USER': 'root', 'PASSWORD': '123' } } 
    # 注意在init文件中写入:

     

      import pymysql
      pymysql.install_as_MySQLdb()前端

     

  2. 配置Django的form表单相关数据:
    app01文件夹下新建文件夹myform,接着在此文件下新建myform.py文件,此py文件中:
    from django import forms from app01 import models # 建立Django的form表单
    class MyForm(forms.Form): username = forms.CharField( max_length=8, min_length=3, label='用户名:', error_messages={ 'required': '用户名不能为空', 'max_length': '用户名最大为8位', 'min_length': '用户名最小3位', }, widget=forms.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': 'form-control'}) ) confirm_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': 'form-control'}) ) email = forms.EmailField( label='邮箱:', error_messages={ 'required': '邮箱不能为空', 'invalid': '邮箱格式错误' }, widget=forms.widgets.EmailInput(attrs={'class': 'form-control'}) ) # 使用局部钩子和全局钩子对用户名和密码进行二次校验:
        # 全局钩子校验密码输入是否一致:
        def clean(self): password = self.cleaned_data.get('password') confirm_password = self.cleaned_data.get('confirm_password') if password != confirm_password: self.add_error('confirm_password', '输入两次密码不一致') return self.cleaned_data  # 记得返回
    
        # 局部钩子校验用户名是否存在:
        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   # 记得返回
  3. 项目app01中models.py文件中建立Django的orm映射表关系模型数据:
    models.py中,建立表(前提是须要先建立一个数据库(bbs01))
    from django.db import models from django.contrib.auth.models import AbstractUser # Create your models here.
    class UserInfo(AbstractUser): phone = models.BigIntegerField(null=True) create_time = models.DateField(auto_now_add=True) blog = models.OneToOneField(to='Blog', null=True) avatar = models.FileField(upload_to='avatar/', default='avatar/default_avatar.jpg') class Blog(models.Model): blog_name = models.CharField(max_length=32) blog_title = models.CharField(max_length=64) blog_theme = models.CharField(max_length=64) class Article(models.Model): title = models.CharField(max_length=64) content = models.TextField() desc = models.CharField(max_length=255) create_time = models.DateField(auto_now_add=True) blog = models.ForeignKey(to='Blog', null=True) category = models.ForeignKey(to='Category', null=True) tag = models.ManyToManyField(to='Tag', through='Article2Tag', through_fields=('article', 'tag')) # 优化查询
        comment_num = models.IntegerField() up_num = models.IntegerField() down_num = models.IntegerField() class Category(models.Model): name = models.CharField(max_length=32) blog = models.ForeignKey(to='Blog', null=True) class Article2Tag(models.Model): article = models.ForeignKey(to='Article') tag = models.ForeignKey(to='Tag') class Tag(models.Model): name = models.CharField(max_length=32) blog = models.ForeignKey(to='Blog', null=True) class UpAndDown(models.Model): is_up = models.BooleanField() user = models.ForeignKey(to='UserInfo') article = models.ForeignKey(to='Article') class Comment(models.Model): content = models.CharField(max_length=255) create_time = models.DateField(auto_now_add=True) user = models.ForeignKey(to='UserInfo') article = models.ForeignKey(to='Article') parent = models.ForeignKey(to='self', null=True)

    注意:记得运行数据库迁移命令makemigrations>>>migrater 使建立的表生效
  4. 配置路由,视图函数、前端register.html建立
    ## urls.py中:
    from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^register/', views.register) ]
    ## register.html文件建立:
    <!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>#} <script src="/static/jQuery-3.4.1.js"></script>
        <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css">
        <link rel="stylesheet" href="/static/reg.css">
        <script src="/static/bootstrap-3.3.7-dist/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表单的目的在于咱们须要经过form表单批量处理表单内输入的数据对象#} <form id="myform"> {% csrf_token %} {% for form in form_obj %} {# 经过在input框外套个div,设置div的class包含form-group,#} {# 就能够经过bootstrap的样式来调整input框之间的间距#} <div class="form-group">
                            <label for="{{ form.auto_id }}">{{ form.label }}</label> {{ form }} <span class="has-error pull-right"></span>
                        </div> {% endfor %} </form>
                    <div class="form-group">
                        <label for="id_myfile">
                            <img id="id_avatar" src="/static/avatar/default_avatar.jpg" width="70" class="img-thumbnail">
                        </label> 点击图片上传头像 <input type="file" id="id_myfile" name="myfile" class="hide">
                    </div>
                    <button class="btn btn-primary pull-right" id="id_submit">提交注册</button>
            </div>
        </div>
    </div>
    
    <script>
        // 利用文件阅读器动态展现用户上传的图像
     $('#id_myfile').on('change',function () { // 获取上传文件对象
     let file_obj = this.files[0]; // 生成一个文件阅读器内置对象
     let fileReader = new FileReader(); // 把文件对象传给文件阅读器对象
     fileReader.readAsDataURL(file_obj); // 将读取出的文件对象替换到img标签
            // 由于渲染图片的速度远比文件阅读器读取文件的速度
            // 因此,这里须要用onload方法等待文件阅读器读取完毕以后在渲染图片,图片才会显示
     fileReader.onload = function () { $('#id_avatar').attr('src', fileReader.result) // img标签src属性可放:文件路径、文件二进制数据、url
     } }); // 经过ajax发送请求,提交数据
     $('#id_submit').on('click',function () { // ajax没法传本身传文件,须要借助内置对象FormData传文件
            // 声明一个formData对象,formData对象既能传文件又能够传普通键值,经过append的方式添加
     let formData = new FormData(); // jQuery对象方法each方法:
            // 将经过$('#myform').serializeArray()方法获得的json格式的对象数组遍历它每一个键值对
            // 把遍历出来的每一个键值对经过函数处理,function()中的index是遍历出来的键值对加的索引,
            // obj就是遍历出来的键值对(也就是这里form表单里面input框的name和填写的value)
     $.each( // 经过serializeArray() 方法序列化form表单值来建立对象数组(名称和值),该方法返回的是个json对象
                // 返回的 JSON 对象是由一个对象数组组成的,其中每一个对象包含键值对 name和value
     $('#myform').serializeArray(), function (index, obj) {formData.append(obj.name,obj.value);} // 1 {name: "username", value: "sgt"} 对应的index 和 obj
     ); // function函数内将遍历出来的json数据经过点name和点value获得对应键值添加进formData中
    
            // 普通键值(input框填入的键值)添加完毕,接下来添加文件数据到formData中
     formData.append('myfile', $('#id_myfile')[0].files[0]); $.ajax({ url: '', // 不写默认当前页面路由提交
     type: 'post', // post提交方式
     data: formData, // 传数据和文件就得借助内置对象formData发送请求
     processData: false, // 告诉浏览器不对数据作任何处理
     contentType: false, // 不进行任何编码,formData数据对象Django后端可以识别
                // 接下来
     success:function (data) { // 注册成功,跳转到后端传来的指定页面
                    if (data.code==100) {location.replace(data.url)} // 注册失败,将后端传来的校验不成功的提示信息进行处理渲染到前端
                    // 这里须要注意一点:Django的form表单在前端渲染的时候会将每一个input框的id名按规律
                    // 定义名字,好比像这样:id_username、id_password
                    else { $.each( data.msg, function (index, obj) { // 注意:index obj 分别是:username ["用户名最小3位"]
                                // 和上面的方法不同,这个是将字典使用each方法,上面是将数组使用each方法
     let targetId = '#id_'+index; // 拼接出input框的id名
                                // 遍历出来的每一个字段名字,经过上面拼接的对应input框对应的id名,经过这个
                                // id名字就能找到它,它下面(经过next()方法)就是咱们须要处理的span标签
                                // 将它添加html文本(就是错误提示信息,注意obj是个列表),同时继续链式操做
                                // 让错误的input框标红框,警告提示,经过parent()方法找到父标签div,将其
                                // class加入一个has-error便可达到目的
     $(targetId).next().html(obj[0]).parent().addClass('has-error') } ) } } }) }); // 上面经过ajax发送post请求进行注册过程基本所有完成,最后还实现了错误信息的渲染,此时还需进行完善一下
        // 用户若是注册一次提示错误,继续进行注册的话,就须要再次清空错误提示,因此:
     $('input').on('focus',function () { $(this).next().html('').parent().removeClass('has-error') // 方法同上面加入错误信息相似,也是找到input框下面的span标签,将她的html置空,就去掉了红色提示
            // 同时继续找到其父标签div去掉class里面的has-error,红框提示取消
     }) </script>
    </body>
    </html>
    ## views.py视图函数
    from django.shortcuts import render,HttpResponse,redirect from app01.myform.myform import MyForm from app01 import models from django.http import JsonResponse # Create your views here.
    def register(request): form_obj = MyForm() # 先定义一个响应字典,后面注册请求来了返回时候要使用
        back_dic = {'code': 100, 'msg': ''} if request.method == 'POST': # 将register页面提交的普通数据获取到丢给MyForm(),实例化一个form_obj
            form_obj = MyForm(request.POST) # 经过form_obj获得Django自动校验提交表单处理结果
            if form_obj.is_valid(): # 校验经过,获取到经过的全部键值数据字典
                data = form_obj.cleaned_data # 这里须要注意,字典里面有个confirm_password的键值,这个键值在校验经过后是不须要的
                # 因此须要去掉:
                data.pop('confirm_password') # 获取用户上传的头像文件对象
                file_obj = request.FILES.get('myfile') # 这里须要加一层判断,看看用户在前端是否上传了头像文件,仍是用的默认头像
                # 传了就添加进data,没传就直接新增数据
                if file_obj: # 用户上传了头像文件,往data里添加头像文件的键值
                    data['avatar'] = file_obj # 注意要用create_user才能正常建立用户数据
                    # data是字典形式的键值对,直接打散传入,perfect!
                models.UserInfo.objects.create_user(**data) back_dic['msg'] = '注册成功'
                # 在相应数据字典里添加注册成功后跳转的页面路由
                back_dic['urs'] = '/login/'
            # 校验不经过,注册不成功
            else: back_dic['code'] = 101  # 用101表明注册失败
                # 将校验失败的信息字典传进去,等待前端渲染提示用户注册失败
                back_dic['msg'] = form_obj.errors return JsonResponse(back_dic) return render(request, 'register.html', locals())

     

相关文章
相关标签/搜索