首页 > 其他分享 >BBS(仿博客园项目)

BBS(仿博客园项目)

时间:2023-01-02 22:46:25浏览次数:29  
标签:obj name 项目 models 博客园 length 文章 BBS verbose

BBS(仿造博客园项目)

项目开发基本流程

1.需求分析
2.架构设计
3.分组开发
4.提交测试
5.交付上线

项目流程

仿造博客园项目
	核心:文章的增删改查
表分析
	先确定表的数量 再确定表的基础字段 最后确定表的外键字段
		1.用户表
		2.个人站点表
		3.文章表
		4.文章分类表
		5.文章标签表
 		6.点赞点踩表
		7.文章评论表
基础字段分析
	'''下列表字段设计仅供参考 你可以有更多的想法'''
	用户表
		替换auth_user表并扩展额外的字段
  		 	电话号码、头像、注册时间
	个人站点表
    	站点名称(jason\lili\kevin)
    	站点标题(努力奋斗去他妹的)
		站点样式(css文件)
	文章表
    	文章标题
    	文章简介
   		文章内容
    	发布时间
 	文章分类表
    	分类名称
	文章标签表
    	标签名称
	点赞点踩表:记录哪个用户给哪篇文章点了推荐(赞)还是反对(踩)
    	用户字段(用户主键)>>>:外键字段
 		文章字段(文章主键)>>>:外键字段
 		点赞点踩
	文章评论表:记录哪个用户给哪篇文章评论了什么内容
   		用户字段(用户主键)>>>:外键字段
 		文章字段(文章主键)>>>:外键字段
       评论内容
   		评论时间
    	外键字段(自关联)
		"""
		id	user_id  article_id  content parent_id
		1    1      1       哈哈哈   null
		2   2      1        哈你妹   1
		3   3      1        讲文明   2
		"""  
    评论点赞点踩表:记录哪个用户给那篇文章的哪条评论点赞或点踩 
   		用户字段(用户主键)>>>:外键字段
 		文章字段(文章主键)>>>:外键字段
        评论字段(评论主键)>>>:外键字段
 		点赞点踩
外键字段
	用户表
		用户与个人站点是一对一外键关系
        
	个人站点表
    	
	文章表
    	文章表与个人站点表是一对多外键关系
   		文章表与文章分类表是一对多外键关系
    	文章表与文章标签表是多对多外键关系
       '''
       数据库字段优化设计:我们想统计文章的评论数 点赞数
       		通过文章数据跨表查询到文章评论表中对应的数据统计即可
       但是文章需要频繁的展示 每次都跨表查询的话效率极低
       		我们在文章表中再创建三个普通字段
       之后只需要确保每次操作评论表或者点赞点踩表时同步修改上述三			个普通字段即可
       '''
    	文章评论数
       文章点赞数
    	文章点踩数
       
    	
 	文章分类表
    	文章分类与个人站点是一对多外键关系
        
	文章标签表
    	文章标签与个人站点是一对多外键关系

image

注册功能

用户注册 
	1.渲染前端标签
 	2.校验用户数据
	3.展示错误提示
ps:forms组件、modelform组件
  
单独开设py文件编写 解耦合!!!

登录功能

img标签的src属性
	1.可以直接填写图片地址
	2.还可以填写一个路由 会自动朝该路由发送get请求
    如果结果是图片的二进制数据 那么自动渲染图片
    
    
pip install pillow -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com

创建表相关代码

from django.db import models

# Create your models here.
from django.contrib.auth.models import AbstractUser


class UserInfo(AbstractUser):
    """用户表,进行扩展,自定义字段"""
    phone = models.CharField(max_length=32, verbose_name='手机号', null=True)
    avatar = models.FileField(upload_to='avatar/', default='avatar/default.png', verbose_name='头像')
    register_time = models.DateTimeField(auto_now_add=True, verbose_name='注册时间')

    """外键字段"""
    """用户表与个人站点表一对一关系"""
    site = models.OneToOneField(to='Site', on_delete=models.CASCADE, null=True)


class Site(models.Model):
    """个人站点表"""
    site_name = models.CharField(max_length=32, verbose_name='站点名称')
    site_title = models.CharField(max_length=32, verbose_name='站点标题')
    site_theme_css = models.TextField(verbose_name='站点css', null=True)
    site_theme_js = models.TextField(verbose_name='站点js', null=True)
    site_theme_html = models.TextField(verbose_name='站点html', null=True)
    site_publish_info = models.TextField(verbose_name='公告', null=True)


class Article(models.Model):
    """文章表"""
    title = models.CharField(max_length=32, verbose_name='文章标题')
    summary = models.TextField(verbose_name='文章摘要')
    content = models.TextField(verbose_name='文章内容')
    publist_time = models.DateTimeField(auto_now_add=True, verbose_name='文章发布时间')

    """数据库字段优化"""
    """因为经常要统计下面字段数量,虽然跨表可以查询,但是浪费数据库资源,所有在进行数据增加或删除数据,对下面字段进行数据的修改
    以达到,减少数据库资源消耗,而进行优化的目的
    """
    up_num = models.IntegerField(verbose_name='点赞数', default=0)
    down_num = models.IntegerField(verbose_name='点踩数', default=0)
    comment_num = models.IntegerField(verbose_name='评论数', default=0)

    """外键字段"""
    site = models.ForeignKey(to='Site', on_delete=models.CASCADE, null=True)
    classify = models.ForeignKey(to='Classify', on_delete=models.CASCADE, null=True)

    """多对多字段,一个文章可以有很多标签,一个标签页可以有很多文章
    采用半自动创建多对多
    """
    labels = models.ManyToManyField(to='Label',
                                    through='Article2Label',
                                    through_fields=('article', 'label'))


class Article2Label(models.Model):
    """多对多手动第三张表,后期可以扩展字段"""
    article = models.ForeignKey(to='Article', on_delete=models.CASCADE, null=True)
    label = models.ForeignKey(to='Label', on_delete=models.CASCADE, null=True)


class Classify(models.Model):
    """文章分类表"""
    name = models.CharField(max_length=32, verbose_name='分类名称')
    site = models.ForeignKey(to='Site', on_delete=models.CASCADE, null=True)


class Label(models.Model):
    """文章标签表"""
    name = models.CharField(max_length=32, verbose_name='文章标签表')
    site = models.ForeignKey(to='Site', on_delete=models.CASCADE, null=True)


class UpAndDownArticle(models.Model):
    """文章点赞点踩表"""
    """记录:哪个用户给那篇文章点赞或点踩"""
    user = models.ForeignKey(to='UserInfo', on_delete=models.CASCADE)  # 外键字段关联userinfo表主键
    article = models.ForeignKey(to='Article', on_delete=models.CASCADE)  # 外键字段
    is_up = models.BooleanField(verbose_name='点赞点踩')  # 写True False 存 1 0


class Comment(models.Model):
    """文章评论表"""
    """记录:哪个用户给那片文章评论的内容与时间"""
    user = models.ForeignKey(to='UserInfo', on_delete=models.CASCADE)
    article = models.ForeignKey(to='Article', on_delete=models.CASCADE)
    content = models.TextField(verbose_name='评论内容')
    comment_time = models.DateTimeField(auto_now_add=True, verbose_name='评论时间')

    """评论显示什么浏览器,以及评论者的ip"""
    user_agent = models.CharField(max_length=64, verbose_name='用户评论浏览器')
    user_ip = models.TextField(verbose_name='用户ip/ipv4/ipv6')

    """自关联字段"""
    """在有时侯,某些字段需要关联所在表的数据就需要使用到自关联字段
    id  user    content     parent
    1   1       哈哈哈       null
    2   2       不要         1
    3   3       你怎么管这么多 2
    """
    parent = models.ForeignKey(to='self', on_delete=models.CASCADE, null=True)

    """数据库字段优化"""
    up_num = models.IntegerField(verbose_name='评论被点赞数', default=0)
    down_num = models.IntegerField(verbose_name='评论被点踩', default=0)


class UpAndDownComment(models.Model):
    """评论点赞点踩表"""
    """记录:哪个用户给那篇文章里的哪个评论点赞或点踩"""
    user = models.ForeignKey(to='UserInfo', on_delete=models.CASCADE)  # 外键字段关联userinfo表主键
    article = models.ForeignKey(to='Article', on_delete=models.CASCADE)  # 外键字段
    comment = models.ForeignKey(to='Comment', on_delete=models.CASCADE)
    is_up = models.BooleanField(verbose_name='点赞点踩')  # 写True False 存 1 0

注册功能相关代码

注册form组件

from django import forms
from django.forms import widgets

from BBS import models


class Register_from(forms.Form):
    """用户注册forms类"""
    # validators正则匹配校验用的 required内容为空用的
    username = forms.CharField(max_length=16, min_length=6, label='用户名',
                               error_messages={
                                   'max_length': '用户名最长为16位',
                                   'min_length': '用户名最短为6位',
                                   'required': '用户名不能为空'
                               },
                               widget=widgets.TextInput(attrs={'class': 'form-control'})
                               )
    password = forms.CharField(max_length=16, min_length=6, label='密码',
                               error_messages={
                                   'max_length': '密码最长为16位',
                                   'min_length': '密码最短为6位',
                                   'required': '密码不能为空'
                               },
                               widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
    confirm_password = forms.CharField(max_length=16, min_length=6, label='确认密码',
                                       error_messages={
                                           'max_length': '密码最长为16位',
                                           'min_length': '密码最短为6位',
                                           'required': '密码不能为空'
                                       },
                                       widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
    email = forms.EmailField(label='邮箱', error_messages={
        'required': '邮箱不能为空'
    },
                              widget=widgets.EmailInput(attrs={'class': 'form-control'})
                              )

    def clean_username(self):
        """局部钩子校验用户名是否已存在"""
        username = self.cleaned_data.get('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 password != confirm_password:
            self.add_error('confirm_password', '两次密码不一致')

        return self.cleaned_data

注册视图类相关代码

项目使用模块

from django.shortcuts import render, HttpResponse, redirect, reverse
from django.http import JsonResponse

# Create your views here.
from django.views import View
from BBS.myforms.register import Register_from

from django.contrib import auth  # 校验模块
from django.contrib.auth.decorators import login_required  # 登录装饰器
from django.views.decorators.csrf import csrf_exempt, csrf_protect  # 取消或者校验csrf
from django.utils.decorators import method_decorator  # 给视图类添加装饰器用的
from django.db.models import Q, F  # 跟查询相关

后端代码

class Register_class(View):
    """用户注册视图类"""

    def get(self, request):
        form_obj = Register_from()
        return render(request, 'bbs/registerPage.html', locals())

    def post(self, request):
        re_dict = {
            'code': 10000,
            'msg': ''
        }
        print(request.POST, request.FILES)
        form_obj = Register_from(request.POST)
        if form_obj.is_valid():
            clean_data = form_obj.cleaned_data
            clean_data.pop('confirm_password')
            file_obj = request.FILES.get('avatar')
            if file_obj:
                clean_data['avatar'] = file_obj
            models.UserInfo.objects.create_user(**clean_data)
            re_dict['url'] = reverse('login_url')
        else:
            re_dict['code'] = 10001
            re_dict['msg'] = form_obj.errors
        return JsonResponse(re_dict)

html代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.js"></script>
    {% load static %}
    <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'>
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h1 class="text-center h1">用户注册</h1>
            <form id="form">
                {% csrf_token %}
                {% for form in form_obj %}
                    <div class="form-group">
                        <label for="{{ form.auto_id }}">{{ form.label }}</label>
                        {{ form }}
                        <span class="pull-right" style="color: red"></span>
                    </div>
                {% endfor %}
                <div class="form-group">
                    <label for="myfile">头像 <img src="{% static 'img/default.png' %}" alt="" id="myimg"
                                                width="100"></label>
                    <input type="file" id="myfile" style="display: none">
                </div>
                <input type="button" id="mybtn" class="btn btn-block btn-success" value="注册">
            </form>
        </div>
    </div>
</div>
<script>
    $('#myfile').change(function () {
        let redfile_obj = new FileReader()
        redfile_obj.readAsDataURL(this.files[0])
        redfile_obj.onload = function () {
            $('#myimg').attr('src', redfile_obj.result)
        }
    })
    $('#mybtn').click(function () {
        let formdata_obj = new FormData()

        let file_obj = $('#myfile')[0].files[0]
        formdata_obj.append('avatar', file_obj)
        $.each($('#form').serializeArray(), function (index, data_obj) {
            formdata_obj.append(data_obj.name, data_obj.value)
        })
        console.log(formdata_obj)

        $.ajax({
            url: '',
            type: 'post',
            data: formdata_obj,

            //取消属性与数据
            contentType: false,
            processData: false,
            success: function (arg) {
                if (arg.code === 10000) {
                    window.location.href = arg.url
                } else {
                    $.each(arg.msg, function (name, value) {
                        let id_name = '#id_' + name
                        $(id_name).next().text(value).parent().addClass('has-error')
                    })
                }
            }
        })


    })
    $('input').click(function () {
        $(this).next().text('').parent().removeClass('has-error')
    })

</script>


</body>
</html>

登录功能

后端代码

def login_func(request):
    """用户登录功能"""
    if request.method == 'POST':
        re_dict = {
            'code': 10000,
            'msg': ''
        }
        captcha: str = request.POST.get('captcha')
        username = request.POST.get('username')
        password = request.POST.get('password')
        if request.session.get('captcha').lower() == captcha.lower():
            user_obj = auth.authenticate(request, username=username, password=password)
            if user_obj:
                auth.login(request, user_obj)
                re_dict['url'] = reverse('home_url')
            else:
                re_dict['code'] = 10001
                re_dict['msg'] = {
                    'password': '用户名或密码错误'
                }
        else:
            re_dict['code'] = 10002
            re_dict['msg'] = {
                'captcha': '验证码错误',
            }
            if not username:
                re_dict['msg']['username'] = '用户名不能为空'
            if not password:
                re_dict['msg']['password'] = '密码不能为空'
        return JsonResponse(re_dict)
    return render(request, 'bbs/loginPage.html', locals())

前端代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.js"></script>
    {% load static %}
    <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'>
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <h1 class="text-center h1">用户登录</h1>
            <form id="form">
                {% csrf_token %}
                <div class="form-group">
                    <label for="username">用户名</label>
                    <input type="text" name="username" id="username" class="form-control">
                    <span class="pull-right" style="color: red"></span>
                </div>
                <div class="form-group">
                    <label for="password">密码</label>
                    <input type="text" name="password" id="password" class="form-control">
                    <span class="pull-right" style="color: red"></span>
                </div>
                <div class="form-group">
                    <label for="captcha">验证码 </label>
                    <div class="row">
                        <div class="col-md-6">
                            <input type="text" name="captcha" class="form-control" id="captcha">
                            <span class="pull-right" style="color: red"></span>
                        </div>
                        <div class="col-md-6" id="div_captcha" onclick="$('#captcha_img').attr('src','{% url 'captcha_url' %}?'+Math.random())"><img src="{% url 'captcha_url' %}" alt="" width="260" height="35" id="captcha_img">
                            <p class="pull-right btn-link"><a href="javascript:void(0);"
                                                              id="change">看不清?点击换一张</a></p>
                        </div>

                    </div>
                </div>
                <input type="button" id="loginBtn" class="btn btn-block btn-success" value="登录">
            </form>
        </div>
    </div>
</div>
<script>
    $('#loginBtn').click(function () {
        $.ajax({
            url: '',
            type: 'post',
            dataType: 'json',
            data: $('#form').serializeArray(),
            success: function (args) {
                console.log(args)
                if (args.code === 10000) {
                    window.location.href = args.url
                } else{
                    $('#div_captcha').click()
                    $.each(args.msg,function (name,value) {
                        let id_name = '#'+name
                        $(id_name).next().text(value).parent().addClass('has-error')
                    })
                }


            }
        })
    })
    $('input').click(function () {
        $(this).next().text('').parent().removeClass('has-error')
    })
</script>
</body>
</html>

验证码功能代码

后端代码

from PIL import Image, ImageFont, ImageDraw
from io import BytesIO, StringIO  # StringIO字符串内存管理对象  BytesIO字节内存管理对象
import random


def get_random_color():
    """获取随机rgb颜色"""
    return random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)


def captcha_func(request):
    """生成验证码存储到session中,返回验证码图片"""
    imgobj = Image.new('RGB', (260, 35), get_random_color())  # 生成一个背景图片
    imgfont = ImageFont.truetype(r'static/font/云峰静龙行书.ttf', size=32)  # 生成字体
    imgdraw = ImageDraw.ImageDraw(imgobj)  # 生成一个画笔对象
    code = ''
    for num in range(5):
        """剩余5位数验证吗"""
        choice_big = chr(random.randint(65, 90))
        choice_small = chr(random.randint(97, 122))
        choice_int = str(random.randint(0, 9))
        choice_code = random.choice([choice_big, choice_small, choice_int])
        imgdraw.text((num * 40 + 40, -3), choice_code, get_random_color(), imgfont)  # 在图像上写字
        code += choice_code
    io_obj = BytesIO()  # 生成字节内存管理对象
    imgobj.save(io_obj, 'png')  # 把图像保存到内存管理对象
    request.session['captcha'] = code
    request.session.set_expiry(60 * 5)
    return HttpResponse(io_obj.getvalue())

标签:obj,name,项目,models,博客园,length,文章,BBS,verbose
From: https://www.cnblogs.com/clever-cat/p/17020741.html

相关文章

  • BBS项目
    目录BBS项目开发基本流程项目分析(表)项目注册功能项目登录功能项目实操注册功能登录功能BBS项目开发基本流程需求分析架构设计分组开发提交测试交付上线项目分析(......
  • 基于Springboot+SSM框架旅游系统项目开发与设计(附源码资料)-毕业设计
    1.项目简介这是一个Springboot旅游网站管理系统,管理员角色包含以下功能:管理员登录,用户管理,旅游路线管理,旅游景点管理,酒店管理,旅游攻略管理,车票管理,订单管理,数据分......
  • bbs
    项目开发基本流程1.需求分析2.架构设计3.分组开发4.提交测试5.交付上线项目流程仿造博客园项目 核心:文章的增删改查表分析 先确定表的数量再确定表的基础字段......
  • bbs项目
    内容概要主题:仿BBS项目项目开发基本流程项目分析(表)项目注册功能项目登录功能今日内容详细项目开发基本流程1.需求分析2.架构设计3.分组开发4.提交测试......
  • BBS仿博客园
    目录项目开发基本流程一、BBS表设计1.表字段设计1.用户表2.个人站点表3.文章标签表4.文章分类表5.文章表6.点赞点踩表7.文章评论表2.七章表之间的关系1.用户表和个人站点表......
  • django_BBS博客系统练习
    表设计表分析先确认表的数量再确认表的基础字段最后确认表的外键字段1.用户表(基于auth模块设计扩展,手机号,头像,注册时间)fromdjango.contrib.auth.modelsimportAbst......
  • Android网络游戏之神农诀项目开发--视频
    Android网络游戏之神农诀项目开发​   Android作为一款为移动终端打造的开源手机操作平台,其引领破除技术垄断、拥有自主知识产权、降低开发成本之潮流,引起业界的......
  • BBS项目 前期准备及注册、登录部分
    项目开发基本流程1.需求分析2.架构设计3.分组开发4.提交测试5.交付上线创建项目配置环境配置TEMPLATES=[{'BACKEND':'django.template.backends......
  • BBS项目练习
    BBS项目练习此项目是对博客园软件的简单模仿,旨在整合django的知识点。七张信息表用户表(与站点一对一)站点表文章表(与站点一对多)文章分类表文章标签表点赞点踩表......
  • BBS项目
    项目开发基本流程1.需求分析2.架构设计3.分组开发4.提交测试5.交付上线项目流程仿博客园项目 核心:文章的增删改查表分析 先确定表的数量,在确定表的基础字段,最......