BBS仿博客園項目
項目需求分析
項目需求(產品經理,架構師,開發組組長)
項目設計(框架的選擇,數據庫的選擇,主要功能模塊) 報價(工期,開發人員工資)
任務分發(開發組長>>>小弟開發)
測試(本地測試+測試人員測試)
交付上線
項目分析
表設計
用戶表(UserInfo)
用戶電話phone
用戶頭像avatar
用戶建立時間create_time
blog 》》》site 一對一我的站點表
我的站點表(Blog)
站點名稱site_name
站點標題site_title
站點樣式site_theme
文章標籤表(Tag)
標籤名稱name
blog >>> Blog 一對多我的站點表
文章分類表
分類名稱name
blog >>> Blog 一對多我的站點表
文章表
文章標題title
文章簡介desc
文章詳情content
文章發佈時間create_time
# 數據庫查詢優化(可寫可不寫,主要是爲了節省跨表查詢,而與點贊點踩這表表要關聯,聯級,因此是要用要事物)
文章評論數comment_num
文章點贊數up_num
文章點踩數down_num
blog 》》》 Blog 一對多我的站點表
tags >>> Tag 多對多標籤表
category 》》》 Category 一對多分類表
點贊點踩表
用戶名字段user 一對多 我的站點/用戶
文章字段article 一對多 文章表
點贊點踩is_up 0/1
user article is_up
1 1 1
2 1 0
1 2 0
文章評論表
用戶名字段user 一對多 我的站點/用戶
文章字段article 一對多 文章表
評論內容content
父評論parent(本身跟本身關聯) 一對多自身
user article content parent
1 1 666
1 2 888
2 1 666
1 from django.db import models 2 3 # Create your models here. 4 from django.contrib.auth.models import AbstractUser 5 6 7 8 class UserInfo(AbstractUser): 9 10 phone = models.BigIntegerField(null=True) 11 create_time = models.DateField(auto_now_add=True) 12 #文件存放的路徑,avatar,自動給你創這個文件夾,默認頭像 13 #若是用戶上傳了一個頭像,就會把這個文件放到avatar這個文件夾下面,若是不上傳頭像我就用默認的這個 14 avatar = models.FileField(upload_to='avatar/',default='static/img/default.jpg') 15 blog = models.OneToOneField(to='Blog',null=True) #null=True 能夠不寫,不要樣式什麼的 16 17 #我的站點 18 class Blog(models.Model): 19 site_name = models.CharField(max_length=32) 20 site_title = models.CharField(max_length=64) 21 #存css樣式文件的樣式 22 theme = models.CharField(max_length=32) 23 24 class Category(models.Model): 25 name = models.CharField(max_length=32) 26 blog = models.ForeignKey(to='Blog') 27 28 class Tag(models.Model): 29 name = models.CharField(max_length=32) 30 blog = models.ForeignKey(to='Blog') 31 32 class Article(models.Model): 33 title = models.CharField(max_length=32) 34 desc = models.CharField(max_length=256) 35 #存大段文本 36 content = models.TextField() 37 create_time = models.DateField(auto_now_add=True) 38 #文章的評論數,點贊數,點踩數 39 comment_num = models.IntegerField(default=0) 40 up_num = models.IntegerField(default=0) 41 down_num = models.IntegerField(default=0) 42 blog = models.ForeignKey(to='Blog',null=True) 43 category = models.ForeignKey(to='Category',null=True) 44 tag = models.ManyToManyField(to='Tag',through='Article2Tag', through_fields=('article','tag')) #文章的標籤 45 46 #自創的第三張表 47 class Article2Tag(models.Model): 48 article = models.ForeignKey(to='Article') 49 tag = models.ForeignKey(to='Tag') 50 51 class UpAndDown(models.Model): 52 user = models.ForeignKey(to='UserInfo') 53 article = models.ForeignKey(to='Article') 54 #存0,1 55 is_up = models.BooleanField() 56 57 58 59 class Comment(models.Model): 60 user = models.ForeignKey(to='UserInfo') 61 article = models.ForeignKey(to='Article') 62 content = models.CharField(max_length=128) 63 create_time = models.DateField(auto_now_add=True) 64 parent = models.ForeignKey(to='self',null=True)
注意(默認值設計):css
UserInfo表須要注意的(默認值):html
1.phone =models.BigIntegerField(null=True)前端
2.create_time=models.DateField(auto_now_add=True)python
3.blog = models.OneToOneField(to='Blog',null=True) 能夠不要樣式mysql
Article表jquery
1.create_time = models.DateField(auto_now_add=True)ajax
2.blog = models.ForeignKey(to='Blog',null=True)sql
3.category = models.ForeignKey(to='Category',null=True)數據庫
4.content = models.TextField() #存大段文本django
Comment表
1.create_time = models.DateField(auto_now_add=True)
2.parent = models.ForeignKey(to='self',null=True)
UpAndDown表
1.is_up= models.BooleanField()
用戶表用的是auth認證,
1.from django.contrib.auth.models import AbstractUser
class UserInfo(AbstractUser):須要繼承
2.settings配置
指定本身的auth用戶表
AUTH_USER_MODEL = 'app01.UserInfo'
鏈接數據庫
1.navicat建立數據庫
2.
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'bbs',
'HOST':'127.0.0.1',
'PORT':3306,
'USER':'root',
'PASSWORD':'123',
}
}
3.到__init__文件中導入
import pymysql
pymysql.install_as_MySQLdb()
配置靜態文件
STATICFILES_DIRS= [
os.path.join(BASE_DIR, 'static')
]
數據遷移,執行兩行命令
python3 manage.py makemigrations
python3 manage.py migrate
建立自定義from組件
在app01下建立文件myforms.py
1 from django import forms 2 from django.forms import widgets 3 from app01 import models 4 5 6 class RegForm(forms.Form): 7 username = forms.CharField(max_length=8,min_length=3,label='用戶名',error_messages={ 8 'max_length':'用戶名最長8位', 9 'min_length':'用戶名最少3位', 10 'required':'用戶名不能爲空', 11 },widget=widgets.TextInput(attrs={'class':'form-control'})) 12 password = forms.CharField(max_length=8, min_length=3, label='密碼', error_messages={ 13 'max_length': '密碼最長8位', 14 'min_length': '密碼最少3位', 15 'required': '密碼不能爲空', 16 }, widget=widgets.PasswordInput(attrs={'class': 'form-control'})) 17 confirm_password = forms.CharField(max_length=8, min_length=3, label='確認密碼', error_messages={ 18 'max_length': '確認密碼最長8位', 19 'min_length': '確認密碼最少3位', 20 'required': '確認密碼不能爲空', 21 }, widget=widgets.PasswordInput(attrs={'class': 'form-control'})) 22 email = forms.EmailField(label='郵箱',error_messages={ 23 'invalid':'郵箱格式錯誤', 24 'required':'郵箱不能爲空' 25 },widget=forms.EmailInput(attrs={"class":'form-control'})) 26 27 # 局部鉤子 校驗用戶名是否已存在 28 def clean_username(self): 29 username = self.cleaned_data.get('username') 30 user = models.UserInfo.objects.filter(username=username).first() 31 if user: 32 self.add_error('username','用戶名已存在') 33 else: 34 return username 35 36 # 全局鉤子 校驗密碼是否一致 37 def clean(self): 38 password = self.cleaned_data.get('password') 39 confirm_password = self.cleaned_data.get('confirm_password') 40 if not password == confirm_password: 41 self.add_error('confirm_password','兩次密碼不一致') 42 else: 43 return self.cleaned_data
鏈接路由
1 from django.conf.urls import url 2 from django.contrib import admin 3 4 urlpatterns = [ 5 url(r'^admin/', admin.site.urls), 6 ]
包裹在div中,方便調節樣式,這個加大間距
<div class="form-group">
{{ form.label }} {{ form }}
<span class="errors"></span>
</div>
‘註冊居中’
<h2 class="text-center">註冊</h2>
分割線
<hr>
{#input框對象點auto_id可以直接獲取input的id值#}
<p><label for="{{ foo.auto_id }}">{{ foo.label }}</label>
{{ foo }}
<span class="errors pull-right" style="color: red"></span>
</p>

$('#id_submit').click(function () {
console.log($('#myform').serializeArray())
})
$('#id_submit').click(function () {
var formData = new FormData();
$.each($('#myform').serializeArray(),function(index,obj){
formData.append(obj.name,obj.value)
})
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script> 7 <meta name="viewport" content="width=device-width, initial-scale=1"> 8 <link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.min.css"> 9 <script src="/static/bootstrap-3.3.7/js/bootstrap.min.js"></script> 10 </head> 11 <body> 12 <div class="container-fluid"> 13 <div class="row"> 14 <div class="col-md-6 col-md-offset-3"> 15 <h2 class="text-center">註冊</h2> 16 <hr> 17 <form id="myform"> 18 {% csrf_token %} 19 {% for foo in form_obj %} 20 <div class="form-group"> 21 {#input框對象點auto_id可以直接獲取input的id值#} 22 <p><label for="{{ foo.auto_id }}">{{ foo.label }}</label> 23 {{ foo }} 24 <span class="errors pull-right" style="color: red"></span> 25 </p> 26 </div> 27 {% endfor %} 28 <div class="form-group"> 29 <label for="id_myfile">頭像 30 <img src="/static/img/default.jpg" alt="" width="80" style="margin-left: 20px" id="id_img"> 31 </label> 32 <input type="file" name="myfile" id="id_myfile" style="display: none;"> 33 </div> 34 <input type="button" class="btn btn-primary pull-right" id="id_button" value="提交"> 35 </form> 36 </div> 37 </div> 38 </div> 39 40 <script> 41 $('#id_myfile').change(function () { 42 // 獲取當前用戶上傳到的文件對象 43 var myfileObj = $(this)[0].files[0]; 44 // 須要用文件閱讀器這個內置對象 45 var fileReader = new FileReader(); 46 // 將文件對象丟給文件閱讀器 47 fileReader.readAsDataURL(myfileObj); 48 // 將文件對象放入img標籤的src屬性中 49 // 當文件對象所有加載完畢再渲染 50 fileReader.onload = function(){ 51 $('#id_img').attr('src',fileReader.result); 52 } 53 }); 54 $('#id_button').click(function () { 55 var formData = new FormData(); 56 {#console.log($('#myform').serializeArray()); 自動獲取form表單中全部input框鍵值對#} 57 $.each($('#myform').serializeArray(),function (index,obj) { 58 {#console.log(index,obj) 知識添加了普通的鍵值對,文件對象須要你手動添加#} 59 formData.append(obj.name,obj.value) 60 }); 61 {#手動添加文件對象#} 62 formData.append('myfile',$('#id_myfile')[0].files[0]); 63 $.ajax({ 64 url:'', 65 type:'post', 66 data:formData, 67 // 用formdata傳數據的時候須要指定兩個參數 68 processData:false, 69 contentType:false, 70 success:function (data) { 71 if(data.code == 100){ 72 location.href = data.url 73 }else{ 74 $.each(data.msg,function (index,obj) { 75 // 手動拼接處forms組件渲染的input的id值 id_字段的特色 76 var targetId = '#id_' + index; 77 $(targetId).next().html(obj[0]).parent().parent().addClass('has-error') 78 }) 79 } 80 } 81 }) 82 }); 83 $('input').focus(function () { 84 $(this).next().html('').parent().parent().removeClass('has-error') 85 }) 86 87 </script> 88 </body> 89 </html>
1 from django.shortcuts import render 2 from app01 import myforms 3 from app01 import models 4 from django.http import JsonResponse 5 # Create your views here. 6 def register(request): 7 back_dic = {'code':100,'msg':''} 8 form_obj = myforms.MyForm() 9 if request.method == 'POST': 10 form_obj = myforms.MyForm(request.POST) 11 if form_obj.is_valid(): 12 data = form_obj.cleaned_data 13 # 將confirm_password去掉 14 data.pop('confirm_password') 15 # 獲取用戶上傳的文件對象 16 file_obj = request.FILES.get('myfile') 17 # 判斷用戶是否上傳了本身的頭像 18 if file_obj: 19 # 往data添加一組鍵值 20 data['avatar'] = file_obj 21 models.UserInfo.objects.create_user(**data) 22 back_dic['msg'] = '註冊成功' 23 back_dic['url'] = '/login/' 24 else: 25 back_dic['code'] = 101 26 back_dic['msg'] = form_obj.errors 27 return JsonResponse(back_dic) 28 return render(request,'register.html',locals())
總結
BBS
項目流程
需求分析
項目設計(架構設計,框架選擇,數據庫...報價)
分任務開發(小組成員開發)
測試(測試)
交付上線(運行)
用戶表(AbstractUser) settings文件必定要告訴django AUTH_USER_MODEL = 'app01.UserInfo'
phone
avatar
create_time
blog 一對一我的站點
我的站點(Blog)
site_name
site_title
site_theme
分類表
name
blog 一對多我的站點
標籤表
name
blog 一對多我的站點
文章表
title
desc
content
create_time
# 數據庫優化
comment_num
up_num
down_num
blog 一對多我的站點
category 一對多分類表
tag 多對多標籤表
點贊點踩表
user 一對多用戶
article 一對多文章
is_up 0/1
評論表
user 一對多用戶
article 一對多文章
comment
parent to='self',null=True
1.寫forms組件
username
password
confirm_password
# 局部鉤子 校驗用戶名是否存在
# 全局鉤子 校驗密碼是否一致
2.搭建註冊頁面
1.利用forms組件渲染前端頁面,手動添加獲取用戶頭像的input框
2.將img標籤放入label中,將input框隱藏
3.利用文件閱讀器動態展現用戶上傳的頭像
注意:須要等待文件閱讀器讀取完畢以後再賦值給src屬性,利用onload
4.ajax發送post請求
利用內置對象FormData傳遞數據
利用form標籤序列化數組
手動獲取文件對象$('[input="file"]')[0].files[0]
formdata發數據須要手動修改兩個參數
processData:false
contentType:false
後端
利用cleaned_data是一個大字典特性,將confirm_password鍵值去掉
手動獲取用戶頭像,判斷用戶是否上傳頭像,再決定要不要放入cleaned_data字典中
利用**{}將字典打散成關鍵字參數的形式
ps:在用ajax作先後端交互的時候一般後端都會實現定義一個字典做爲數據交互的媒介
ps:img標籤src屬性能夠放文件路徑,也能夠放文件二進制數據,還能夠放url!
# 昨日內容回顧
# 登陸
# 1.搭建前端頁面(用戶名,密碼,圖片驗證碼)
# 2.圖片如何動態生成,實時變化
# 1.img標籤的src它既能夠寫文件路徑,也能夠放圖片二進制數據,還能夠放url
# 2.推導步驟讀取本地的圖片二進制數據
# 3.利用pillow模塊動態生成圖片(最新是6.0版本,建議使用4.0~5.0)
# from PIL import Image,ImageDraw,ImageFont
# Image.new("RGB",(長,寬),'red')
# Image.new("RGB",(長,寬),(255,255,255))
# 4.爲了可以讓圖片的顏色也能動態變化
# import random
# def get_random():
# return random.randint(0,255),random.randint(0,255),random.randint(0,255)
# 5.利用內存管理器模塊io
# from io import BytesIO
# io_obj = BytesIO() # 就當成文件句柄
# img_obj = Image.new("RGB",(長,寬),'red')
# img_obj.save(io_obj,'png')
# 6.圖片上寫字,並生成隨機驗證碼
# 1.生成一個畫筆對象(將生成好的圖片當作參數傳入實例化產生對象)
# 2.生成一個字體對象(字體文件.ttf,字體大小)
# 3.隨機驗證碼(數字+小寫字母+大寫字母)
# 外層for循環規定驗證碼的位數
# 內部利用random.choice每次從數字+小寫字母+大寫字母三個中取一個
# 利用畫筆對象往圖片上寫字(寫的位置xy,寫的內容,隨機顏色數,字體對象)
# 4.將生成好的隨機驗證碼保存到session中爲了在以後的登陸頁面中校驗用戶輸入的驗證碼是否正確
# 7.爲了頁面更加人性化,給展現驗證碼的圖片綁定了點擊事件。用戶每點一次局部刷新驗證碼
# 利用img標籤src屬性在放url的時候。一旦url發生變化。會自動出現朝當前url地址請求數據
#
# 3.獲取用戶輸入的用戶名 密碼 驗證碼
# 先校驗驗證碼是否一致(能夠不忽略大小寫,統一轉成小寫或大寫進行比較)
# 利用auth模塊校驗用戶名和密碼是否正確user_obj = auth.authenticate(username=username,password=password)
# 利用auth模塊記錄當前用戶登陸狀態auth.login(request,user_obj)
# 4.跳轉到主頁
# 頂部導航條
# 右側根據用戶是否登陸動態控制展現的內容
# 當用戶登陸的狀況下展現用戶名和更多操做
# 修改密碼
# 利用auth模塊修改密碼
# request.user.check_password()
# request.user.set_password()
# request.user.save() # 必定要save()
# 修改頭像
# 註銷
# auth.logout(request)
# 當用戶沒有登陸的狀況下展現註冊和登陸
#
#
#
#
#
# 主頁搭建
# 我的站點
# 側邊欄
# 分類展現
# 標籤展現
# 日期歸檔
# 側邊欄篩選功能
#
# 文章詳情頁
# 文章的點贊點踩
#
#
#
# django admin後臺管理
# 1.必須是超級用戶才能夠登陸後臺管理
# createasuperuser
# 2.須要將模型表註冊到對應應用名下的admin.py文件中
#
#
#
# 網站用的靜態文件都放在static文件夾下
# 將用戶上傳的靜態文件單獨放在另一個文件夾(media文件夾下)
# media
# avater
# files
# jianli
# ziliao
#
#
# 第一件事
# 規定用戶上傳的文件都統一放到media文件夾下
# settings文件中配置用戶上傳的文件存放位置
#
# 第二件事
# 將media文件夾暴露給外界能夠直接訪問
#
# media配置
# 可以制定暴露給用戶後端服務器資源文件
#
# MEDIA_ROOT = os.path.join(BASE_DIR,'media') # 用戶上傳的文件會自動存放到該文件夾而且該文件夾不要你手動建立
#
# 手動配置路由
# from django.views.static import servee
# # 手動配置media文件路徑
# url(r'^media/(?P<path>.*)',serve,{'document_root':settings.MEDIA_ROOT})
#
# 上述配置完,就會將後端media文件夾全部的資源暴露給用戶
#
# """
# 基於該方法,你能夠作到任意暴露後端任何資源給用戶
# 在做死的邊緣試探
# MEDIA_ROOT = os.path.join(BASE_DIR,'app01') # 用戶上傳的文件會自動存放到該文件夾而且該文件夾不要你手動建立
#
# url(r'^app01/(?P<path>.*)',serve,{'document_root':settings.MEDIA_ROOT})
# 上述配置會將後端邏輯代碼數據庫代碼所有暴露給用戶,慎重使用
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!FBI warining!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# """
#
#
# 圖片防盜鏈
# Referer: https://www.cnblogs.com/ # 標記你上一次是從哪一個頁面訪問過來的
#
# 該功能能夠再中間件中作
# 訪問頻率校驗
# referer
# 身份驗證
#
#
#
#
# 日期歸檔
#
# id create_time month
# 1 2018-06-21 2018-06
# 2 2019-08-11 2019-08
# 3 2019-08-21 2019-08
# 4 2019-03-11 2019-03
#
# -官方提供
# from django.db.models.functions import TruncMonth
# models.Article.objects
# .annotate(month=TruncMonth('create_time')) # Truncate to month and add to select list
# .values('month') # Group By month
# .annotate(c=Count('id')) # Select the count of the grouping
# .values('month', 'c') # (might be redundant, haven't tested) select month and count
#
#
# 側邊欄
# 1.先搭建前端頁面
# 分類
# 標籤
# 日期歸檔
#
# 若是按照日期歸檔的時候出錯了
#
#
#
#
# 2.側邊欄篩選功能
# 基於已經有了的當前用戶全部的文章
# 進行對應的篩選(利用queryset鏈式操做)
#
# 上週五內容回顧# 首頁搭建# 用戶頭像展現# 網站使用的靜態文件統一都放在static文件夾# 用戶上傳的靜態文件統一放到另一個文件夾(media)# # 1.須要在settings文件中配置# MEDIA_ROOT = os.path.join(BASE_DIR,'media')# """# 1.自動建立media文件夾# 2.用戶上傳的文件會自動放入其中# """# # 2.手動去urls路由層配置# from django.view.static import serve# from BBS import settings# url(r'^media/(?P<path>.*)',serve,{'document_root':settings.MEDIA_ROOT})# """# 1.將media文件內全部的資源所有暴露給用戶,用戶只須要輸入具體的文件路徑# 就能夠放到文件內容# """# # 3.上述配置能夠暴露後端任何文件夾的資源# MEDIA_ROOT = os.path.join(BASE_DIR,'app01')# from django.view.static import serve# from BBS import settings# url(r'^media/(?P<path>.*)',serve,{'document_root':settings.MEDIA_ROOT})# """# 1.將media文件內全部的資源所有暴露給用戶,用戶只須要輸入具體的文件路徑# 就能夠放到文件內容# """# 前端頭像渲染# user_obj.avatar # avatar/111.png# <img src='/media/{{user_obj.avatar}}/'/># 我的站點# url(r'^(?P<username>\w+)/',views.site)# 左側欄+篩選# 文章分類# # 統計當前用戶每一個分類名及分類下的文章數# 文章標籤# # 統計當前用戶每一個標籤名及標籤下的文章數# 日期歸檔# # 按照年月對文章進行統計# id create_time month# 1 2018-06-21 2018-06# 2 2019-08-11 2019-08# 3 2019-08-21 2019-08# 4 2019-03-11 2019-03## -官方提供# from django.db.models.functions import TruncMonth# models.Article.objects# .annotate(month=TruncMonth('create_time')) # Truncate to month and add to select list# .values('month') # Group By month# .annotate(c=Count('id')) # Select the count of the grouping# .values('month', 'c') # (might be redundant, haven't tested) select month and count## # 1.寫三條路由跟分類,標籤,日期對應上,讓後端可以知道你想按找什麼條件進行篩選# url(r'^(?P<username>\w+)/(?P<condition>category|tag|archive)/(?P<param>.*)/',views.site)## def site(request,username,**kwargs):# pass# # 經過判斷kwargs是否有值從而決定返回的頁面是我的全部文章仍是篩選以後的文章## inclusion_tag# 自定義inclusion_tag完成側邊欄的渲染# # 1.在對應的應用名下建立一個名爲templatetags文件夾# # 2.在該文件夾內建立一個任意名字的py文件(mytag)# # 3.在文件中固定先寫如下兩句# from django import template## register = template.Library()## @register.inclusion_tag('left_menu.html',name='lm')# def left_menu(username):# return {}### 前端頁面# {% load mytag %}# {% lm xxx %}## 文章詳情頁# 文章內容其實就是一堆html代碼# 文章內容的渲染須要轉義## 點贊點踩# # 1.先搭建點贊點踩前端樣式(直接拷貝博客園樣式 把代碼和對應的css樣式都拷貝過來)# # 2.給點贊點踩標籤(給他們取一個共同的類屬性),給這個類屬性綁定一個點擊事件# # 3.利用$(this)指代的就是當前被點擊對象的特色再經過判斷當前被點擊點擊有沒有具體的類屬性從而區分是點贊仍是點踩# # 4.發送ajax請求# """# 後端應該單獨開一個專門處理點贊點踩邏輯的視圖函數(由於點贊點踩後端邏輯比較複雜)# """# 後端# """# 1.先校驗當前用戶是否登陸# 2.當前用戶點擊的文件是不是本身寫的# 3.判斷當前用戶是否已經點過了# 4.記錄的數據必定要在兩個地方進行修改,保持數據的一致性# """##### 文章評論# 文章的根評論# 1.前端頁面渲染(用戶評論框)# 2.後端評論邏輯# 3.前端渲染評論列表# render渲染# bom渲染###### 文章的子評論# 1.前端渲染評論列表# render渲染# bom渲染### 1.點擊回覆按鈕發生了幾件事# 1.把你想評論的那條評論的人名自動添加到textarea中(@+用戶名)# 2.自動換行# 3.textarea自動聚焦## 2.注意給回覆按鈕綁定點擊事件的時候,儘可能不要用id,由於回覆按鈕不止一個,而標籤id是不能重複的## 3.獲取評論人名及評論id的時候,能夠利用標籤能夠支持自定義任意的屬性來幫你快速的存儲及獲取數據## 4.子評論在存儲的時候應該將@人名清除## 5.必定要將全局的根評論id清空#### 後臺管理