BBS项目(仿博客园项目)

项目简介

使用python中 django框架开发类似博客园基本功能的小项目
技术:django框架、编程语言(python3.8)、前端基础(bootstarp3.4.1,jQuery3.5.1)、数据库(mysql5.6.44)

项目开发流程规范:

我们在前期ATM项目中已经介绍过项目开发流程了;
在此我们就简单的总结以下开发流程规范的大体步骤:
1.需求分析
	分析项目的具体需求
2.架构设计
	考虑项目需要使用的编程语言、框架、等等所需做的准备
3.分组开发
	分成小组对项目进行拆解,每组开发不同的功能板块
4.提交测试
	提交给测试部门进行一些测试,查缺补漏
5.交付上线
	上线项目,正常运行!
在本次BBS项目设计当中,我们就把以上的所有全都自己干了!

BBS项目流程

仿造博客园项目
	核心:'文章的增删改查'
表分析
	# 先确定表的数量 再确定表的基础字段 最后确定表的外键字段
		1.用户表
		2.个人站点表
		3.文章表
		4.文章分类表
		5.文章标签表
 		6.点赞点踩表
		7.文章评论表

创建django项目

使用pycharm进行django项目的创建

image

使用mysql创建项目所需数据库

image

创建static目录
将项目所需前端静态文件粘贴至static目录下

img是存放图片用的
就是这张了!
image

image

配置django中settings.py文件

我们创建好django项目后第一步应该去我们项目名称下的settings.py文件中进行一些配置!

BBS(仿博客园项目)-基于django框架详解(包含图文)-小白菜博客
BBS(仿博客园项目)-基于django框架详解(包含图文)-小白菜博客
image

下面让我们进行激动人心的时刻--表设计!

BBS表设计(重点!)

我们在开发项目的时候,最为重要的就是对表的分析以及设计
我们在上题已经将基础的表总结了下来,在这里我们需要对表进行更细致的分析设计

基础字段分析

'''下列表字段设计仅供参考 可以有更多的想法!!'''
1.用户表
	UserInfo
	我们对这张表使用django中auth模块给我们提供的表进行扩展
	替换auth_user表并扩展额外的字段
  	电话号码、头像、注册时间

image

2.个人站点表
    	站点名称(xiaoming\bob\sam)  个人站点就相当于是我们输入url 然后在url的后缀添加不同的路由跳转到不同的站点上https://www.cnblogs.com/'ddsuifeng/'
    	站点标题(努力奋斗去他妹的)   
		站点样式(css文件)

image

3.文章表
    	文章标题
    	文章简介
   		文章内容
    	发布时间

image

4.文章分类表
    	分类名称

image

5.文章标签表
    	标签名称

image

6.点赞点踩表:# 记录哪个用户给哪篇文章点了推荐(赞)还是反对(踩)
    	用户字段(用户主键)>>>:外键字段
 		文章字段(文章主键)>>>:外键字段
 		点赞点踩

image

7.文章评论表:# 记录哪个用户给哪篇文章评论了什么内容

    文章评论表:记录哪个用户给哪篇文章评论了什么内容
    用户字段(用户主键)>>>:外键字段 可以为空
    文章字段(文章主键)>>>:外键字段 可以为空
    评论内容
    评论时间
    外键字段(自关联)
    """
    id	user_id  article_id  content parent_id
    1     1          1       哈哈哈     null 如果自关联id为null就说明这条评论是由id为1的用户 评论了id为1的文章 内容为:哈哈哈
    2     2          1       哈你妹      1   这条评论是由id为2的用户  内容为:哈你妹
    3     3          1       讲文明      2
    """

image

外键字段

1.用户表
用户与个人站点是一对一外键关系

image

2.个人站点表
3.文章表
文章表与个人站点表是一对多外键关系
文章表与文章分类表是一对多外键关系
文章表与文章标签表是多对多外键关系
'''
		   数据库字段优化设计:我们想统计文章的评论数 点赞数
				通过文章数据跨表查询到文章评论表中对应的数据统计即可
		   但是文章需要频繁的展示 每次都跨表查询的话效率极低
				我们在文章表中再创建三个普通字段
		   之后只需要确保每次操作评论表或者点赞点踩表时同步修改上述三个普通字段即可
'''
文章评论数
文章点赞数
文章点踩数

image

文章表与文章标签的多对多关系(我们自己创建第三张表)

image

4.文章分类表
文章分类与个人站点是一对多外键关系

image

5.文章标签表
文章标签与个人站点是一对多外键关系

image

但是别忘记了跟数据库进行同步操作!
python3.8 manage.py makemigrations
python3.8 manage.py migrate

image

在pycharm中连接一下数据库看看效果!

BBS(仿博客园项目)-基于django框架详解(包含图文)-小白菜博客
需要点击apply才生效!
image

同步之后数据库应该如图所示

image

至此我们的表就设计完毕,开始编写接下来的注册功能部分!

注册功能

首先在BBS项目的同名文件下的urls进行路由配置!

image

views.py中编写视图函数

image

模板层创建html页面进行简单配置!

image

由于我们编写的是注册页面,需要使用许多输入框进行编写,那么我们就可以使用django中的forms组件

创建forms文件在app01项目下,命名myform
BBS(仿博客园项目)-基于django框架详解(包含图文)-小白菜博客
myform文件代码

from django import forms
from app01 import models


class RegisterForm(forms.Form):
    username = forms.CharField(min_length=5, max_length=8, label='用户名', error_messages=
    {
        'min_length': '用户名最少为5位!',
        'max_length': '用户名最长8位',
        'required': '用户名不能为空'
    }, widget=forms.widgets.TextInput(attrs={
        'class': 'form-control'
    }))
    password = forms.CharField(min_length=5, max_length=8, label='密码', error_messages=
    {
        'min_length': '密码最少为5位!',
        'max_length': '密码最长8位',
        'required': '密码不能为空'
    }, widget=forms.widgets.TextInput(attrs={
        'class': 'form-control'
    }))
    confirm_password = forms.CharField(min_length=5, max_length=8, label='确认密码', error_messages=
    {
        'min_length': '密码最少为5位!',
        'max_length': '密码最长8位',
        'required': '确认密码不能为空'
    }, widget=forms.widgets.TextInput(attrs={
        'class': 'form-control'
    }))
    email = forms.EmailField(label='邮箱',
                             error_messages={'invalid': '邮箱格式不正确!',
                                             'required': '邮箱填写不能为空'},
                             widget=forms.widgets.EmailInput(attrs={
                                 'class': 'form-control'}))

    # 局部钩子
    # 判断用户名是否已存在!
    def clean_username(self):
        # 从过滤过的数据中拿到username
        username = self.cleaned_data.get('username')
        # 判断username数据是否在数据库中重复
        user_obj = models.UserInfo.objects.filter(username=username)
        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

image

编写前端register.html代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    {% load static %}
    <script src="{% static 'jquery-3.5.1/jquery-3.5.1.min.js' %}"></script>
    <link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
    <script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
</head>
<body>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <h2 class="text-center">注册界面</h2>
            <form id="form">
            <div class="form-group">
            		{% csrf_token %}  # 需要添加这个csrf,这个参数传递给后端,不然会出现403forbidden错误码
                    {% for form in form_obj %}
                        <label for="{{ form.auto_id }}">
                        {{ form.label }}
                        </label>
                        {{ form }}
                        <span style="color: red" class="pull-right"></span>
                    {% endfor %}
            </div>
            <div class="form-group">
                <label for="myfile">头像
                        <img src="/static/img/default.jpg" alt="" width="120" id="myimg">
                        <input type="file" style="display: none" id="myfile">
                </label>
            </div>
            <input type="button" id="submitBtn" class="btn btn-success btn-block" value="注册">
        </form>
        </div>
    </div>
</div>
<script>
    // 1.更换头像动态展示
    $('#myfile').change(function () {
        // 1.产生一个文件阅读器对象
        let myFileReaderObj = new FileReader()
        // 2.获取用户上传的头像文件
        let fileObj = this.files[0]
        // 3.将文件对象传递给文件阅读器对象读取
        myFileReaderObj.readAsDataURL(fileObj)  // 这是异步操作,所以需要让这行代码先进行加载,在进行img标签的src属性修改展示
        myFileReaderObj.onload = function (){
                    // 4.修改img标签的src属性并展示图片
        $('#myimg').attr('src',myFileReaderObj.result)
        }
    })
    // 2.给注册按钮绑定点击事件 发送ajax  携带了文件数据
    $('#submitBtn').click(function (){
        // 1.产生一个内置文件对象
        let myFormDataObj = new FormData();
        // 2.添加普通数据(单个单个的别写效率很低!)
        $.each($('#form').serializeArray(),function (index,dataObj) {
            myFormDataObj.append(dataObj.name,dataObj.value) //{'name':'','value':''}
        })
        // 3.添加文件数据
        myFormDataObj.append('avatar',$('#myfile')[0].files[0])
        $.ajax({
            url:'',
            type:'post',
            data:myFormDataObj,
            contentType:false,
            processData:false,

            success:function (args) {
                 if(args.code === 888){
                        window.location.href = args.url
            }else {
                     let dataObj = args.msg;
                     $.each(dataObj, function (k, msgArray) {
                         // 拼接标签的id值
                         let eleId = '#id_' + k
                         // 根据id查找标签 修改下面span标签的内容 并给父标签添加错误样式
                         $(eleId).next().text(msgArray[0]).parent().addClass('has-error')
                     })
                 }
                 // 3.给所有的input标签绑定获取焦点事件 移除错误样式
                $('input').focus(function () {
                    $(this).next().text('').parent().removeClass('has-error')
                })
            }
        })
    })
</script>
</body>
</html>

views中 register视图函数代码

def register(request):
    back_dict = {'msg': '', 'code': 888}
    form_obj = myform.RegisterForm()
    if request.method == 'POST': # 判断请求方法
        form_obj = myform.RegisterForm(request.POST)
        if form_obj.is_valid(): # 判断获取数据是否合法
            clean_data = form_obj.cleaned_data
            clean_data.pop('confirm_password')
            avatar_obj = request.FILES.get('avatar')
            if avatar_obj:
                clean_data['avatar'] = avatar_obj
            models.UserInfo.objects.create_user(**clean_data)
            back_dict['msg'] = '注册成功'
            back_dict['url'] = '/login/'  # 跳转到登录页面
        else:
            back_dict['code'] = 1000  # 如果不合法的话就需要返回 错误信息!
            back_dict['msg'] = form_obj.errors
        return JsonResponse(back_dict)

    return render(request, 'register.html', locals())