Django小项目简单BBS论坛
開發一個簡單的BBS論壇
項目需求:
1 整體參考“抽屜新熱榜” + “虎嗅網” 2 實現不同論壇版塊 3 帖子列表展示 4 帖子評論數、點贊數展示 5 在線用戶展示 6 允許登錄用戶發貼、評論、點贊 7 允許上傳文件 8 帖子可被置頂 9 可進行多級評論知識必備:(注:沒有必備下面知識的同學,請返回去看會之后再看下面的內容防止蒙了~~!)
1 Django 2 HTML\CSS\JS 3 BootStrap 4 Jquery設計表結構
1、表結構重要性
在開發任何項目的時候,設計到數據庫,第一個事情要做的是設計表結構。表結構設計不好就不要寫代碼,表結構是體現了你業務邏輯關系的。你的數據都要往數據庫里存,其實表結構你要理清了你的架構也就出來了!
2、設計表
?
#!/usr/bin/env python #-*- coding:utf-8 -*-from __future__ import unicode_literals from django.db import models from django.contrib.auth.models import User# Create your models here.class Article(models.Model):'''帖子表'''#標題最大長度255,不能重名title = models.CharField(u'文章標題',max_length=255,unique=True)#發布辦款-使用外鍵關聯Categorycategory = models.ForeignKey("Category",verbose_name='板塊名稱')'''這里在admin中,title默認是顯示英文的,我們可以在他的最前面加要給字段,在admin中就可以顯示中文,他和verbose_name一樣,什么時候必須使用verbose_name呢?比如上面的{category = models.ForeignKey("Category",verbose_name='板塊名稱')} 這個字段第一個字段是關聯的類,這里就必須使用verbose_name'''#上傳文件head_img = models.ImageField(upload_to="uploads")#文章內容(文章內容可能有很多,所以我們就不用"CharField"來寫了,我們用TextField,不用規定他多長了,為可擴展長度)content = models.TextField(u"內容")#文章作者author = models.ForeignKey("UserProfile",verbose_name="作者")#發布日期publish_date = models.DateTimeField(auto_now=True,verbose_name="發布日期")#是否隱藏hidden = models.BooleanField(default=False,verbose_name="是否隱藏")#帖子的優先級priority = models.IntegerField(default=1000,verbose_name="優先級")def __unicode__(self):return "<%s,author:%s>" % (self.title,self.author)class Comment(models.Model):'''評論表'''#評論是基于文章的,并且一條評論只屬于一個文章!對多的關系#一個文章可以有多個評論,一個評論只屬于一個文章#評論文章article = models.ForeignKey("Article")#評論用戶user = models.ForeignKey("UserProfile")#評論內容comment = models.TextField(max_length=1000)#評論時間date = models.DateTimeField(auto_now=True)#多級評論,是不是評論評論的當前的表(自己表),所以就得和自己做一個關聯!#這里在關聯自己的時候必須設置一個related_name否則會報錯沖突#這里parent_comment,必須設置為可以為空,因為如果他是第一評論他是沒有父ID的parent_comment = models.ForeignKey("self",related_name='p_comment',blank=True,null=True)'''prent selfNull 11 21 32 4通過上面的這種方法來記錄,評論的級別關系!'''def __unicode__(self):return "<user:%s>" %(self.user) class ThumbUp(models.Model):'''點贊'''#給那個文章點的article = models.ForeignKey('Article')#用戶名user = models.ForeignKey('UserProfile')#時間date = models.DateTimeField(auto_now=True)class Category(models.Model):'''板塊表'''#板塊名稱name = models.CharField(max_length=64,unique=True,verbose_name="板塊名稱")#板塊管理員admin = models.ManyToManyField("UserProfile",verbose_name="模塊管理員")def __unicode__(self):return self.nameclass UserProfile(models.Model):'''用戶表'''#使用Django提供的用戶表,直接繼承就可以了.在原生的User表里擴展!(原生的User表里就有用戶名和密碼)#一定要使用OneToOne,如果是正常的ForeignKey的話就表示User中的記錄可以對應UserProfile中的多條記錄!#并且OneToOne的實現不是在SQL級別實現的而是在代碼基本實現的!user = models.OneToOneField(User)#名字name = models.CharField(max_length=32)#屬組groups = models.ManyToManyField("UserGroup")def __unicode__(self):return self.nameclass UserGroup(models.Model):'''用戶組表'''name = models.CharField(max_length=64,unique=True)def __unicode__(self):return self.name?
配置Django Admin
配置admin注冊model,不要忘記創建Django 管理員用戶
from django.contrib import admin import models # Register your models here. admin.site.register(models.Article) admin.site.register(models.Category) admin.site.register(models.Comment) admin.site.register(models.ThumbUp) admin.site.register(models.UserProfile) admin.site.register(models.UserGroup)我創建了幾個板塊,我在板塊中查看的時候。只能看到下面簡單的信息:
這里我想看到板塊中的ID或其他信息怎么辦?
#!/usr/bin/env python #-*- coding:utf-8 -*-from django.contrib import admin import models # Register your models here.#給某個表專門的定制的類class CategoryAdmin(admin.ModelAdmin):list_display = ('id','name')class ArticleAdmin(admin.ModelAdmin):list_display = ('id','title','author','hidden','publish_date')admin.site.register(models.Article,ArticleAdmin) #把自定義的類綁定到注冊的類中 admin.site.register(models.Category,CategoryAdmin) #把自定義的類綁定到注冊的類中admin.site.register(models.Comment) admin.site.register(models.ThumbUp) admin.site.register(models.UserProfile) admin.site.register(models.UserGroup)效果如下:
前端頁面&URLorViews配置
1、url別名使用
url里配置別名
url(r'^category/(\d+)/$',views.category,name='category'),html里配置的時候就只認那個別名了
<li role="presentation"><a href="{% url 'category' 1 %}">歐美專區</a></li><li role="presentation"><a href="{% url 'category' 2 %}">日韓專區</a></li><li role="presentation"><a href="{% url 'category' 3 %}">印度專區</a></li>別名的好處:如果說那天想修改url里的這個url名稱了,是不是所有前端都得修改!并且在有好幾層的時候怎么改使用別名就會非常方便了!
2、前端頁面寫完之后發現圖片無法正常顯示
出現這個問題的原因:他能找到uploads這個目錄嗎?他能直接訪問這個目錄嗎?他不能直接訪問不了!
- 一個是在Linux環境下做一個軟連接連接過去
如果在settings里加入uploads這個目錄,但是這個方法還是有問題!他會去找/static/uploads/uploads目錄,看下面的圖!
但是通過下面的方式就可以訪問(原因就是因為:他去/static/uploads/uploads目錄找了)
2.2、我們自己寫上傳的方法
?定義form表單認證
#!/usr/bin/env python #-*- coding:utf-8 -*- # Tim Luo LuoTianShuaifrom django import formsclass ArticleForm(forms.Form):title = forms.CharField(max_length=255,min_length=5)summary = forms.CharField(max_length=255,min_length=5)head_img = forms.ImageField()content = forms.CharField(min_length=10)category_id = forms.IntegerField()定義上傳方法
#!/usr/bin/env python #-*- coding:utf-8 -*- # Tim Luo LuoTianShuaiimport osdef handle_upload_file(f,request): #f這里獲取到文件句柄base_img_upload_path = 'static/Uploads'user_path = "%s/%s" % (base_img_upload_path,request.user.userprofile.id)if not os.path.exists(user_path):os.mkdir(user_path)with open('%s/%s'% (user_path,f.name),'wb+') as destinations:for chunk in f.chunks():destinations.write(chunk)#為了防止用戶傳送圖片進行沖突,我們為每個用戶進行創建用戶return "/static/Uploads/%s/%s" % (request.user.userprofile.id,f.name)定義views
def new_article(request):category_list = models.Category.objects.all()if request.method == 'POST':form = ArticleForm(request.POST,request.FILES)if form.is_valid():form_data = form.cleaned_dataform_data['author_id'] = request.user.userprofile.id#自定義圖片上傳new_img_path = handle_upload_file(request.FILES['head_img'],request)#但是在views也保存了一份,我們給他改掉改成我們自己的就行了form_data['head_img'] = new_img_path#create只能返回成功失敗,我想在創建完成之后返回文章的ID,直接下面那么寫就可以print form_datanew_article_obj = models.Article(**form_data)new_article_obj.save()#這個對象就直接返回了return render(request,'new_article.html',{'new_article_obj':new_article_obj}) #如果沒有這個變量說明是創建新文章呢else:print form.errorsreturn render(request,'new_article.html',{'category_list':category_list})?
?
?
多級評論實現
用戶可以直接對貼子進行評論,其它用戶也可以對別的用戶的評論再進行評論,也就是所謂的壘樓,如下圖:
所有的評論都存在一張表中, 評論與評論之前又有從屬關系,如何在前端 頁面上把這種層級關系體現出來?
首先咱們在存儲數據的時候是怎么來實現記錄層級關系的呢?(下面的圖是經過簡化的把其他列隱藏了)
我們在上面創建數據庫表結構的時候,就定義了一個外鍵為他們自己(parent_comment_id),如果他沒有父級別的ID說明他們是第一層,如果有說明他包含在一個評論之內!(仔細看上面的表結構)
先把評論簡化成一個這樣的模型:
data = [(None,'A'),('A','A1'),('A','A1-1'),('A1','A2'),('A1-1','A2-3'),('A2-3','A3-4'),('A1','A2-2'),('A2','A3'),('A2-2','A3-3'),('A3','A4'),(None,'B'),('B','B1'),('B1','B2'),('B1','B2-2'),('B2','B3'),(None,'C'),('C','C1'),]轉換為字典之后:
data_dic = {'A': {'A1': {'A2':{'A3':{'A4':{}}},'A2-2':{'A3-3':{}}}},'B':{'B1':{'B2':{'B3':{}},'B2-2':{}}},'C':{'C1':{}}}看上面的字典,我們能通過for循來獲取他有多少層嗎?當然不行,我們不知道他有多少層就沒有辦法進行找,或者通過while循環,最好是用遞歸進行一層一層的查找!
我們在前端展示的時候需要知道,那條數據是那一層的,不可能是壘下去的!因為他們是有層級關系的!
我們用后端來實現:咱們給前端返回一個字典這樣是不行的,咱們在后端把層級關系建立起來~返回的時候直接返回一個完整的HTML
轉換為字典之后就有層級關系了我們可以通過遞歸來實現了!上面再沒有轉換為字典的時候層級關系就不是很明確了!
?
在循環的過程中不斷的創建字典,先建立最頂級的,然后在一層一層的建立
先通過一個簡單的例子看下:
#!/usr/bin/env python #-*- coding:utf-8 -*- # Tim Luo LuoTianShuaidata = [(None,'A'),('A','A1'),('A','A1-1'),('A1','A2'),('A1-1','A2-3'),('A2-3','A3-4'),('A1','A2-2'),('A2','A3'),('A2-2','A3-3'),('A3','A4'),(None,'B'),('B','B1'),('B1','B2'),('B1','B2-2'),('B2','B3'),(None,'C'),('C','C1'),]def tree_search(d_dic,parent,son):#一層一層找,先撥第一層,一層一層往下找for k,v in d_dic.items():#舉例來說我先遇到A,我就把A來個深度查詢,A沒有了在找Bif k == parent:#如果等于就找到了parent,就吧son加入到他下面d_dic[k][son] = {} #son下面可能還有兒子#這里找到就直接return了,你找到就直接退出就行了returnelse:#如果沒有找到,有可能還有更深的地方,的需要剝掉一層 tree_search(d_dic[k],parent,son)data_dic = {}for item in data:# 每一個item代表兩個值一個父親一個兒子parent,son = item#先判斷parent是否為空,如果為空他就是頂級的,直接吧他加到data_dicif parent is None:data_dic[son] = {} #這里如果為空,那么key就是他自己,他兒子就是一個空字典else:'''如果不為空他是誰的兒子呢?舉例來說A3他是A2的兒子,但是你能直接判斷A3的父親是A2你能直接判斷他是否在A里面嗎?你只能到第一層.key所以咱們就得一層一層的找,我們知道A3他爹肯定在字典里了,所以就得一層一層的找,但是不能循環找,因為你不知道他有多少層,所以通過遞歸去找直到找到位置'''tree_search(data_dic,parent,son) #因為你要一層一層找,你的把data_dic傳進去,還的把parent和son傳進去for k,v in data_dic.items():print(k,v)執行結果:(完美)
('A', {'A1': {'A2': {'A3': {'A4': {}}}, 'A2-2': {'A3-3': {}}}, 'A1-1': {'A2-3': {'A3-4': {}}}}) ('C', {'C1': {}}) ('B', {'B1': {'B2-2': {}, 'B2': {'B3': {}}}})?2、前端返回
當咱們把這個字典往前端返回的時候,前端模板里是沒有一個語法遞歸的功能的,雖然咱們的層級關系已經出來了!所以咱們需要自定義一個模板語言然后拼成一個html然后返回給前端展示!
2.1、配置前端吧數據傳給simple_tag
{% load custom_tags %}{% build_comment_tree article_obj.comment_set.select_related %}2.2、simple_tag獲取數據然后把用戶穿過來的數據進行轉換為字典
#!/usr/bin/env python # -*- coding:utf-8 -*-from django import template from django.utils.safestring import mark_saferegister = template.Library()def tree_search(d_dic,comment_obj):#這里不用傳附近和兒子了因為他是一個對象,可以直接找到父親和兒子for k,v_dic in d_dic.items():if k == comment_obj.parent_comment:#如果找到了d_dic[k][comment_obj] = {} #如果找到父親了,你的把自己存放在父親下面,并把自己當做key,value為一個空字典returnelse:#如果找不到遞歸查找 tree_search(d_dic[k],comment_obj)@register.simple_tag def build_comment_tree(comment_list):'''把評論傳過來只是一個列表格式(如下),要把列別轉換為字典,在把字典拼接為html[<Comment: <A,user:羅天帥>>, <Comment: <A2-1,user:羅天帥>>, <Comment: <A3-1,user:羅天帥>>, <Comment: <A2-2,user:羅天帥>>,<Comment: <A4-1,user:羅天帥>>, <Comment: <A4-2,user:羅天帥>>, <Comment: <A5-1,user:羅天帥>>, <Comment: <A3-2,user:羅天帥>>,<Comment: <B2,user:羅天帥>>, <Comment: <B2-1,user:羅天帥>>]:param comment_list::return:'''comment_dic = {}#print(comment_list)for comment_obj in comment_list: #每一個元素都是一個對象if comment_obj.parent_comment is None: #如果沒有父親comment_dic[comment_obj] = {}else:#通過遞歸找 tree_search(comment_dic,comment_obj)# #測試:# for k,v in comment_dic.items():# print(k,v)# 上面完成之后開始遞歸拼接字符串2、3生成html標簽
#!/usr/bin/env python # -*- coding:utf-8 -*-from django import template from django.utils.safestring import mark_saferegister = template.Library()def tree_search(d_dic,comment_obj):#這里不用傳附近和兒子了因為他是一個對象,可以直接找到父親和兒子for k,v_dic in d_dic.items():if k == comment_obj.parent_comment:#如果找到了d_dic[k][comment_obj] = {} #如果找到父親了,你的把自己存放在父親下面,并把自己當做key,value為一個空字典returnelse:#如果找不到遞歸查找 tree_search(d_dic[k],comment_obj)def generate_comment_html(sub_comment_dic):#先創建一個html默認為空html = ""for k,v_dic in sub_comment_dic.items():#循環穿過來的字典html += "<div class='comment-node'>" + k.comment + "</div>"#上面的只是把第一層加了他可能還有兒子,所以通過遞歸繼續加if v_dic:html += generate_comment_html(v_dic)return html@register.simple_tag def build_comment_tree(comment_list):'''把評論傳過來只是一個列表格式(如下),要把列別轉換為字典,在把字典拼接為html[<Comment: <A,user:羅天帥>>, <Comment: <A2-1,user:羅天帥>>, <Comment: <A3-1,user:羅天帥>>, <Comment: <A2-2,user:羅天帥>>,<Comment: <A4-1,user:羅天帥>>, <Comment: <A4-2,user:羅天帥>>, <Comment: <A5-1,user:羅天帥>>, <Comment: <A3-2,user:羅天帥>>,<Comment: <B2,user:羅天帥>>, <Comment: <B2-1,user:羅天帥>>]:param comment_list::return:'''comment_dic = {}#print(comment_list)for comment_obj in comment_list: #每一個元素都是一個對象if comment_obj.parent_comment is None: #如果沒有父親comment_dic[comment_obj] = {}else:#通過遞歸找 tree_search(comment_dic,comment_obj)# #測試:# for k,v in comment_dic.items():# print(k,v)# 上面完成之后開始遞歸拼接字符串#div框架html = "<div class='comment-box'>"margin_left = 0for k,v in comment_dic.items():#第一層的htmlhtml += "<div class='comment-node'>" + k.comment + "</div>"#通過遞歸把他兒子加上html += generate_comment_html(v)html += "</div>"return mark_safe(html)效果如下:
2.4、上面的看起來不是很好看怎么辦?給他增加一個margin-left讓他來顯示層級效果,每次進行遞歸的時候給他加一個值!
#!/usr/bin/env python # -*- coding:utf-8 -*-from django import template from django.utils.safestring import mark_saferegister = template.Library()def tree_search(d_dic,comment_obj):#這里不用傳附近和兒子了因為他是一個對象,可以直接找到父親和兒子for k,v_dic in d_dic.items():if k == comment_obj.parent_comment:#如果找到了d_dic[k][comment_obj] = {} #如果找到父親了,你的把自己存放在父親下面,并把自己當做key,value為一個空字典returnelse:#如果找不到遞歸查找 tree_search(d_dic[k],comment_obj)def generate_comment_html(sub_comment_dic,margin_left_val):#先創建一個html默認為空html = ""for k,v_dic in sub_comment_dic.items():#循環穿過來的字典html += "<div style='margin-left:%spx' class='comment-node'>" % margin_left_val + k.comment + "</div>"#上面的只是把第一層加了他可能還有兒子,所以通過遞歸繼續加if v_dic:html += generate_comment_html(v_dic,margin_left_val+15)return html@register.simple_tag def build_comment_tree(comment_list):'''把評論傳過來只是一個列表格式(如下),要把列別轉換為字典,在把字典拼接為html[<Comment: <A,user:羅天帥>>, <Comment: <A2-1,user:羅天帥>>, <Comment: <A3-1,user:羅天帥>>, <Comment: <A2-2,user:羅天帥>>,<Comment: <A4-1,user:羅天帥>>, <Comment: <A4-2,user:羅天帥>>, <Comment: <A5-1,user:羅天帥>>, <Comment: <A3-2,user:羅天帥>>,<Comment: <B2,user:羅天帥>>, <Comment: <B2-1,user:羅天帥>>]:param comment_list::return:'''comment_dic = {}#print(comment_list)for comment_obj in comment_list: #每一個元素都是一個對象if comment_obj.parent_comment is None: #如果沒有父親comment_dic[comment_obj] = {}else:#通過遞歸找 tree_search(comment_dic,comment_obj)# #測試:# for k,v in comment_dic.items():# print(k,v)# 上面完成之后開始遞歸拼接字符串#div框架html = "<div class='comment-box'>"margin_left = 0for k,v in comment_dic.items():#第一層的htmlhtml += "<div class='comment-node'>" + k.comment + "</div>"#通過遞歸把他兒子加上html += generate_comment_html(v,margin_left+15)html += "</div>"return mark_safe(html)效果如下:
來源:?http://www.cnblogs.com/luotianshuai/p/5331982.html
來自為知筆記(Wiz)
轉載于:https://www.cnblogs.com/bruceg/p/5572687.html
總結
以上是生活随笔為你收集整理的Django小项目简单BBS论坛的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JS判断客户端是否是iOS或者Andro
- 下一篇: grunt 前端开发环境搭建