1.项目开发基本流程

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

2.项目流程

仿造博客园项目(核心:文章的增删改查)
1.表分析:
1.1用户表
1.2个人站点表
1.3文章表
1.4文章标签表
1.5文章标签表
1.6点赞点踩表
1.7文章评论表 2.基础字段分析
'''下列表字段仅供参考,可以扩展'''
2.1用户表:替换auth_user表并扩展字段:电话号码、头像、注册时间
2.2个人站点表:
站点名称
站点标题 站点样式(css文件)
2.3文章表
文章标题
文章简介
文章内容
发布时间
2.4文章分类表
分类名称
2.5文章标签表
标签名称
2.6点赞点踩表:记录哪个用户给哪篇文章点了推荐(赞)还是反对(踩)
用户字段(用户主键)>>>:外键字段
文章字段(文章主键)>>>:外键字段
点赞点踩
2.7文章评论表:记录哪个用户给哪篇文章评论了什么内容
用户字段(用户主键)>>>:外键字段
文章字段(文章主键)>>>:外键字段
评论内容
评论时间
外键字段(自关联)
"""
评论有两种情况:
1.针对文章的评论,叫做根评论。
2.针对某条评论的评论(或回复),叫做子评论。
""" 3.外键字段
3.1用户表:用户表和个人站点是一对一外键关系。
3.2个人站点表:暂无外键
3.3文章表:
文章表和个人站点表是一对多关系
文章表和文章分类表是一对多外键关系
文章表与文章标签表 是多 对多关系
文章评论数
文章点赞数
文章点踩数
"""
我们本想通过跨表查询拿到文章的评论数、点赞数,但是文章需要频繁的展示,每次跨表查询效率极低,所以我们在文章表中再创建三个普通字段:评论数,点赞数,点踩数。之后只需要确保每次操作评论表或者点赞点踩表时同步修改上述三个普通字段即可。
"""
3.4文章分类表:文章分类与个人站点是一对多外键关系
3.5文章标签表:文章标签与个人站点是一对多外键关系

3.表创建(包含数据库迁移命令)

1.由于views中用来存放视图函数,我们将form组件的校验内容放在应用当中新建一个py文件。

2.针对多对多字段(文章表和标签表)采用半自动创建第三张表方式。手动创建的第三张表需要指定和哪张表关联(),并且多对多关系中外键所在的一方需要指明通过哪张表(第三张表)以及哪些字段和另一张表连接。
eg:
tags = models.ManyToManyField(to='Tag',through='Article2Tag',through_fields=('article','tag'),null=True)

4.注册功能

1.选择头像时选择照片选完就出现在图片链接上:
<label for="myfile">头像 # 和input按钮id一样,点击头像和点击input按钮效果一样
<img src="/media/avatar/default.png" alt="" width="50" id="myimg">
</label>
<input type="file" id="myfile" style="display: none"> <script>
// 用户头像实时展示:固定以下代码
$('#myfile').change(function () {
let myFileReadObj = new FileReader();
let fileObj = this.files[0];
myFileReadObj.readAsDataURL(fileObj);
myFileReadObj.onload = function () {
$('#myimg').attr('src',myFileReadObj.result)
}
})
</script> 2.用ajax传输数据:
"""
用ajax提交数据时,form表单的input标签的type不能为submit,因为submit会触发form表单的提交功能,和ajax冲突。
"""
console.log(标签选择器.serializeArray())可以拿到一个数组套对象(列表套字典)
<script>
// 给注册按钮绑定点击事件,发送ajax请求,携带文件数据
$('#subBtn').click(function () {
let myFormDataObj = new FormData();
console.log($('#form').serializeArray())
})
</script>
# [{},{},{},{}]

我们将拿到的结果$('#form').serializeArray()进行for循环,发现a是索引值,b是对象(字典)。
<script>
$('#subBtn').click(function () {
let myFormDataObj = new FormData();
$.each($('#form').serializeArray(),function (a,b) {
console.log(a,b)
})
})
</script>
'''
0 {name: 'username', value: 'max'}
1 {name: 'password', value: '222'}
2 {name: 'confirm_password', value: '222'}
3 {name: 'email', value: '275712541@qq.com'}
'''

"""
ajax携带数据的模板:
<script>
$('#d3').click(function () {
// 1.先产生一个FormData对象
let myFormDataObj = new FormData();
// 2.往该对象中添加普通数据
myFormDataObj.append('name', 'jason');
myFormDataObj.append('age', 18);
// 3.往该对象中添加文件数据
myFormDataObj.append('file', $('#d2')[0].files[0])
// 4.发送ajax请求
$.ajax({
url:'',
type:'post',
data:myFormDataObj, // ajax发送文件固定的两个配置
contentType:false,
processData:false,
success:function (args){
alert(args)
} })
})
</script>
"""
我们用对象分别点name和value就是拿到对象的键和至,传给myFormDataObj对象。但是该方法只能拿到普通字段,不能添加文件字段。
<script>
$('#subBtn').click(function () {
let myFormDataObj = new FormData();
$.each($('#form').serializeArray(),function (index,dataObj) {
myFormDataObj.append(dataObj.name,dataObj.value)
})
})
</script> 3.成功传输数据后,在后端去掉confirm键值对(因为如果能传到后端说明password和confirm_password一样),然后再添加一个键值对:avatar,值是文件对象。然后用**将字典打散成关键字参数传入。 4.接下来我们要补充错误信息:生成一个字典。在前端根据结果不同显示不同结果。
success:function (args) {
if(args.code === 10000) {
window.location.href = args.url
}else{
console.log(args.msg)
}
}
args.msg是错误信息。我们先打印出来看看是什么效果:

结果是一个对象,对象的值是数组(错误信息),对象的键是字段名。
因为input标签在for循环你当中由form组件自动生成,所以我们没有办法直接拿到它的id,通过查看我们得知for循环中的input标签的id值是id_字段名。
# 4
所以我们采用拼接的方式拿到input标签的id值,在通过next拿到渲染错误信息的span标签,将错误信息渲染上去。
$('#subBtn').click(function () {
let myFormDataObj = new FormData();
$.each($('#form').serializeArray(),function (index,dataObj) {
myFormDataObj.append(dataObj.name,dataObj.value)
})
myFormDataObj.append('avatar',$('#myfile')[0].files[0])
$.ajax({
url:'',
type:'post',
{#'csrfmiddlewaretoken':{{ csrf_token }},#}
data:myFormDataObj,
contentType:false,
processData:false,
success:function (args) {
if(args.code === 10000) {
window.location.href = args.url
}else{
{#console.log(args.msg)#}
let dataObj = args.msg
$.each(dataObj,function (k,msgArray) { // k 是对象的键,msgArray是错误信息数组
let eleId = '#id_' + k
$(eleId).next().text(msgArray[0])
})
}
}
})
})
# 5 5.后端的if avatar_obj不能省略:
if avatar_obj: # 判断不能不加,如果不加用户不上传avatar_obj对应的值是None,不是默认头像。默认值只有不上传的时候才能使用
clean_data['avatar'] = avatar_obj

5.登录功能

1.img标签的src属性
1.可以直接填写图片地址
2.还可以填写一个路由 会自动朝该路由发送get请求
如果结果是图片的二进制数据 那么自动渲染图片 2.登陆验证码视频:
from PIL import Image, ImageFont, ImageDraw
from io import BytesIO, StringIO 随机产生三个数字,产生背景色:
def get_random():
return random.randint(0, 255), random.randint(0, 255), random.randint(0, 255) def get_code_func(request):
img_obj = Image.new('RGB', (350, 35), get_random())
draw_obj = ImageDraw.Draw(img_obj)
font_obj = ImageFont.truetype('static/font/333.ttf', 25)
'''ttf字体下载网站:https://www.fonts.net.cn/font-20768396126.html'''
code = ''
for i in range(4):
random_upper = chr(random.randint(65, 90))
random_lower = chr(random.randint(97, 122))
random_int = str(random.randint(0, 9))
tem_choice = random.choice([random_upper, random_lower, random_int])
draw_obj.text((i * 60 +65, 0), tem_choice, font=font_obj)
code += tem_choice
print(code)
request.session['code'] = code
io_obj = BytesIO()
img_obj.save(io_obj, 'png')
return HttpResponse(io_obj.getvalue()) 点击验证码动态刷新:
<script>
// 验证码动态刷新
$('#d1').click(function () {
let oldSrc = $(this).attr('src');
$(this).attr('src',oldSrc + '?')
})
</script>
"""
每局部刷新一次验证码img标签的src属性后面的路由都会加一个问号,所以我们每点击一次图片验证码在路由后面加一个?
"""
# 1

6.修改密码

1.修改密码在前端用一个模态框来提示用户输入信息。模态框的触发器是一个a标签,需要在a标签中填写以下参数:data-toggle="modal" data-target="#myModal"才可以正常触发模态框弹出(myModal是模态框的id)。
要注意的是这个a标签作用是触发模态框,在模态框中点击提交按钮提交post请求,所以a标签不能再填写路由,因为填写路由代表向路由发送get请求,如果get请求没有提交Httpresponse对象后端会报错。

7.home页面搭建

1.前端页面在搭建头像时要注意userinfo和site的绑定。首先拿到所有文章的queryset,传到前端。

2.前端循环拿到所有的文章标题,文章简介。

3.在搭建头像和文章简介时,头像和文章在同一行,头像和文章简介分别用div包起来,头像的class为media-left,文章简介的class为media-body。
# 2 3

6.通过admin后台管理录入数据

1.首先注册超级管理员,然后登陆/admin/。

2.然后在应用下的admin.py中依次注册所有的表,这样我们可以通过admin后台管理录入数据:
from app01 import models
admin.site.register(models.表名) 3.修改user表名:
在models中表中添加添加以下代码:
class Meta:
verbose_name_plural = '用户表'
修改其他表表名:在类中添加__str__方法:
def __str__(self):
return f'{self.name}'
"""
超级管理员:jack 222222
普通用户:
max 222222
jason 222222
jerry 222222
""" 4.当我绑定用户表中的站点(外键Site)时,网页提示我们需要输入电话号码,我们只需要在models.py中的用户表字段phone后面添加blank=True即可,该字段指在后台管理录入数据时可以不上传该数据,不需要执行数据库迁移命令。 5.暴露文件目录:我们之前的用户头像文件都是直接存储在avatar目录中的,但是这个目录无法直接暴露在页面上,也就是我们的页面上无法直接显示用户的头像。因此我们需要在应用下新建一个media目录,并且在settings中声明MEDIA_ROOT = os.path.join(BASE_DIR, 'media')。
此外还需要在urls.py中新增一个路由:
from django.views.static import serve
re_path('media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT})。
由于此时我们的路径是app01>>>media>>>avatar>>>图片文件,所以我们还需要在前端代码拼接路径:
<img class="media-object" src="/media/{{ article_obj.site.userinfo.avatar }}" alt="" width="40">。
这样我们在网页上输入图片的路径也可以找到该文件。 6.不要忘记绑定用户表和个人站点表之间的关系。

6.个人站点功能

1.如果想模仿出博客园一样的效果,每个月发布了几篇文章,需要导入一个模块并且进行以下操作:
views.py: from django.db.models.functions import TruncMonth
def site_func(request, username):
site_obj = models.Site.objects.filter(site_name=username).first()
if not site_obj:
return render(request, 'errorPage.html')
article_queryset = models.Article.objects.filter(site=site_obj)
catagory_queryset = models.Category.objects.filter(site=site_obj).annotate(article_num=Count('article__pk')).values(
'name', 'article_num')
tag_queryset = models.Tag.objects.filter(site=site_obj).annotate(article_num=Count('article__pk')).values('name','article_num')
date_queryset = models.Article.objects.filter(site=site_obj).annotate(month=TruncMonth('create_time')).values('month').annotate(article_num=Count('pk')).values('month','article_num')
"""
前一个annotate作用是处理字段,后一个才是分组
"""
print(date_queryset) # <QuerySet [{'month': datetime.datetime(2023, 1, 1, 0, 0), 'article_num': 4}]> return render(request, 'sitePage.html', locals()) 2.如果报错提示为time zone相关内容则需要在settings.py中修改时区:
TIME_ZONE = 'Asia/Shanghai'
USE_TZ = False 3.如果按照筛选功能输入指定年月的路由任然无法筛选文章那么在settings.py中设置(解决方式和上述问题一致):
TIME_ZONE = 'Asia/Shanghai'
USE_TZ = False

7.侧边栏筛选功能

1.研究博客园三种情况下的筛选特性:
思路:在个人站点中再次筛选一些文章显示在页面上
分类筛选路由特性: 站点名称/catagory/数据主键值
标签筛选特性: 站点名称/tag/数据主键值
日期筛选特性: 站点名称/archive/文章

8.文章详情表

1.文章内容数据添加:如果想要复制别人的博客,只需要在别人的博客页面上右键>>>检查>>>点击cnblog_post_body>>>copy>>>copy outerHTML,然后在admin后台管理文章内容处粘贴以上代码。此外还需要在前端装上过滤器:
{{ article_obj.content|safe }}才能正常转译内容。

9.点赞点踩

1.后端判断逻辑:首先判断是否是POST请求,然后判断用户是否已经登陆,然后判断文章作者是否是已登录用户,在判断该用户表是否给该文章点过赞(或踩),再判断是赞还是踩,根据不同的结果在表中添加不同的数据。

2.添加点赞点踩图标:复制博客园的图标代码,css样式需要同步复制。
"""
模板的继承不仅需要继承body标签中的内容区域,还需继承每个标签(包含其子标签的js样式)(在head标签中的style标签中)。
<head>
{% block css %} {% endblock %}
</head> <body>
<script>
{% block js %} {% endblock %}
</script>
</body>
""" 3.当我们把点赞点踩图标成功显示在页面上时,在它的下面继续写内容,发现内容和图标在同一水平线上,这是因为浮动现象。我们需要用一个div标签将图标包起来,并且该div需要用class="clearfix"来取消浮动。 4.如何判断赞和踩:我们在后端发现赞和踩分别属于两个不同的class,所以我们在前端通过:
let isUp = $(this).hacClass('点赞独有的class'),拿到的isUp是一个js的布尔值('true'或'flase'),通过结果不同可以分辨是点赞还是点踩。 5.将isUp传到后端后,可以通过反序列化将js的布尔值转成python的布尔值:
up_or_down = request.POST.get('isup')
up_or_down = json.loads(up_or_down) 6.数据确认无误后,不但要在comment中添加数据,还要在article表中同步更新点赞数,采用F查询拿到指定字段中的数据后添加。 7.在传到前端的字典中的值如果是字符串,并且该字符串内含有前端代码,在前端渲染时用html(args.msg)。

文章评论

文章评论分为两部分:
1.评论样式搭建
2.评论楼渲染
1.评论样式搭建:
1.1前端的评论样式在文章详情表中搭建,首先判断用户是否已经登陆,如果登陆把评论框样式显示在页面上,如果未登录显示两个a标签:登录和注册。 1.2首先在全局定义一个全局变量parentIId,该全局变量用来记录是评论还是回复,默认为null(该全局变量和函数中类似,在每个点击事件中可以改全局变量)。给提交评论按钮绑定点击事件,同时将parentId发送到后端。 1.3给回复按钮绑定点击事件,在该点击事件中改parentId。
"""
默认点击回复按钮会自动跳到页首,如果想点击回复按钮定位到textarea标签,需要将回复的a标签定位到textarea标签,在a标签中修改:href="#textarea标签的id"。
"""

文章添加

1.Kindeditor富文本编辑器使用:http://kindeditor.net/docs/usage.html
Kindeditor富文本编辑器下载:http://kindeditor.net/down.php
下载好之后将文件拷贝一份到static目录下。 2.使用:
需要在添加文本框的html文件内加入以下代码:
<script charset="utf-8" src="/static/kindeditor/kindeditor-all-min.js"></script>
<script charset="utf-8" src="/static/kindeditor/lang/zh-CN.js"></script>
<script>
KindEditor.ready(function (K) {
window.editor = K.create('#mycontent',
{
width:'100%',
height:'300px',
resizeType:'0',
}
);
});
</script> """
mycontent是textarea的id值。宽度可以写百分比和长度(单位为px),高度只能写长度(px)。resizeType参数:2或1或0,2时可以拖动改变宽度和高度,1时只能改变高度,0时不能拖动。
"""

bbs大作业的更多相关文章

  1. 数据库大作业--由python+flask

    这个是项目一来是数据库大作业,另一方面也算是再对falsk和python熟悉下,好久不用会忘很快. 界面相比上一个项目好看很多,不过因为时间紧加上只有我一个人写,所以有很多地方逻辑写的比较繁琐,如果是 ...

  2. 程设大作业xjb写——魔方复原

    鸽了那么久总算期中过[爆]去[炸]了...该是时候写写大作业了 [总不能丢给他们不会写的来做吧 一.三阶魔方的几个基本定义 ↑就像这样,可以定义面的称呼:上U下D左L右R前F后B UD之间的叫E,LR ...

  3. 大作业NABC分析结果

    大作业NABC分析结果 这次的大作业计划制作一款关于七巧板的游戏软件.关于编写的APP的NABC需求分析: N:需求 ,本款软件主要面向一些在校的大学生,他们在校空闲时间比较多,而且热衷于一些益智类游 ...

  4. [留念贴] C#开发技术期末大作业——星月之痕

    明天就要去上海大学参加 2015赛季 ACM/ICPC最后一场比赛 —— EC-Final,在这之前,顺利地把期末大作业赶出来了. 在这种期末大作业10个人里面有9个是从网上下载的国内计算机水平五六流 ...

  5. Hadoop综合大作业

    Hadoop综合大作业 要求: 用Hive对爬虫大作业产生的文本文件(或者英文词频统计下载的英文长篇小说)词频统计. 用Hive对爬虫大作业产生的csv文件进行数据分析 1. 用Hive对爬虫大作业产 ...

  6. 爬虫综合大作业——网易云音乐爬虫 & 数据可视化分析

    作业要求来自于https://edu.cnblogs.com/campus/gzcc/GZCC-16SE2/homework/3075 爬虫综合大作业 选择一个热点或者你感兴趣的主题. 选择爬取的对象 ...

  7. 期末Java Web大作业----简易的学生管理系统

    学生信息管理系统(大作业) 2018-12-21:此文章已在我的网站更新,添加视图介绍等信息,源码请移步下载https://www.jeson.xin/javaweb-sims.html PS:首先不 ...

  8. CSAPP HITICS 大作业 hello's P2P by zsz

    摘 要 摘要是论文内容的高度概括,应具有独立性和自含性,即不阅读论文的全文,就能获得必要的信息.摘要应包括本论文的目的.主要内容.方法.成果及其理论与实际意义.摘要中不宜使用公式.结构式.图表和非公知 ...

  9. #006 C语言大作业学生管理系统第三天

    还差最后两部分 读取文件 恢复删除的学生信息 先学会处理文件的 知识点,再继续跟着视频做这个作业. 应该明天周六能把视频里手把手教的学生管理系统敲完 第二周尽量自己能完成C语言课本最后面那道学生管理系 ...

  10. 最课程阶段大作业之01:使用SVN实现版本控制

    版本控制在友军那里都是放在整个培训的最后阶段才开始讲的,但我们打算放到SE阶段.与其匆匆在项目实战阶段弄个半生不熟,然后进入实际工作中接受他人对你的怀疑,不如……早死早超生~~~. 可是,我们毕竟现在 ...

随机推荐

  1. 畅联新增插件:新增依爱NB烟感

    双美接入,C++版,就是解析Json时稍微有点问题,依爱这边的Json格式,Value字段竟然是单引号....呵呵,反正也解决了. 备注一下:电信模式,AEP平台模式.

  2. Asp.Net Core6.0中MediatR的应用CQRS

    1.前言 对于简单的系统而言模型与数据可以进行直接的映射,比如说三层模型就足够支撑项目的需求了.对于这种简单的系统我们过度设计说白了无异于增加成本,因为对于一般的CRUD来说我们不用特别区分查询和增删 ...

  3. 推荐三个实用的 Go 开发工具

    孙悟空在花果山称王的时候,特意去了一趟东海,在那里淘到了如意金箍棒.因为身为一个山大王,怎么能没有一件趁手的兵器呢? 作为程序员的我们也一样,除了我们的傍身武器 Ctrl C + V 之外,还要不停的 ...

  4. 【云原生 · Kubernetes】KubeVirt热迁移

    [云原生 · Kubernetes]KubeVirt热迁移 检查节点和kubevirt状态 启用热迁移 创建虚拟机 在虚拟机上启动一个服务 迁移虚拟机 热迁移是KubeVirt支持的一个常见虚拟化特性 ...

  5. vue3.0使用tui.image-editor图片编辑组件报错TypeError: Cannot convert undefined or null to object

    在vue3.0的项目中使用tui.image-editor组件.一直都是报错.查看报错位置发现代码 addEventListener() { Object.keys(this.$listeners). ...

  6. c#winfrom通讯录管理系统

    一个简单的通讯录管理系统,适合毕业设计. 主要实现以下功能 1.系统登录 2.增加联系人 3.修改和删除联系人 4.查找联系人 5.系统用户管理 首先先搭建数据库. 我这边使用的版本是sqlserve ...

  7. c++学习笔记(入门)

    1 struct和class的区别 struct成员变量(成员函数)的访问属性缺省的情况下默认为public. class成员变量(成员函数)的访问属性缺省的情况下默认为private. 2 初始化列 ...

  8. 一文教会你如何在内网搭建一套属于自己小组的在线 API 文档?

    Hello,大家好,我是阿粉,对接文档是每个开发人员不可避免都要写的,友好的文档可以大大的提升工作效率. 阿粉最近将项目的文档基于 Gitbook 和 Gitlab 的 Webhook 功能的在内网部 ...

  9. 一个小时,200行代码,手写Spring的IOC、DI、MVC

    一.概述 配置阶段:主要是完成application.xml配置和Annotation配置. 初始化阶段:主要是加载并解析配置信息,然后,初始化IOC容器,完成容器的DI操作,已经完成HandlerM ...

  10. TypeError: Object(…) is not a function

    vue中遇到的这个错误 1. 先检查变量名或者函数名是否有重复定义 报这错之后看了好久,也没有发现starkflow上说的,重复定义了变量或者函数 2. vue的话 检查下函数写的位置,直接写到cre ...