摘要:

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

一、需求分析:

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

二、项目分析:

表设计:

  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)

  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 viewsurlpatterns = [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_passwordelse {$.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_objform_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.errorsreturn JsonResponse(back_dic)return render(request, 'register.html', locals())

转载于:https://www.cnblogs.com/suguangti/p/11055666.html

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

  1. BBS(仿博客园系统)项目03(主页搭建、个人站点搭建(侧边栏分类展示、标签展示、日期归档)、文章详情页相关功能实现)...

    摘要: 主页面的搭建(导航条下面的区域) 个人站点 侧边栏分类展示 侧边栏标签展示 侧边栏日期归档 文章详情页 文章内容 文章点赞点踩 文章评论 一.主页面home.html的搭建(进一步完善) ho ...

  2. python3进阶开发-第一个仿博客园的项目(1)

    首先我们要设计一下表结构: UserInfo(用户信息表) -------->一对一    ----------->Blog(博客信息表) UserInfo(用户信息表) -------- ...

  3. 博客园php教程,PHP仿博客园,个人博客(1)_PHP教程

    本人本科学历,自学PHP大半年多了,断断续续地,但是最终还是坚定了我的想法,将PHP继续下去,所以写这个PHP的博客是为了找个稳定的 PHP工作,不求工资多高,但求一收留之地.我能看懂大部分英语文档, ...

  4. BBS+ BLOG系统(仿博客园)

    一.基本要求 作业题目:开发BBS+BLOG系统 作业需求: 1 基于ajax和用户认证组件实现登录验证 2 基于ajax和form组件实现注册功能 3 系统首页文章列表的渲染 4 个人站点页面设计5 ...

  5. python3-开发进阶-仿博客园项目setting.py的文件的配置,admin,forms(2)

    前面我们先分析了一下,做这个项目需要的几张表,今天我们从配置文件开始一步一步去解释这么的原因 首先先来看setting.py文件: """ Django settings ...

  6. [Tool] 仿博客园插入代码的 WLW 插件

    一  插件相关效果图展示 插件效果图: 在 Windows Live Writer 中的效果如图: [1] [2] [3] 对应的插入代码在博客中的效果如下: [1] public static vo ...

  7. 仿博客园个人博客(3)基本完成

    唠叨几句: 前段时间找了家公司上班,也算是真正跨入了计算机行业了,可惜地是我们这城市太小,没什么PHP工作机会,少数地几家都是做网络营销型的,我几乎相当于美工!可我不会啊,但是他们招聘条件上说了: 要 ...

  8. mysql项目酒店管理博客园_项目中常用的19条MySQL优化

    在写文章之前,首先感谢 飞友科技 陆老师提供的文档.. 声明一下:下面的优化方案都是基于 " Mysql-索引-BTree类型 " 的 一.EXPLAIN 做MySQL优化,我们要 ...

  9. Asp.net MVC 仿照博客园的简单网站首页 列表设计

    本来我打算采用ajax提交请求,异步的请求获取数据,但是我发现如果这样的话就会拖慢开发的进度,拖长时间.所以在这篇博客中仿照首页的列表设计其实和左侧列表网站分类采用了同样的方式,通过局部视图的方式呈现 ...

最新文章

  1. JavaScript继承的多种方式和优缺点
  2. 黑马程序员——集合框架(二)
  3. 超级vga显示卡_VGA 显示卡简介
  4. Consul 入门指南
  5. 判断 iframe 是否加载完成的完美方法
  6. Springboot 请求数据
  7. Chrome 如何让光标快速定位到地址栏-进行搜索
  8. 查看linux系统的sp,查看linux系统版本命令
  9. 532 -数组中的K-diff对
  10. 著名程序员刘涛涛WinMount程序BUG
  11. JUNIT Hello World
  12. bat shell 命令行中 21 的含义
  13. HTML5写的简单小游戏-绵羊快跑
  14. python花瓣网图片_python制作花瓣网美女图片爬虫
  15. Aria2+yaaw+Chrome插件BaiduExporter实现百度网盘下载
  16. leetcode——第714题——可以买卖多次股票(每次有手续费)
  17. 联机/中断网络磁盘驱动器对话框
  18. Unit Test Harness(用具)应该具备什么功能?
  19. 基于niua框架的Excel的导出
  20. DEV C++编写程序出现 [errror]Id returned 1 exit status错误可能出现的原因

热门文章

  1. 逼急了自己人都坑,腾讯内部上演吃鸡大战
  2. 有关javabean的说法不正确的是_7、关于JavaBean,下列叙述中不正确的是
  3. 临键锁如何实现幻读_如何实现智能锁客户裂变?看完你就已成功一半!
  4. 基于python的证件照_用20行python代码给证件照换底色
  5. mysql数据库服务器实例_服务器上运行一个mysql实例里有多个数据库呢?还是多MYSQL实例?...
  6. onestage方法_FCOS : 找到诀窍了,anchor-free的one-stage目标检测算法也可以很准 | ICCV 2019...
  7. kibana服务器性能要求,Kibana停止工作,尽管kibana.service正常启动,但服务器未准备就绪...
  8. getHandel redis_Controller层利用Redis实现分布式锁(注解实现)
  9. rhel6上使用udev配置oracle asm,在RHEL6上用UDEV配置ASM
  10. java 字节流写入_Java之基于字节流文件的写入和读取