程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

Django框架項目——BBS項目介紹、表設計、表創建同步、注冊、登錄功能、登錄功能、首頁搭建、admin、頭像、圖片防盜、個人站點、側邊欄篩選、文章的詳情頁、點贊點踩、評論、後台管理、添加文章、頭像

編輯:Python

文章目錄

  • 1 BBS項目介紹、表設計
      • 項目開發流程
      • 表設計
  • 2 表創建同步、注冊、登錄功能
      • 數據庫表創建及同步
      • 注冊功能
      • 登陸功能
  • 3 登錄功能、首頁搭建、admin、頭像、圖片防盜、個人站點、側邊欄篩選
      • 登陸功能
      • 首頁搭建
      • admin後台管理
      • 用戶頭像展示
      • 圖片防盜鏈
      • 個人站點
      • 側邊欄篩選功能
  • 4 文章的詳情頁、點贊點踩、評論等功能
      • 擴展:admin路由分發的本質
      • 文章詳情頁
      • 文章點贊點踩
      • 文章評論
  • 5 後台管理、添加文章、修改用戶頭像、BBS總結
      • 後台管理
      • 添加文章
      • kindeditor富文本編輯器
      • 編輯器上傳圖片
      • 修改用戶頭像
      • bbs項目總結

1 BBS項目介紹、表設計

項目開發流程

# 1.需求分析
架構師+產品經理+開發者組長
在跟客戶談需求之前,會大致先了解客戶的需求,然後自己先設計一套比較好寫方案
在跟客戶溝通交流中引導客戶往我們之前想好的方案上面靠
形成一個初步的方案
# 2.項目設計
架構師干的活
編程語言選擇
框架選擇
數據庫選擇
主庫:MySQL,postgreSQL,...
緩存數據庫:redis、mongodb、memcache...
功能劃分
將整個項目劃分成幾個功能模塊
找組長開會
給每個組分發任務
項目報價
技術這塊需要多少人,多少天(一個程序員一天1500~2000計算(大致))
產品經理公司層面 再加點錢
公司財務簽字確認
公司老板簽字確認
產品經理去跟客戶溝通
後續需要加功能 繼續加錢
# 3.分組開發
組長找組員開會,安排各自功能模塊
我們其實就是在架構師設計好的框架裡面填寫代碼而已(碼畜)
我們在寫代碼的時候 寫完需要自己先測試是否有bug
如果是一些顯而易見的bug,你沒有避免而是直接交給了測試部門測出來
那你可能就需要被扣績效了(一定要跟測試小姐姐搞好關系)
薪資組成 15K(合理合規合法的避稅)
底薪 10K
績效 3K
崗位津貼 1K
生活補貼 1K
# 4.測試
測試部門測試你的代碼
壓力測試
...
# 5.交付上線
1.交給對方的運維人員
2.直接上線到我們的服務器上 收取維護費用
3.其他...

表設計

""" 一個項目中最最最重要的不是業務邏輯的書寫 而是前期的表設計,只要將表設計好了,後續的功能書寫才會一帆風順 bbs表設計 1.用戶表 繼承AbstractUser 擴展 phone 電話號碼 avatar 用戶頭像 create_time 創建時間 外鍵字段 一對一個人站點表 2.個人站點表 site_name 站點名稱 site_title 站點標題 site_theme 站點樣式 3.文章標簽表 name 標簽名 外鍵字段 一對多個人站點 4.文章分類表 name 分類名 外鍵字段 一對多個人站點 5.文章表 title 文章標題 desc 文章簡介 content 文章內容 create_time 發布時間 數據庫字段設計優化(******) (雖然下述的三個字段可以從其他表裡面跨表查詢計算得出,但是頻繁跨表效率) up_num 點贊數 down_num 點踩數 comment_num 評論數 外鍵字段 一對多個人站點 多對多文章標簽 一對多文章分類 6.點贊點踩表 記錄哪個用戶給哪篇文章點了贊還是點了踩 user ForeignKey(to="User") article ForeignKey(to="Article") is_up BooleanField() 1 1 1 1 2 1 1 3 0 2 1 1 7.文章評論表 記錄哪個用戶給哪篇文章寫了哪些評論內容 user ForeignKey(to="User") article ForeignKey(to="Article") content CharField() comment_time DateField() # 自關聯 parent ForeignKey(to="Comment",null=True) # ORM專門提供的自關聯寫法 parent ForeignKey(to="self",null=True) id user_id article_id parent_id 1 1 1 2 2 1 1 根評論子評論的概念 根評論就是直接評論當前發布的內容的 子評論是評論別人的評論 1.PHP是世界上最牛逼的語言 1.1 python才是最牛逼的 1.2 java才是 根評論與子評論是一對多的關系 """

2 表創建同步、注冊、登錄功能

數據庫表創建及同步

""" 由於django自帶的sqlite數據庫對日期不敏感,所以我們換成MySQL """
from django.db import models
# Create your models here.
""" 先寫普通字段 之後再寫外鍵字段 """
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)
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) # 存css/js的文件路徑
class Category(models.Model):
name = models.CharField(verbose_name='文章分類',max_length=32)
blog = models.ForeignKey(to='Blog',null=True)
class Tag(models.Model):
name = models.CharField(verbose_name='文章標簽',max_length=32)
blog = models.ForeignKey(to='Blog', null=True)
class Article(models.Model):
title = models.CharField(verbose_name='文章標題',max_length=64)
desc = models.CharField(verbose_name='文章簡介',max_length=255)
# 文章內容有很多 一般情況下都是使用TextField
content = models.TextField(verbose_name='文章內容')
create_time = models.DateField(auto_now_add=True)
# 數據庫字段設計優化
up_num = models.BigIntegerField(verbose_name='點贊數',default=0)
down_num = models.BigIntegerField(verbose_name='點踩數',default=0)
comment_num = models.BigIntegerField(verbose_name='評論數',default=0)
# 外鍵字段
blog = models.ForeignKey(to='Blog', null=True)
category = models.ForeignKey(to='Category',null=True)
tags = models.ManyToManyField(to='Tag',
through='Article2Tag',
through_fields=('article','tag')
)
class Article2Tag(models.Model):
article = models.ForeignKey(to='Article')
tag = models.ForeignKey(to='Tag')
class UpAndDown(models.Model):
user = models.ForeignKey(to='UserInfo')
article = models.ForeignKey(to='Article')
is_up = models.BooleanField() # 傳布爾值 存0/1
class Comment(models.Model):
user = models.ForeignKey(to='UserInfo')
article = models.ForeignKey(to='Article')
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) # 有些評論就是根評論

注冊功能

""" 我們之前是直接在views.py中書寫的forms組件代碼 但是為了接耦合 應該將所有的forms組件代碼單獨寫到一個地方 如果你的項目至始至終只用到一個forms組件那麼你可以直接建一個py文件書寫即可 myforms.py 但是如果你的項目需要使用多個forms組件,那麼你可以創建一個文件夾在文件夾內根據 forms組件功能的不同創建不同的py文件 myforms文件夾 regform.py loginform.py userform.py orderform.py ... """
def register(request):
form_obj = MyRegForm()
if request.method == 'POST':
back_dic = {
"code": 1000, 'msg': ''}
# 校驗數據是否合法
form_obj = MyRegForm(request.POST)
# 判斷數據是否合法
if form_obj.is_valid():
# print(form_obj.cleaned_data) # {'username': 'jason', 'password': '123', 'confirm_password': '123', 'email': '[email protected]'}
clean_data = form_obj.cleaned_data # 將校驗通過的數據字典賦值給一個變量
# 將字典裡面的confirm_password鍵值對刪除
clean_data.pop('confirm_password') # {'username': 'jason', 'password': '123', 'email': '[email protected]'}
# 用戶頭像
file_obj = request.FILES.get('avatar')
"""針對用戶頭像一定要判斷是否傳值 不能直接添加到字典裡面去"""
if file_obj:
clean_data['avatar'] = file_obj
# 直接操作數據庫保存數據
models.UserInfo.objects.create_user(**clean_data)
back_dic['url'] = '/login/'
else:
back_dic['code'] = 2000
back_dic['msg'] = form_obj.errors
return JsonResponse(back_dic)
return render(request,'register.html',locals())
<script>
$("#myfile").change(function () {

// 文件閱讀器對象
// 1 先生成一個文件閱讀器對象
let myFileReaderObj = new FileReader();
// 2 獲取用戶上傳的頭像文件
let fileObj = $(this)[0].files[0];
// 3 將文件對象交給閱讀器對象讀取
myFileReaderObj.readAsDataURL(fileObj) // 異步操作 IO操作
// 4 利用文件閱讀器將文件展示到前端頁面 修改src屬性
// 等待文件閱讀器加載完畢之後再執行
myFileReaderObj.onload = function(){

$('#myimg').attr('src',myFileReaderObj.result)
}
})
$('#id_commit').click(function () {

// 發送ajax請求 我們發送的數據中即包含普通的鍵值也包含文件
let formDataObj = new FormData();
// 1.添加普通的鍵值對
{
#console.log($('#myform').serializeArray()) // [{},{},{},{},{}] 只包含普通鍵值對#}
$.each($('#myform').serializeArray(),function (index,obj) {

{
#console.log(index,obj)#} // obj = {}
formDataObj.append(obj.name,obj.value)
});
// 2.添加文件數據
formDataObj.append('avatar',$('#myfile')[0].files[0]);
// 3.發送ajax請求
$.ajax({

url:"",
type:'post',
data:formDataObj,
// 需要指定兩個關鍵性的參數
contentType:false,
processData:false,
success:function (args) {

if (args.code==1000){

// 跳轉到登陸頁面
window.location.href = args.url
}else{

// 如何將對應的錯誤提示展示到對應的input框下面
// forms組件渲染的標簽的id值都是 id_字段名
$.each(args.msg,function (index,obj) {

{
#console.log(index,obj) // username ["用戶名不能為空"]#}
let targetId = '#id_' + index;
$(targetId).next().text(obj[0]).parent().addClass('has-error')
})
}
}
})
})
// 給所有的input框綁定獲取焦點事件
$('input').focus(function () {

// 將input下面的span標簽和input外面的div標簽修改內容及屬性
$(this).next().text('').parent().removeClass('has-error')
})
</script>
# 擴展
""" 一般情況下我們在存儲用戶文件的時候為了避免文件名沖突的情況 會自己給文件名加一個前綴 uuid 隨機字符串 ... """

登陸功能

""" img標簽的src屬性 1.圖片路徑 2.url 3.圖片的二進制數據 我們的計算機上面致所有能夠輸出各式各樣的字體樣式 內部其實對應的是一個個.ttf結尾的文件 字體網站鏈接:http://www.zhaozi.cn/ai/2019/fontlist.php?ph=1&classid=32&softsq=%E5%85%8D%E8%B4%B9%E5%95%86%E7%94%A8 """
""" 圖片相關的模塊 pip3 install pillow """
from PIL import Image,ImageDraw,ImageFont
""" Image:生成圖片 ImageDraw:能夠在圖片上亂塗亂畫 ImageFont:控制字體樣式 """
from io import BytesIO,StringIO
""" 內存管理器模塊 BytesIO:臨時幫你存儲數據 返回的時候數據是二進制 StringIO:臨時幫你存儲數據 返回的時候數據是字符串 """
import random
def get_random():
return random.randint(0,255),random.randint(0,255),random.randint(0,255)
def get_code(request):
# 推導步驟1:直接獲取後端現成的圖片二進制數據發送給前端
# with open(r'static/img/111.jpg','rb') as f:
# data = f.read()
# return HttpResponse(data)
# 推導步驟2:利用pillow模塊動態產生圖片
# img_obj = Image.new('RGB',(430,35),'green')
# img_obj = Image.new('RGB',(430,35),get_random())
# # 先將圖片對象保存起來
# with open('xxx.png','wb') as f:
# img_obj.save(f,'png')
# # 再將圖片對象讀取出來
# with open('xxx.png','rb') as f:
# data = f.read()
# return HttpResponse(data)
# 推導步驟3:文件存儲繁瑣IO操作效率低 借助於內存管理器模塊
# img_obj = Image.new('RGB', (430, 35), get_random())
# io_obj = BytesIO() # 生成一個內存管理器對象 你可以看成是文件句柄
# img_obj.save(io_obj,'png')
# return HttpResponse(io_obj.getvalue()) # 從內存管理器中讀取二進制的圖片數據返回給前端
# 最終步驟4:寫圖片驗證碼
img_obj = Image.new('RGB', (430, 35), get_random())
img_draw = ImageDraw.Draw(img_obj) # 產生一個畫筆對象
img_font = ImageFont.truetype('static/font/222.ttf',30) # 字體樣式 大小
# 隨機驗證碼 五位數的隨機驗證碼 數字 小寫字母 大寫字母
code = ''
for i in range(5):
random_upper = chr(random.randint(65,90))
random_lower = chr(random.randint(97,122))
random_int = str(random.randint(0,9))
# 從上面三個裡面隨機選擇一個
tmp = random.choice([random_lower,random_upper,random_int])
# 將產生的隨機字符串寫入到圖片上
""" 為什麼一個個寫而不是生成好了之後再寫 因為一個個寫能夠控制每個字體的間隙 而生成好之後再寫的話 間隙就沒法控制了 """
img_draw.text((i*60+60,-2),tmp,get_random(),img_font)
# 拼接隨機字符串
code += tmp
print(code)
# 隨機驗證碼在登陸的視圖函數裡面需要用到 要比對 所以要找地方存起來並且其他視圖函數也能拿到
request.session['code'] = code
io_obj = BytesIO()
img_obj.save(io_obj,'png')
return HttpResponse(io_obj.getvalue())
# 點圖片進行刷新驗證碼
<script>
$("#id_img").click(function () {

// 1 先獲取標簽之前的src
let oldVal = $(this).attr('src');
$(this).attr('src',oldVal += '?')
})
</script>

3 登錄功能、首頁搭建、admin、頭像、圖片防盜、個人站點、側邊欄篩選

登陸功能

def login(request):
if request.method == 'POST':
back_dic = {
'code':1000,'msg':''}
username = request.POST.get('username')
password = request.POST.get('password')
code = request.POST.get('code')
# 1 先校驗驗證碼是否正確 自己決定是否忽略 統一轉大寫或者小寫再比較
if request.session.get('code').upper() == code.upper():
# 2 校驗用戶名和密碼是否正確
user_obj = auth.authenticate(request,username=username,password=password)
if user_obj:
# 保存用戶狀態
auth.login(request,user_obj)
back_dic['url'] = '/home/'
else:
back_dic['code'] = 2000
back_dic['msg'] = '用戶名或密碼錯誤'
else:
back_dic['code'] = 3000
back_dic['msg'] = '驗證碼錯誤'
return JsonResponse(back_dic)
return render(request,'login.html')

首頁搭建

# 1.動態展示用戶名稱
{
% if request.user.is_authenticated %}
<li><a href="#">{
{
 request.user.username }}</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">更多操作 <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">修改密碼</a></li>
<li><a href="#">修改頭像</a></li>
<li><a href="#">後台管理</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">退出登陸</a></li>
</ul>
</li>
{
% else %}
<li><a href="{% url 'reg' %}">注冊</a></li>
<li><a href="{% url 'login' %}">登陸</a></li>
{
% endif %}
# 更多操作

admin後台管理

""" django給你提供了一個可視化的界面用來讓你方便的對你的模型表 進行數據的增刪改查操作 如果你先想要使用amdin後台管理操作模型表 你需要先注冊你的模型表告訴admin你需要操作哪些表 去你的應用下的admin.py中注冊你的模型表 from django.contrib import admin from app01 import models # Register your models here. admin.site.register(models.UserInfo) admin.site.register(models.Blog) admin.site.register(models.Category) admin.site.register(models.Tag) admin.site.register(models.Article) admin.site.register(models.Article2Tag) admin.site.register(models.UpAndDown) admin.site.register(models.Comment) """
# admin會給每一個注冊了的模型表自動生成增刪改查四條url
http://127.0.0.1:8000/admin/app01/userinfo/ 查
http://127.0.0.1:8000/admin/app01/userinfo/add/ 增
http://127.0.0.1:8000/admin/app01/userinfo/1/change/ 改
http://127.0.0.1:8000/admin/app01/userinfo/1/delete/ 刪
http://127.0.0.1:8000/admin/app01/blog/ 查
http://127.0.0.1:8000/admin/app01/blog/add/ 增
http://127.0.0.1:8000/admin/app01/blog/1/change/ 改
http://127.0.0.1:8000/admin/app01/blog/1/delete/ 刪
""" 關鍵點就在於urls.py中的第一條自帶的url 前期我們需要自己手動苦逼的錄入數據,自己克服一下 """
# 1.數據綁定尤其需要注意的是用戶和個人站點不要忘記綁定了
# 2.標簽
# 3.標簽和文章
千萬不要把別人的文章綁定標簽

用戶頭像展示

""" 1 網址所使用的靜態文件默認放在static文件夾下 2 用戶上傳的靜態文件也應該單獨放在某個文件夾下 media配置 該配置可以讓用戶上傳的所有文件都固定存放在某一個指定的文件夾下 # 配置用戶上傳的文件存儲位置 MEDIA_ROOT = os.path.join(BASE_DIR,'media') # 文件名 隨你 自己 會自動創建多級目錄 如何開設後端指定文件夾資源 首先你需要自己去urls.py書寫固定的代碼 from django.views.static import serve from BBS14 import settings # 暴露後端指定文件夾資源 url(r'^media/(?P<path>.*)',serve,{'document_root':settings.MEDIA_ROOT}) """

圖片防盜鏈

# 如何避免別的網站直接通過本網站的url訪問本網站資源
# 簡單的防盜
我可以做到請求來的時候先看看當前請求是從哪個網站過來的
如果是本網站那麼正常訪問
如果是其他網站直接拒絕
請求頭裡面有一個專門記錄請求來自於哪個網址的參數
Referer: http://127.0.0.1:8000/xxx/
# 如何避免
1.要麼修改請求頭referer
2.直接寫爬蟲把對方網址的所有資源直接下載到我們自己的服務器上

個人站點

# 全是每個用戶都可以有自己的站點樣式
<link rel="stylesheet" href="/media/css/{
{ blog.site_theme }}/">
id content create_time month
1 111 2020-11-11 2020-11
2 222 2020-11-12 2020-11
3 333 2020-11-13 2020-11
4 444 2020-11-14 2020-11
5 555 2020-11-15 2020-11
""" django官網提供的一個orm語法 from django.db.models.functions import TruncMonth -官方提供 from django.db.models.functions import TruncMonth Sales.objects .annotate(month=TruncMonth('timestamp')) # 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 時區問題報錯 TIME_ZONE = 'Asia/Shanghai' USE_TZ = True """

側邊欄篩選功能

https://www.cnblogs.com/jason/tag/Python/ 標簽
https://www.cnblogs.com/jason/category/850028.html 分類
https://www.cnblogs.com/jason/archive/2016/10.html 日期
https://www.cnblogs.com/jason/tag/1/ 標簽
https://www.cnblogs.com/jason/category/1 分類
https://www.cnblogs.com/jason/archive/2020-11/ 日期
def site(request,username,**kwargs):
""" :param request: :param username: :param kwargs: 如果該參數有值 也就意味著需要對article_list做額外的篩選操作 :return: """
# 先校驗當前用戶名對應的個人站點是否存在
user_obj = models.UserInfo.objects.filter(username=username).first()
# 用戶如果不存在應該返回一個404頁面
if not user_obj:
return render(request,'errors.html')
blog = user_obj.blog
# 查詢當前個人站點下的所有的文章
article_list = models.Article.objects.filter(blog=blog) # queryset對象 側邊欄的篩選其實就是對article_list再進一步篩選
if kwargs:
# print(kwargs) # {'condition': 'tag', 'param': '1'}
condition = kwargs.get('condition')
param = kwargs.get('param')
# 判斷用戶到底想按照哪個條件篩選數據
if condition == 'category':
article_list = article_list.filter(category_id=param)
elif condition == 'tag':
article_list = article_list.filter(tags__id=param)
else:
year,month = param.split('-') # 2020-11 [2020,11]
article_list = article_list.filter(create_time__year=year,create_time__month=month)
# 1 查詢當前用戶所有的分類及分類下的文章數
category_list = models.Category.objects.filter(blog=blog).annotate(count_num=Count('article__pk')).values_list('name','count_num','pk')
# print(category_list) # <QuerySet [('jason的分類一', 2), ('jason的分類二', 1), ('jason的分類三', 1)]>
# 2 查詢當前用戶所有的標簽及標簽下的文章數
tag_list = models.Tag.objects.filter(blog=blog).annotate(count_num=Count('article__pk')).values_list('name','count_num','pk')
# print(tag_list) # <QuerySet [('tank的標簽一', 1), ('tank的標簽二', 1), ('tank的標簽三', 2)]>
# 3 按照年月統計所有的文章
date_list = models.Article.objects.filter(blog=blog).annotate(month=TruncMonth('create_time')).values('month').annotate(count_num=Count('pk')).values_list('month','count_num')
# print(date_list)
return render(request,'site.html',locals())

4 文章的詳情頁、點贊點踩、評論等功能

擴展:admin路由分發的本質

# 路由分發本質 include 可以無限制的嵌套N多層
url(r'^index/',([],None,None))
# url(r'^index/',([
# url(r'^index_1/',([
# url(r'^index_1_1',index),
# url(r'^index_1_2',index),
# url(r'^index_1_3',index),
# ],None,None)),
# url(r'^index_2/',index),
# url(r'^index_3/',index),
# ],None,None)),

“”"

文章詳情頁

# url設計
/username/article/1
# 先驗證url是否會被其他url頂替
# 文章詳情頁和個人站點基本一致 所以用模版繼承
# 側邊欄的渲染需要傳輸數據才能渲染 並且該側邊欄在很多頁面都需要使用
1.哪個地方用就拷貝需要的代碼(不推薦 有點繁瑣)
2.將側邊欄制作成inclusion_tag
""" 步驟 1.在應用下創建一個名字必須叫templatetags文件夾 2.在該文件夾內創建一個任意名稱的py文件 3.在該py文件內先固定寫兩行代碼 from django import template register = template.Library() # 自定義過濾器 # 自定義標簽 # 自定義inclusion_tag """
# 自定義inclusion_tag
@register.inclusion_tag('left_menu.html')
def left_menu(username):
# 構造側邊欄需要的數據
user_obj = models.UserInfo.objects.filter(username=username).first()
blog = user_obj.blog
# 1 查詢當前用戶所有的分類及分類下的文章數
category_list = models.Category.objects.filter(blog=blog).annotate(count_num=Count('article__pk')).values_list(
'name', 'count_num', 'pk')
# print(category_list) # <QuerySet [('jason的分類一', 2), ('jason的分類二', 1), ('jason的分類三', 1)]>
# 2 查詢當前用戶所有的標簽及標簽下的文章數
tag_list = models.Tag.objects.filter(blog=blog).annotate(count_num=Count('article__pk')).values_list('name',
'count_num',
'pk')
# print(tag_list) # <QuerySet [('tank的標簽一', 1), ('tank的標簽二', 1), ('tank的標簽三', 2)]>
# 3 按照年月統計所有的文章
date_list = models.Article.objects.filter(blog=blog).annotate(month=TruncMonth('create_time')).values(
'month').annotate(count_num=Count('pk')).values_list('month', 'count_num')
# print(date_list)
return locals()

文章點贊點踩

""" 浏覽器上你看到的花裡胡哨的頁面,內部都是HTML(前端)代碼 那現在我們的文章內容應該寫什麼??? >>> html代碼 如何拷貝文章 copy outerhtml 1.拷貝文章 2.拷貝點贊點踩 1.拷貝前端點贊點踩圖標 只拷了html 2.css也要拷貝 由於有圖片防盜鏈的問題 所以將圖片直接下載到本地 課下思考: 前端如何區分用戶是點了贊還是點了踩 1.給標簽各自綁定一個事件 兩個標簽對應的代碼其實基本一樣,僅僅是是否點贊點踩這一個參數不一樣而已 2.二合一 給兩個標簽綁定一個事件 // 給所有的action類綁定事件 $('.action').click(function () { alert($(this).hasClass('diggit')) }) 由於點贊點踩內部有一定的業務邏輯,所以後端單獨開設視圖函數處理 """
# 個人建議:寫代碼先把所有正確的邏輯寫完再去考慮錯誤的邏輯 不要試圖兩者兼得
import json
from django.db.models import F
def up_or_down(request):
""" 1.校驗用戶是否登陸 2.判斷當前文章是否是當前用戶自己寫的(自己不能點自己的文章) 3.當前用戶是否已經給當前文章點過了 4.操作數據庫了 :param request: :return: """
if request.is_ajax():
back_dic = {
'code':1000,'msg':''}
# 1 先判斷當前用戶是否登陸
if request.user.is_authenticated():
article_id = request.POST.get('article_id')
is_up = request.POST.get('is_up')
# print(is_up,type(is_up)) # true <class 'str'>
is_up = json.loads(is_up) # 記得轉換
# print(is_up, type(is_up)) # True <class 'bool'>
# 2 判斷當前文章是否是當前用戶自己寫的 根據文章id查詢文章對象 根據文章對象查作者 根request.user比對
article_obj = models.Article.objects.filter(pk=article_id).first()
if not article_obj.blog.userinfo == request.user:
# 3 校驗當前用戶是否已經點了 哪個地方記錄了用戶到底點沒點
is_click = models.UpAndDown.objects.filter(user=request.user,article=article_obj)
if not is_click:
# 4 操作數據庫 記錄數據 要同步操作普通字段
# 判斷當前用戶點了贊還是踩 從而決定給哪個字段加一
if is_up:
# 給點贊數加一
models.Article.objects.filter(pk=article_id).update(up_num = F('up_num') + 1)
back_dic['msg'] = '點贊成功'
else:
# 給點踩數加一
models.Article.objects.filter(pk=article_id).update(down_num=F('down_num') + 1)
back_dic['msg'] = '點踩成功'
# 操作點贊點踩表
models.UpAndDown.objects.create(user=request.user,article=article_obj,is_up=is_up)
else:
back_dic['code'] = 1001
back_dic['msg'] = '你已經點過了,不能再點了' # 這裡你可以做的更加的詳細 提示用戶到底點了贊還是點了踩
else:
back_dic['code'] = 1002
back_dic['msg'] = '你個臭不要臉的!'
else:
back_dic['code'] = 1003
back_dic['msg'] = '請先<a href="/login/">登陸</a>'
return JsonResponse(back_dic)
<script>
// 給所有的action類綁定事件
$('.action').click(function () {

{
#alert($(this).hasClass('diggit'))#}
let isUp = $(this).hasClass('diggit');
let $div = $(this);
// 朝後端發送ajax請求
$.ajax({

url:'/up_or_down/',
type:'post',
data:{

'article_id':'{
{ article_obj.pk }}',
'is_up':isUp,
'csrfmiddlewaretoken':'{
{ csrf_token }}'
},
success:function (args) {

if(args.code == 1000){

$('#digg_tips').text(args.msg)
// 將前端的數字加一
// 先獲取到之前的數字
let oldNum = $div.children().text(); // 文本 是字符類型
// 易錯點
$div.children().text(Number(oldNum) + 1) // 字符串拼接了 1+1 = 11 11 + 1 = 111
}else{

$('#digg_tips').html(args.msg)
}
}
})
})
</script>

文章評論

""" 我們先寫根評論 再寫子評論 點擊評論按鈕需要將評論框裡面的內容清空 根評論有兩步渲染方式 1.DOM臨時渲染 2.頁面刷新render渲染 子評論 點擊回復按鈕發生了幾件事 1.評論框自動聚焦 2.將回復按鈕所在的那一行評論人的姓名 @username 3.評論框內部自動換行 根評論子評論都是點擊一個按鈕朝後端提交數據的 parent_id 根評論子評論區別在哪? parent_id """

5 後台管理、添加文章、修改用戶頭像、BBS總結

後台管理

""" 當一個文件夾下文件比較多的時候 你還可以繼續創建文件夾分類處理 templates文件夾 backend文件夾 應用1文件夾 應用2文件夾 """

添加文章

有兩個需要注意的問題
1.文章的簡介
不能直接切去
應該先想辦法獲取到當前頁面的文本內容之後截取150個文本字符
2.XSS攻擊
針對支持用戶直接編寫html代碼的網址
針對用戶直接書寫的script標簽 我們需要處理
1.注視標簽內部的內容
2.直接將script刪除
如何解決?
我們自己的話
針對1 後端通過正則表達式篩選
針對2 首先需要確定及獲取script標簽
這兩步都好煩 有木有人來幫我一下
beautifulsoup模塊 bs4模塊
專門用來幫你處理html頁面內的
該模塊主要用於爬蟲程序
下載千萬不要下錯了
pip3 install beautifulsoup4
# 模塊使用
soup = BeautifulSoup(content,'html.parser')
tags = soup.find_all()
# 獲取所有的標簽
for tag in tags:
# print(tag.name) # 獲取頁面所有的標簽
# 針對script標簽 直接刪除
if tag.name == 'script':
# 刪除標簽
tag.decompose()
# 文章簡介
# 1 先簡單暴力的直接切去content 150個字符
# desc = content[0:150]
# 2 截取文本150個
desc = soup.text[0:150]
""" 當你發現一個數據處理起來不是很方便的時候 可以考慮百度搜搜有沒有現成的模塊幫你完成相應的功能 """

kindeditor富文本編輯器

編輯器的種類有很多,你可以自己去網上搜索

編輯器上傳圖片

別人寫好了接口 但是接口不是你自己的
你需要手動去修改
# 在使用別人的框架或者模塊的時候 出現了問題不要慌 看看文檔可能會有對應的處理方法

修改用戶頭像

@login_required
def set_avatar(request):
if request.method == 'POST':
file_obj = request.FILES.get('avatar')
# models.UserInfo.objects.filter(pk=request.user.pk).update(avatar=file_obj) # 不會再自動加avatar前綴
# 1.自己手動加前綴
# 2.換一種更新方式
user_obj = request.user
user_obj.avatar = file_obj
user_obj.save()
return redirect('/home/')
blog = request.user.blog
username = request.user.username
return render(request,'set_avatar.html',locals())

bbs項目總結

""" 在開發任意的web項目的時候 其實到了後期需要寫的代碼會越來越少 都是用已經寫好的url填寫到a標簽href屬性完成跳轉即可 """
主要功能總結
表設計 開發流程(粗燥流程 還可以細化)
注冊功能
forms組件使用
頭像動態展示
錯誤信息提示
登陸功能
圖片驗證碼
滑動驗證碼
首頁展示
media配置
主動暴露任意資源接口
個人站點展示
側邊欄展示
側邊欄篩選
側邊欄inclusion_tag
文章詳情頁
點贊點踩
評論
後台管理
""" 針對bbs需要你掌握每一個功能的書寫思路 內部邏輯 之後再去敲代碼熟悉 找感覺 """

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved