一、BBS-个人博客项目完整搭建  
项目开发流程
一、项目分类
现在互联网公司需要开发的主流web项目一般分为两类:面向互联网用户,和公司内部管理。
面向互联网用户:  C( consumer) 端项目
公司内部管理:B( business) 端项目
还有一类web应用,基本采用基于角色的权限控制,不同的员工在这套系统中拥有不同的权限,
适用于公司内部管理。
基于权限的后台关系系统:
	RBAC权限管理系统
二、项目开发模式分类
最常见的两类为瀑布开发模式和敏捷开发模式。
1 、瀑布开发模式
	瀑布开发模式是一种更倾向于严格控制的管理模式,要求在提出需求之后,充分完成项目的规划,各阶段都要经过严格的评审,只有当一个阶段的需求完成得非常好时才能进入下一个阶段。
	但是瀑布开发模式开发的失败率较高,且周期较长,于是就产生了敏捷开发模式。
	
2 、敏捷开发模式
	敏捷开发模式的核心是迭代开发,它将一个项目完整的开发流程分为了几个周期(也可以说是版本),由于每个版本的开发流程相似,看上去相当于一个重复迭代的过程。这么开发的好处是,使一个大的项目能在较短时间内上线,并在后续对它进行不断地迭代和优化。
	
3 、项目开发流程
	- 立项
	- 需求分析
	- 原型图( 产品画的) 
	- 切图
	- 技术选型,数据库架构设计
	- 前后端协同开发
	- 上线测试服务器联调
	- 测试
	- 修改bug
	- 上线运行
  
 二、BBS多人博客项目基本功能和需求  
比如:产品需求的确定,技术选型,数据库设计. . . 
"""
	1. 用户表(你可以自己创建,也可以使用auth_user表)
		扩展auth_user表
		phone 
		avatar:用来存储头像的地址
		create_time:用户注册的时间
		...
		
		blog_id = OneToOneField(to='blog')	
		
	2. 站点表(blog)
		站点名称
		站点标题
		站点样式:存的是css的路径
		
	3. 分类表(cate)
		分类名称
		blog = ForeignKey(to='blog')	
	4. 标签表
		标签名称
		blog = ForeignKey(to='blog')	
		
	5. 文章表(*******************article)
		文章标题
		文章摘要
		文章内容
		文章发表时间
		
		# 1. 通过文章id去点赞表或者评论表中查询
		# 优化
		# 2. 在文章表中增加子弹
		up_num			1
		down_num		1
		comment_num	    4
		
		
		# 外键关系子弹
		cate = ForeignKey(to='cate')	
		tag  = ManyToManyField(to='tag')
        blog = ForeignKey(to='blog')	
       
        
        
	6. 点赞点踩表
		谁给哪篇文章什么时间点了赞还是踩
		user		ForeignKey(to='user')	
		article		ForeignKey(to='article')	
		is_up			0/1
		create_time
		
		id  	user_id  	article_id			is_up   create_time
		1			1			1					0		‘’
		2			2			1					1		‘’
	7. 	评论表(comment)
		
		谁给哪篇文章在什么时间评论了什么内容
		user		ForeignKey(to='user')	
		article		ForeignKey(to='article')	
		content
		create_time
		parent_id		ForeignKey(to='comment')	
		parent_id		ForeignKey(to='self')
		#自关联
		
		id  	user_id  	article_id			content   create_time parent_id
		1			1			1					0		‘’			0
		2			2			1					1		‘’			1
		3			2			1					1		‘’			2
		
		
		子评论!!!
		评论评论的评论!!!
		根评论
		
		1. Python是世界上最好的语言
			1.1 PHP是世界上最好的语言
				1.2 Java是世界上最好的语言
				
			
	 无限极分类	
		category表
		id				cate_name			pid
		1				手机/数码			  0
		2				电脑/办公			  0
		
		...
		20					手机通讯			1
		21					运营商				 1
		...
		
		
		
		50				5G手机				20
		51				手机					20
		
		...
		
		
		100				128G/5G				50
		
""" 
  
 三、项目程序设计  
 
 四、BBS数据库表结构设计  
 1.用户表:UserInfo  
(通过继承AbstractUser类来扩写Auth_user)
•phone:用户的联系方式
•bg_img:  用户的主页背景
•province:  用户的省份
•city:  用户的城市
•gender :  用户的性别
•avatar:用户的头像
•blog:用户的博客站点(外键一对一关联博客表Blog)
  
 2.博客表:Blog  
•title:博客标题
•subtitle:  博客子标题
•style:博客样式
  
 3.文章表:Article  
•title:文章标题
•head_img:  头像
•description:文章摘要
•content:文章内容
•create_time:文章的创建时间
•modify_time:   文章的修改时间
•up_num : 点赞数
•down_num: 点踩数
•comment_num: 评论数
•blog:属于哪个博客站点(外键关联博客表Blog)
•category:属于哪个分类(外键关联分类表Category)
  
 4.标签表:Tag  
•name:标签名
•blog:属于哪个博客站点(外键关联博客表Blog)
  
 5.分类表:Category  
•name:分类名
•blog:属于哪个博客站点(外键关联博客表Blog)
  
6.评论表:Comment  
•user:评论的用户(外键关联用户表UserInfo)
•article:该评论属于哪篇文章(外键关联文章表Article)
•content:评论内容
•comment_time:  评论的创建时间
•comment_id:评论的目标id (外键进行自关联)
  
 7.点赞点踩表:UpAndDown  
•user:来自哪个用户(外键关联用户表UserInfo)
•article:属于哪篇文章(外键关联文章表Article)
•is_up:点赞还是点踩(根据bool 值来判断)
•create_time :  点赞或踩的时间
  
 8.文章标签表: Tag2Article  
根据文章与标签的多对多关系手动建立的第三张表
•	tag:标签名(外键关联标签表Tag)
•	article:属于哪篇文章(外键关联文章表Article)
  
 9.轮播图表 Swiper (拓展)  
•	image:  轮播图图片名
•	title:轮播图标题
•	img_url:  点击轮播图要跳转的url地址
  
 10.日志表: Log(拓展)  
•	id :  日志id 
•	ip:  访问的ip地址
•	time:  访问的时间
•	url:  访问的url
•	device:  访问的浏览器
•	platform: 访问的操作系统类型
  
 11.日志表: Log(拓展)  
•	id :  日志id 
•	ip:  访问的ip地址
•	time:  访问的时间
•	url:  访问的url
•	device:  访问的浏览器
•	platform: 访问的操作系统类型
  
 五、创建BBS表模型  
 模型层models.py中写入以下orm语句模型来创建表模型  
from  django. contrib. auth. models import  AbstractUser
from  django. utils. html import  mark_safe
from  django. db import  models
from  markdown import  markdown
class  Log ( models. Model) : 
    id  =  models. AutoField( primary_key= True ) 
    ip =  models. CharField( max_length= 64 ,  verbose_name= '访问IP' ,  help_text= '访问用户的IP地址' ) 
    time =  models. DateTimeField( auto_now_add= True ,  verbose_name= '访问时间' ,  help_text= '该用户的访问时刻' ) 
    url =  models. CharField( max_length= 64 ,  verbose_name= '访问的URL' ,  help_text= '该用户访问的URL地址' ) 
    device =  models. CharField( max_length= 256 ,  null= True ,  verbose_name= '访问的浏览器' ,  help_text= '该用户是用什么浏览器访问的' ) 
    platform =  models. CharField( max_length= 256 ,  null= True ,  verbose_name= '访问的系统' ,  help_text= '该用户用的是什么操作系统' ) 
    
    def  __str__ ( self) : 
        return  self. ip
    
    class  Meta : 
        ordering =  [ 'id' ] 
        verbose_name_plural =  '日志' 
class  UserInfo ( AbstractUser) : 
    avatar =  models. FileField( upload_to= 'avatar/' ,  default= 'avatar/default.png' ,  verbose_name= '头像' ,  help_text= '该用户的头像' ) 
    bg_img =  models. FileField( upload_to= 'bg_img/' ,  default= 'bg_img/default_bg.png' ,  verbose_name= '头像' , 
                              help_text= '该用户的主页背景' ) 
    province =  models. CharField( max_length= 32 ,  default= '' ,  verbose_name= '省' ,  help_text= '该用户的省' ) 
    city =  models. CharField( max_length= 32 ,  default= '' ,  verbose_name= '城市' ,  help_text= '该用户的市' ) 
    gender =  models. IntegerField( choices= ( ( 0 ,  '保密' ) ,  ( 1 ,  '男' ) ,  ( 2 ,  '女' ) ) ,  default= 0 ,  verbose_name= '性别' , 
                                 help_text= '该用户的性别' ) 
    phone =  models. CharField( max_length= 11 ,  null= True ,  default= '' ,  verbose_name= '联系方式' ,  help_text= '该用户的联系方式' ) 
    blog =  models. OneToOneField( to= 'Blog' ,  on_delete= models. CASCADE,  null= True ,  verbose_name= '博客' ,  help_text= '该用户的博客' ) 
    def  __str__ ( self) : 
        return  self. username
    class  Meta : 
        verbose_name_plural =  '用户' 
class  Blog ( models. Model) : 
    title =  models. CharField( max_length= 32 ,  verbose_name= '博主昵称' ,  help_text= '博主昵称' ) 
    subtitle =  models. CharField( max_length= 32 ,  verbose_name= '子标题/公告' ,  help_text= '博客的子标题/公告' ) 
    style =  models. CharField( max_length= 32 ,  verbose_name= '样式' ,  help_text= '该博客独有的样式' )   
    def  __str__ ( self) : 
        return  self. title
    class  Meta : 
        verbose_name_plural =  '博客站点' 
class  Article ( models. Model) : 
    title =  models. CharField( max_length= 32 ,  verbose_name= '标题' ,  help_text= '文章的标题' ) 
    head_img =  models. FileField( upload_to= 'article_head_img/' ,  default= 'article_head_img/default_head.png' , 
                                verbose_name= '头图' , 
                                help_text= '文章的头图' ) 
    description =  models. CharField( max_length= 128 ,  verbose_name= '摘要' ,  help_text= '简要描述该文章' ) 
    content =  models. TextField( verbose_name= '内容' ,  help_text= '文章的内容' ) 
    markdown =  models. TextField( verbose_name= 'Markdown内容' ,  default= '暂无' ,  help_text= '文章的Markdown内容' ) 
    create_time =  models. DateTimeField( auto_now_add= True ,  verbose_name= '创建时间' ,  help_text= '该文章的创建时间' ) 
    modify_time =  models. DateTimeField( auto_now= True ,  verbose_name= '修改时间' ,  help_text= '该文章的最后修改时间' ) 
    up_num =  models. IntegerField( default= 0 ,  verbose_name= '点赞数' ,  help_text= '该文章的点赞数' ) 
    down_num =  models. IntegerField( default= 0 ,  verbose_name= '点踩数' ,  help_text= '该文章的点踩数' ) 
    comment_num =  models. IntegerField( default= 0 ,  verbose_name= '评论数' ,  help_text= '该文章的评论数' ) 
    blog =  models. ForeignKey( to= 'Blog' ,  on_delete= models. CASCADE,  null= True ,  blank= True ,  verbose_name= '博客' , 
                             help_text= '该文章属于哪个博客页面' ) 
    category =  models. ForeignKey( to= 'Category' ,  on_delete= models. CASCADE,  null= True ,  blank= True ,  verbose_name= '分类' , 
                                 help_text= '该文章属于哪个分类' ) 
    tag =  models. ManyToManyField( to= 'Tag' ,  through= 'Tag2Article' , 
                                 through_fields= ( 'article' ,  'tag' ) ,  verbose_name= '标签' , 
                                 help_text= '该文章有哪些标签' ) 
    def  get_text_md ( self) : 
        return  mark_safe( markdown( self. content) ) 
    def  __str__ ( self) : 
        return  self. title
    class  Meta : 
        verbose_name_plural =  '文章' 
        ordering =  [ 'id' ,  ] 
class  Tag ( models. Model) : 
    name =  models. CharField( max_length= 32 ,  verbose_name= '标签' ,  help_text= '标签的名字' ) 
    blog =  models. ForeignKey( to= 'Blog' ,  on_delete= models. DO_NOTHING,  null= True ,  blank= True ,  verbose_name= '博客' , 
                             help_text= '该标签属于哪个博客页面' ) 
    def  __str__ ( self) : 
        return  self. name
    class  Meta : 
        verbose_name_plural =  '标签' 
class  Category ( models. Model) : 
    name =  models. CharField( max_length= 32 ,  verbose_name= '分类' ,  help_text= '分类的名称' ) 
    blog =  models. ForeignKey( to= 'Blog' ,  on_delete= models. DO_NOTHING,  null= True ,  blank= True ,  verbose_name= '博客' , 
                             help_text= '该分类属于哪个博客页面' ) 
    def  __str__ ( self) : 
        return  self. name
    class  Meta : 
        verbose_name_plural =  '分类' 
class  Comment ( models. Model) : 
    user =  models. ForeignKey( to= 'UserInfo' ,  on_delete= models. DO_NOTHING,  verbose_name= '用户' ,  help_text= '该评论来自哪个用户' ) 
    article =  models. ForeignKey( to= 'Article' ,  on_delete= models. CASCADE,  null= True ,  verbose_name= '文章' , 
                                help_text= '评论的对象是哪篇文章' ) 
    content =  models. CharField( max_length= 256 ,  verbose_name= '内容' ,  help_text= '评论的内容' ) 
    comment_time =  models. DateTimeField( auto_now_add= True ,  verbose_name= '时间' ,  help_text= '评论的时间' ) 
    comment_id =  models. ForeignKey( to= 'self' ,  on_delete= models. CASCADE,  null= True ,  verbose_name= '评论id' , 
                                   help_text= '对哪个id的评论进行评论' ) 
    def  __str__ ( self) : 
        return  self. content
    class  Meta : 
        verbose_name_plural =  '评论' 
class  UpAndDown ( models. Model) : 
    user =  models. ForeignKey( to= 'UserInfo' ,  on_delete= models. CASCADE,  verbose_name= '用户' ,  help_text= '来自哪个用户' ) 
    article =  models. ForeignKey( to= 'Article' ,  on_delete= models. CASCADE,  null= True ,  verbose_name= '文章' , 
                                help_text= '针对哪篇文章' ) 
    is_up =  models. BooleanField( null= True ,  verbose_name= '点赞点踩' ,  help_text= 'True为点赞,False为点踩' ) 
    create_time =  models. DateTimeField( auto_now_add= True ,  verbose_name= '创建时间' ,  help_text= '点赞点踩的时间' ) 
    def  __str__ ( self) : 
        return  self. user
    class  Meta : 
        verbose_name_plural =  '点赞点踩' 
class  Tag2Article ( models. Model) : 
    tag =  models. ForeignKey( to= 'Tag' ,  on_delete= models. SET_DEFAULT,  default= '' ,  verbose_name= '标签' ,  help_text= '关联的标签' ) 
    article =  models. ForeignKey( to= 'Article' ,  on_delete= models. CASCADE,  default= '' ,  verbose_name= '文章' , 
                                help_text= '关联的文章' ) 
    class  Meta : 
        verbose_name_plural =  '标签关联文章' 
class  Swiper ( models. Model) : 
    image =  models. FileField( upload_to= 'swiper_img/' ,  default= 'swiper_img/default.jpg' ,  verbose_name= '图片' , 
                             help_text= '轮播图的图片' ) 
    title =  models. CharField( max_length= 32 ,  verbose_name= '标题' ,  help_text= '图片的标题' ) 
    img_url =  models. CharField( max_length= 64 ,  verbose_name= 'URL' ,  help_text= '点击图片要跳转的URL地址' ) 
    def  __str__ ( self) : 
        return  self. img_url
    class  Meta : 
        verbose_name_plural =  '轮播图' 
  
 django2表设计  
from  django. db import  models
"""
先写普通字段
之后再写外键字段
""" 
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 ,  on_delete= models. CASCADE) 
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 )   
class  Category ( models. Model) : 
    name =  models. CharField( verbose_name= '文章分类' ,  max_length= 32 ) 
    blog =  models. ForeignKey( to= 'Blog' ,  null= True ,  on_delete= models. CASCADE) 
class  Tag ( models. Model) : 
    name =  models. CharField( verbose_name= '文章标签' ,  max_length= 32 ) 
    blog =  models. ForeignKey( to= 'Blog' ,  null= True ,  on_delete= models. CASCADE) 
class  Article ( models. Model) : 
    title =  models. CharField( verbose_name= '文章标题' ,  max_length= 64 ) 
    desc =  models. CharField( verbose_name= '文章简介' ,  max_length= 255 ) 
    
    content =  models. TextField( verbose_name= '文章内容' ) 
    create_time =  models. DateField( auto_now_add= True ) 
    
    up_num =  models. IntegerField( verbose_name= '点赞数' ,  default= 0 ) 
    down_num =  models. IntegerField( verbose_name= '点踩数' ,  default= 0 ) 
    comment_num =  models. IntegerField( verbose_name= '评论数' ,  default= 0 ) 
    
    blog =  models. ForeignKey( to= 'Blog' ,  null= True ,  on_delete= models. CASCADE) 
    category =  models. ForeignKey( to= 'Category' ,  null= True ,  on_delete= models. CASCADE) 
    tags =  models. ManyToManyField( to= 'Tag' , 
                                  through= 'Article2Tag' , 
                                  through_fields= ( 'article' ,  'tag' ) 
                                  ) 
class  Article2Tag ( models. Model) : 
    article =  models. ForeignKey( to= 'Article' ,  on_delete= models. CASCADE) 
    tag =  models. ForeignKey( to= 'Tag' ,  on_delete= models. CASCADE) 
class  UpAndDown ( models. Model) : 
    user =  models. ForeignKey( to= 'UserInfo' ,  on_delete= models. CASCADE) 
    article =  models. ForeignKey( to= 'Article' ,  on_delete= models. CASCADE) 
    is_up =  models. BooleanField( )   
class  Comment ( models. Model) : 
    user =  models. ForeignKey( to= 'UserInfo' ,  null= True ,  on_delete= models. CASCADE) 
    article =  models. ForeignKey( to= 'Article' ,  null= True ,  on_delete= models. CASCADE) 
    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 ,  on_delete= models. CASCADE)