Django的bbs项目开发

Posted tornadoes-destroy-parking-lots

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Django的bbs项目开发相关的知识,希望对你有一定的参考价值。

一、表设计

1.用户表

技术图片
继承AbstractUser
扩展
phone 电话号码
avatar 用户头像
create_time 创建时间

外键字段
一对一个人站点表
技术图片

 

2.个人站点表

site_name 站点名称
site_title 站点标题
site_theme    站点样式

 

3.文章标签表

name    标签名

外键字段
一对多个人站点


4.文章分类表

name    分类名

外键字段
一对多个人站点


5.文章表

title    文章标题
desc    文章简介
content    文章内容
create_time 发布时间

 


数据库字段设计优化(******)

(虽然下述的三个字段可以从其他表里面跨表查询计算得出,但是频繁跨表效率)
up_num 点赞数
down_num 点踩数
comment_num 评论数

外键字段
一对多个人站点
多对多文章标签
一对多文章分类

6.点赞点踩表

记录哪个用户给哪篇文章点了赞还是点了踩
user    ForeignKey(to="User")    
article    ForeignKey(to="Article")    
is_up    BooleanField()

7.文章评论表

技术图片
记录哪个用户给哪篇文章写了哪些评论内容
user    ForeignKey(to="User")    
article    ForeignKey(to="Article")
content    CharField()
comment_time    DateField()
# 自关联
parent    ForeignKey(to="Comment",null=True)    
# ORM专门提供的自关联写法    
parent    ForeignKey(to="self",null=True)
7.评论表

根评论和子评论
to=self
技术图片

技术图片

 

 modles.py

技术图片
from django.contrib.auth.models import AbstractUser


class UserInfo(AbstractUser):
    phone = models.BigIntegerField(verbose_name=手机号,null=True)
    # 头像
    avatar = models.FileField(upload_to=avatar/,default=avatar/default.png,verbose_name=用户头像)
    """
    给avatar字段传文件对象 该文件会自动存储到avatar文件下 然后avatar字段只保存文件路径avatar/default.png
    """
    create_time = models.DateField(auto_now_add=True)

    blog = models.OneToOneField(to=Blog,null=True)


class Blog(models.Model):
    site_name = models.CharField(verbose_name=站点名称,max_length=32)
    site_title = models.CharField(verbose_name=站点标题,max_length=32)
    # 简单模拟 带你认识样式内部原理的操作
    site_theme = models.CharField(verbose_name=站点样式,max_length=64)  # 存css/js的文件路径


class Category(models.Model):
    name = models.CharField(verbose_name=文章分类,max_length=32)
    blog = models.ForeignKey(to=Blog,null=True)


class Tag(models.Model):
    name = models.CharField(verbose_name=文章标签,max_length=32)
    blog = models.ForeignKey(to=Blog, null=True)


class Article(models.Model):
    title = models.CharField(verbose_name=文章标题,max_length=64)
    desc = models.CharField(verbose_name=文章简介,max_length=255)
    # 文章内容有很多 一般情况下都是使用TextField
    content = models.TextField(verbose_name=文章内容)
    create_time = models.DateField(auto_now_add=True)

    # 数据库字段设计优化
    up_num = models.BigIntegerField(verbose_name=点赞数,default=0)
    down_num = models.BigIntegerField(verbose_name=点踩数,default=0)
    comment_num = models.BigIntegerField(verbose_name=评论数,default=0)

    # 外键字段
    blog = models.ForeignKey(to=Blog, null=True)
    category = models.ForeignKey(to=Category,null=True)
    tags = 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 UpAndDown(models.Model):
    user = models.ForeignKey(to=UserInfo)
    article = models.ForeignKey(to=Article)
    is_up = models.BooleanField()  # 传布尔值 存0/1


class Comment(models.Model):
    user = models.ForeignKey(to=UserInfo)
    article = models.ForeignKey(to=Article)
    content = models.CharField(verbose_name=评论内容,max_length=255)
    comment_time = models.DateTimeField(verbose_name=评论时间,auto_now_add=True)
    # 自关联
    parent = models.ForeignKey(to=self,null=True)  # 有些评论就是根评论
技术图片

 

二、注册功能

注册功能用到的技术点有以下

  • forms组件

  • 用户头像前端实时展示

  • ajax

我们之前写forms代码是直接写到views里面的 

为了贯彻面向对象思想,我们会把forms组件代码写到单独的一个py文件里,如果说这个项目从头到位就使用一个form组件,直接写在app下面的任意一个自己写的文件里就行,不过记得不要叫forms,这样在导入Django库里的form时会出现问题,可以叫myforms.py或者其他,只要补交forms就行

如果你项目中用到多个不同的forms组件时,那最好还是自己建一个文件夹,然后把对应的forms组件写上对应的名字,然后放在这个文件夹下面,这样也比较清晰

myforms.py

技术图片
from django import forms
from app01 import models


class MyRegForm(forms.Form):
    username = forms.CharField(label=用户名, min_length=3, max_length=8,
                               error_messages={
                                   required: 用户名不能为空,
                                   min_length: "用户名最少3位",
                                   max_length: "用户名最大8位"
                               },
                               # 还需要让标签有bootstrap样式
                               widget=forms.widgets.TextInput(attrs={class: form-control})
                               )

    password = forms.CharField(label=密码, min_length=3, max_length=8,
                               error_messages={
                                   required: 密码不能为空,
                                   min_length: "密码最少3位",
                                   max_length: "密码最大8位"
                               },
                               # 还需要让标签有bootstrap样式
                               widget=forms.widgets.PasswordInput(attrs={class: form-control})
                               )

    confirm_password = forms.CharField(label=确认密码, min_length=3, max_length=8,
                                       error_messages={
                                           required: 确认密码不能为空,
                                           min_length: "确认密码最少3位",
                                           max_length: "确认密码最大8位"
                                       },
                                       # 还需要让标签有bootstrap样式
                                       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_username(self):
        username = self.cleaned_data.get(username)
        # 去数据库中校验
        is_exist = models.UserInfo.objects.filter(username=username)
        if is_exist:
            # 提示信息
            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
技术图片

views.py的register视图函数

技术图片
def register(request):
    form_obj = MyRegForm()
    if request.method == POST:
        back_dic = {"code": 1000, msg: ‘‘}
        # 校验数据是否合法
        form_obj = MyRegForm(request.POST)
        # 判断数据是否合法
        if form_obj.is_valid():
            # print(form_obj.cleaned_data)  # {‘username‘: ‘jason‘, ‘password‘: ‘123‘, ‘confirm_password‘: ‘123‘, ‘email‘: ‘123@qq.com‘}
            clean_data = form_obj.cleaned_data  # 将校验通过的数据字典赋值给一个变量
            # 将字典里面的confirm_password键值对删除
            clean_data.pop(confirm_password)  # {‘username‘: ‘jason‘, ‘password‘: ‘123‘, ‘email‘: ‘123@qq.com‘}
            # 用户头像
            file_obj = request.FILES.get(avatar)
            """针对用户头像一定要判断是否传值 不能直接添加到字典里面去"""
            if file_obj:
                clean_data[avatar] = file_obj
            # 直接操作数据库保存数据
            models.UserInfo.objects.create_user(**clean_data)
            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

技术图片
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
    <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <h1 class="text-center">注册</h1>
            <form id="myform">  <!--这里我们不用form表单提交数据 知识单纯的用一下form标签而已-->
                {% csrf_token %}
                {% for form in form_obj %}
                    <div class="form-group">
                        <label for="{{ form.auto_id }}">{{ form.label }}</label>
                        {{ form }}
                        <span style="color: red" class="pull-right"></span>
                    </div>
                {% endfor %}
                <div class="form-group">
                    <label for="myfile">头像
                        {% load static %}
                        <img src="{% static ‘img/default.png‘ %}" id=‘myimg‘ alt="" width="100" style="margin-left: 10px">
                    </label>
                    <input type="file" id="myfile" name="avatar" style="display: none" >
                </div>

                <input type="button" class="btn btn-primary pull-right" value="注册" id="id_commit">
            </form>
        </div>
    </div>
</div>

<script>
    $("#myfile").change(function () {
        // 文件阅读器对象
        // 1 先生成一个文件阅读器对象
        let myFileReaderObj = new FileReader();
        // 2 获取用户上传的头像文件
        let fileObj = $(this)[0].files[0];
        // 3 将文件对象交给阅读器对象读取
        myFileReaderObj.readAsDataURL(fileObj)  // 异步操作  IO操作
        // 4 利用文件阅读器将文件展示到前端页面  修改src属性
        // 等待文件阅读器加载完毕之后再执行
        myFileReaderObj.onload = function(){
             $(#myimg).attr(src,myFileReaderObj.result)
        }
    })

    $(#id_commit).click(function () {
        // 发送ajax请求     我们发送的数据中即包含普通的键值也包含文件
        let formDataObj = new FormData();
        // 1.添加普通的键值对
        {#console.log($(#myform).serializeArray())  // [{},{},{},{},{}]  只包含普通键值对#}
        $.each($(#myform).serializeArray(),function (index,obj) {
            {#console.log(index,obj)#}  // obj = {}
            formDataObj.append(obj.name,obj.value)
        });
        // 2.添加文件数据
        formDataObj.append(avatar,$(#myfile)[0].files[0]);

        // 3.发送ajax请求
        $.ajax({
            url:"",
            type:post,
            data:formDataObj,

            // 需要指定两个关键性的参数
            contentType:false,
            processData:false,

            success:function (args) {
                if (args.code==1000){
                    // 跳转到登陆页面
                    window.location.href = args.url
                }else{
                    // 如何将对应的错误提示展示到对应的input框下面
                    // forms组件渲染的标签的id值都是 id_字段名
                    $.each(args.msg,function (index,obj) {
                        {#console.log(index,obj)  //  username        ["用户名不能为空"]#}
                        let targetId = #id_ + index;
                        $(targetId).next().text(obj[0]).parent().addClass(has-error)
                    })
                }
            }
        })
    })
    // 给所有的input框绑定获取焦点事件
    $(input).focus(function () {
        // 将input下面的span标签和input外面的div标签修改内容及属性
        $(this).next().text(‘‘).parent().removeClass(has-error)
    })
</script>
</body>
</html>
技术图片

三、登录功能 

登录功能的技术点有以下

  • 实现图片验证码

  • ajax

以上是关于Django的bbs项目开发的主要内容,如果未能解决你的问题,请参考以下文章

Django:之BBS项目

Django小项目简单BBS论坛

基于Django开发一个BBS案例

Django之BBS博客项目

Django学习_BBS开发

django框架之BBS项目之评论功能