django--BBS項目,後端業務邏輯整理

經典的生活價值觀

別讓人生,輸給了心情。心情不是人生的所有,卻能左右人生的所有。心情好,什麼都好,心情很差,一切都亂了。咱們經常不是輸給了別人,而是壞心情貶低了咱們的形象,下降了咱們的能力,擾亂了咱們的思惟,從而輸給了本身。

控制好心情,生活纔會到處祥和。好的心態塑造好心情,好心情塑造最出色的你。

靜靜的過本身的生活,心若不動,風又奈何。你若不傷,歲月無恙。

BBS 項目開發邏輯梳理

第一步:先進行數據庫設計

數據庫設計規則是:
	1.先建立基表:用戶表、站點表、文章表、標籤表、分類表、文章2標籤第三張關係表、點贊點踩表、評論表
    2.書寫表中的基本含有的字段
    3.添加外鍵(一對一,一對多,多對多)
    4.第三張關係表
注意事項:建立外鍵關係的時候,to='表名',不要忘記引號,null=true,並非全部的外鍵都加的

第二步settings配置

必定要進行settings的相關配置:
	1.數據庫配置
    DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'bbszikao',
        'USER':'root',
        'PASSWORD': 'root',
        'HOST':'127.0.0.1',
        'PORT':3306,
        'CHARSET':'utf8'
    }
}
	
    2.靜態文件資源配置
    STATICFILES_DIRS=[
        os.path.join(BASE_DIR,'static')
    ]
    
    3.models.py文件中,用戶表繼承AbstractUser類,須要對其進行settings配置
    from django.contrib.auth.models import AbstractUser
    配置auth模塊的訪問:
    AUTH_USER_MODEL='app01.Userinfo'
    
    4.靜態圖片資源settings配置+urls路由訪問配置,暴露給用戶查看,用戶註冊能夠訪問到默認頭像
    #settings文件配置:
    MEDIA_ROOT=os.path.join(BASE_DIR,'media')
    #urls文件訪問頭像路由配置
    url(r'^media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}),

第三步功能開發

註冊功能

後端開發邏輯:
分爲:register函數+register.html+myform.py文件
後端開發邏輯:
# myform.py 文件
	1.創建myform.py文件,利用forms表單,提交註冊的數據信息
    建立class MyRegForm(forms.Form):類
        用戶名,密碼,確認密碼,郵箱
from django import forms
from app01 import models

class MyRegForm(forms.Form):
    username = forms.CharField(max_length=8,min_length=3,label='用戶名',
                               error_messages={
                                   'max_length': '用戶名最長8位',
                                   'min_length': '用戶名最短3位',
                                   'required': "用戶名不能爲空"
                               },
                     #標籤設置
                     widget=forms.widgets.TextInput(attrs={'class': 'form-control'})
                               
    創建局部鉤子函數校驗用戶名是否存在
    # 局部鉤子 校驗用戶名是否已存在
    def clean_username(self):
        username = self.cleaned_data.get('username')
        res = models.Userinfo.objects.filter(username=username)
        if res:
            self.add_error('username','用戶名已存在')
        return username  
                               
                               
    創建全局鉤子函數校驗密碼是否一致
    # 全局鉤子 校驗兩次密碼是否一致
    def clean(self):
        password = self.cleaned_data.get('password')
        confirm_password = self.cleaned_data.get('confirm_password')
        if not password == confirm_password:
            self.add_error('confirm_password','兩次密碼不一致')
        return self.cleaned_data

                               
# models.py 文件    
    2.把myform文件中的MyRegForm表單對象拿到,
    而後把form_obj對象發送給前端register.html頁面
    form_obj=myform.MyRegForm()
    return render(request,'register.html',locals())

後端開發邏輯以下:
    把myform文件中的MyRegForm表單對象拿到
    判斷前端發送過來的請求方式是否是post請求
    定義back_dic 字典
    對用戶在前端提交的post數據進行校驗,生成form_obj對象
    若是數據合法:
          獲取全部鍵值對
          pop掉確認密碼鍵值對
          獲取前端發送的文件請求(avatar),建立文件對象
          判斷用戶是否上傳文件:
              上傳文件了,把avatar添加到clean_data字典對象中
          用戶表建立用戶(create_user)
          back_dic字典添加msg信息,註冊成功
          back_dic字典添加url,login路徑
    
    數據不合法:
          字典添加code=2000  
          字典添加msg=form_obj.errors
    
    返回json數據到前端,[須要導入JsonResponse模塊(from django.http import JsonResponse)]
    
    而後把form_obj對象發送給前端register.html頁面
前端開發邏輯:

上傳頭像文件功能,註冊按鈕功能html

前端開發邏輯:register.html文件,前端只整理須要整理的
上傳頭像文件功能
<script>
    //上傳頭像文件相關的處理
    $('#mdd').on('change',function () {
        //利用內置對象filereader完成文件的讀取操做
        let MyFileReader=new FileReader();
        //獲取用戶上傳的文件對象
        let fileobj=$(this)[0].files[0];
        //讓文件閱讀器讀取文件,IO操做,異步
        MyFileReader.readAsDataURL(fileobj);
        //將讀取以後的內容替換到img標籤src屬性中
        MyFileReader.onload=function () {
            $('#img').attr('src',MyFileReader.result)
        }
    });
    
    

        // 註冊按鈕
    $('#submit').click(function () {
        // 將用戶輸入的數據所有發送給後端     普通的鍵值對   文件
        let MyFormData = new FormData();
        // 不停的朝裏面添加鍵值對
        {#MyFormData.append('','')#}
        {#console.log($('#myform').serializeArray())#}
        // 普通鍵值對添加完畢 (利用form標籤內部有一個自動序列化普通鍵值對方法)
        $.each($('#myform').serializeArray(),function (index,obj) {
            MyFormData.append(obj.name,obj.value)
        });
        // 手動添加文件數據
        MyFormData.append('avatar',$('#mdd')[0].files[0]);
        // 發送ajax請求
        $.ajax({
            url:'',
            type:'post',
            data:MyFormData,
            // 發送文件必定要指定兩個參數
            processData:false,  // 不要讓瀏覽器處理你的數據
            contentType:false,  // 不要使用任何的編碼  django可以識別對象自身

            success:function (data) {
                if (data.code == 1000){
                    // 跳轉到登陸頁面
                    window.location.href = data.url
                }else{
                    $.each(data.msg,function (index,obj) {
                        {#console.log(index,obj)#} // index就是報錯字段  obj就是錯誤信息 數組的形式
                        // 獲取報錯字段  手動拼接出該字段所對應的input框的id值
                        let targetId = '#id_' + index;
                        $(targetId).next().text(obj[0]).parent().addClass('has-error')
                    })
                }
            }
        })

    });
    // input框獲取焦點事件,---這個是鼠標放到input框上面後,錯誤信息消失
    $('input').focus(function () {
        $(this).next().text('').parent().removeClass('has-error')
    })

</script>

圖片驗證碼功能

後端開發邏輯

1.所須要的模塊
import random
from PIL import Image,ImageDraw,ImageFont
from io import BytesIO,StringIO
'''
內存管理器模塊:
BytesIO 保存數據,而且在獲取的時候,是以二進制的方式給你
StringIO 保存數據,而且在獲取的時候,是以字符串的方式給你

Image       生成圖片
ImageDraw   在圖片上寫字
ImageFont   控制字的字體樣式

'''
#io_obj=BytesIO()    #你就將該對象當作是文件句柄便可
    '''
 什麼是文件句柄???
    在文件I/O中,要從一個文件讀取數據,應用程序首先
    要調用操做系統函數並傳送文件名,並選一個到該文件
    的路徑來打開文件。該函數取回一個順序號,即文件句柄
    (file handle),該文件句柄對於打開的文件是惟一的
    識別依據。要從文件中讀取一塊數據,應用程序須要調用
    函數ReadFile,並將文件句柄在內存中的地址和要拷貝的
    字節數傳送給操做系統。當完成任務後,再經過調用系統函數
    來關閉該文件。」
    '''
2.圖片驗證碼的開發分爲兩步:
	隨機取色+圖片驗證碼
    2.1隨機取色
    def get_random():
    	return random.randint(0,255),random.randint(0,255),random.randint(0,255)
    
    2.2圖片驗證碼函數
    	圖片的寬高和隨機取色---生成畫板對象   Image.new
        將生成好的圖片對象交給ImageDraw---畫筆對象   ImageDraw.Draw
        字體樣式	---何種字體   ImageFont.truetype
        #隨機驗證碼	---何種要求(大小寫英文加數字,5位)
        定義code=''
        循環5次:
        	大寫字母		upper_str
            小寫字母		lower_str
            str(隨機數)	 random_int
            
            隨機選取一個,random.choice([大寫,小寫,str(隨機數)])
            往圖片上寫一個驗證碼		img_draw.text
            存儲寫的字				code+=tmp
        將code存到session中,供全局函數訪問			request.session['code']=code
        生成I/O文件句柄							io_obj=BytesIO()	
        圖片對象調用save方法保存io_obj文件對象,png的格式進行保存								img_obj.save(io_obj,'png')
        return HttpResponse(io_obj.getvalue())
        
 --------------------------------------------------------------------------------        
3.圖片驗證碼代碼以下:
def get_code(request):
    # 圖片的寬高和隨機取色        ----畫板
    img_obj=Image.new('RGB',(360,35),get_random())
    #將生成好的圖片對象交給ImageDraw   ---畫筆
    img_draw=ImageDraw.Draw(img_obj)
    #字體樣式                       ---何種字體
    img_font=ImageFont.truetype('static/font/111.ttf',30)
    #隨機驗證碼      ---何種要求(大小寫英文加數字,5位)
    code=''
    for i in range(5):
        upper_str=chr(random.randint(65,90))
        lower_str=chr(random.randint(97,122))
        random_int=str(random.randint(0,9))
        #隨機選取一個
        tmp=random.choice([upper_str,lower_str,random_int])
        #往圖片上寫一個驗證碼
        img_draw.text((i*60+60,0),tmp,get_random(),img_font)
        #存儲寫的字
        code+=tmp
    print(code)
    #這個驗證碼後面其餘視圖函數可能要用到,
    #找個地方存一下,而且這個地方全局的視圖函數都能訪問
    request.session['code']=code
    io_obj=BytesIO()    #你就將該對象當作是文件句柄便可
    '''
    在文件I/O中,要從一個文件讀取數據,應用程序首先
    要調用操做系統函數並傳送文件名,並選一個到該文件
    的路徑來打開文件。該函數取回一個順序號,即文件句柄
    (file handle),該文件句柄對於打開的文件是惟一的
    識別依據。要從文件中讀取一塊數據,應用程序須要調用
    函數ReadFile,並將文件句柄在內存中的地址和要拷貝的
    字節數傳送給操做系統。當完成任務後,再經過調用系統函數
    來關閉該文件。」
    '''
    img_obj.save(io_obj,'png')
    return HttpResponse(io_obj.getvalue())

圖片驗證碼實時刷新功能

前端開發邏輯

圖片驗證碼隨機變化的邏輯其實很簡單,就是:
    首先爲驗證碼綁定點擊事件,
        其次拿到img中的src屬性,
        最後爲src設置新的值,使得圖片驗證碼不斷的更換


<div class="col-md-6">
	<img src="/get_code/" alt="" width="360" height="35" id="id_img">
</div>

<script>
    //拿到img中的src,
    //而後爲src設置新的值,使得圖片驗證碼不斷的更換
    $('#id_img').click(function () {
        var oldPath = $(this).attr('src');
        $(this).attr('src',oldPath+='?')
    });

</script>

登陸功能

後端開發邏輯

urls.py文件中:
	# 登陸功能
    url(r'^login/',views.login,name='login'),

views.py文件中:
開發邏輯以下:

若是請求方式是post請求:
	定義back_dic
    獲取post請求的用戶名
    獲取post請求的密碼
    獲取post請求的驗證碼
    #先校驗驗證碼是否正確,忽略大小寫
    #再校驗用戶名和密碼是否正確
    若是用戶輸入的驗證碼和後端保存的驗證碼相等:
    	再用auth模塊校驗用戶名和密碼是否相等,生成用戶對象
        若是相等:
        	利用auth模塊保存用戶對象的登陸狀態
            #就能夠在任意位置經過request.user獲取到當前登陸對象,而且request.user.is_authenticated()判斷當前用戶是否登陸
            back_dic字典添加msg登陸成功
            back_dic字典添加url,home主頁路徑
        
        若是用戶名和密碼不相等:
        	back_dic字典添加code=2000
            back_dic字典添加msg用戶名或密碼錯誤
    
    若是驗證碼不相等:
    	back_dic字典添加code=3000
        back_dic字典添加msg,驗證碼錯誤
    經過JsonResponse返回字典
 返回到登陸頁面



# 保存用戶登陸狀態,這個不太會,對該知識點模糊要多注意了,
# 經過auth模塊的login方法將用戶保存起來,就能夠在任意位置經過request.user,
# 獲取到當前登陸對象,而且能夠經過request.user.is_authenticated()判斷當前用戶是否登陸
auth.login(request, user_obj)
auth模塊很重要須要多複習複習

前端開發邏輯

爲登陸按鈕綁定點擊事件,
	利用ajax請求,把數據發送到後端
	ajax的固定格式爲:
		$.ajax({
			url:'',
			type:'post',
			data:{k,v鍵值對,'csrfmiddlewaretoken':'{{ csrf_token }}'},	
			success:function(data){
				//此處的data就是後端返回過來的back_dic字典對象
				若是code=1000:
					跳轉到對應頁面的url鏈接
				若是不等於1000:
					渲染錯誤數據信息
					信息錯誤後自動刷新驗證碼
		}

	})

提交post請求跳過csrf中間件攔截的兩種方式:
{% csrf_token %}	------- 在form表單中書寫
'csrfmiddlewaretoken':'{{ csrf_token }}'	------- 在ajax中的data字典中書寫


<div>
    <input type="button" class="btn btn-primary" value="登陸" id="id_submit">
    <span style="color: red;" id="id_error"></span>  //在此處渲染頁面的錯誤信息
</div>

<script>
$('#id_submit').click(function () {
        $.ajax({
            url:'',
            type:'post',
            data:{
                //$('#標籤名').val()是獲取當前標籤的值
                'username':$('#id_username').val(),
                'password':$('#id_password').val(),
                'code':$('#id_code').val(),
                'csrfmiddlewaretoken':'{{ csrf_token }}'
            },
            success:function (data) {
                if(data.code == 1000){
                    // 跳轉連接
                    window.location.href = data.url
                }else{
                    //渲染錯誤數據信息
                    $('#id_error').text(data.msg);
                    //數據填寫錯誤,提交後驗證碼再次刷新
                    var oldPath = $('#id_img').attr('src');
                    $('#id_img').attr('src',oldPath+='?')

                }
            }
        })
    
</script>

首頁搭建功能

後端開發邏輯

#home主頁
第一步:查詢全部文章,生成文章queryset對象,
第二步:把數據所有提交到home頁面

def home(request):
    #查詢全部文章,生成文章queryset對象,
    #把數據所有提交到home頁面
    article_queryset=models.Article.objects.all()
    return render(request,'home.html',locals())

前端開發邏輯

主頁搭建共分爲四塊,
	第一塊,導航條
    第二塊,左側面板	2
    第三塊,中間面板	8
    第四塊,右側面板	2

退出登陸功能

後端開發邏輯

導入auth模塊,導入登陸認證裝飾器:
from django.contrib import auth
from django.shortcuts import reverse  ----這個是用來反向解析的模塊
from django.contrib.auth.decorators import login_required
給退出登陸函數添加@login_required裝飾器,裝飾器不要加括號
	利用auth模塊的退出登陸函數			---auth.logout(request)
    返回經過重定向反轉解析到home主頁	  ---redirect(reverse('home'))

    
代碼以下:
@login_required
def logout(request):
    auth.logout(request)
    return redirect(reverse('home'))		---退出登陸以後跳轉到home主頁

前端開發邏輯

用戶登陸狀況下:
	展現用戶名:
    	拿到用戶的用戶名,------超連接;經過auth模塊的is_authenticated判斷用戶是否已經登陸
        
    修改密碼
    修改頭像
    後臺管理
    退出登陸
未登陸狀況下:
	展現登陸	--超連接,反向解析login,{% url 'login' %}
    展現註冊	--超連接,反向解析register,{% url 'register' %}
 

部分邏輯代碼以下:
{% if request.user.is_authenticated %}
<li><a href='#'>  {{  request.user.username	}}	</a></li>
    <li><a data-target="#myModal" data-toggle="modal">修改密碼</a></li>
    <li><a href="#">修改頭像</a></li>
    <li><a href="#">後臺管理</a></li>
    <li role="separator" class="divider"></li>
    <li><a href="{% url 'logout' %}">退出登陸</a></li>
{% else %}
    <li><a href="{% url 'login' %}">登陸</a></li>
    <li><a href="{% url 'register' %}">註冊</a></li>
{% endif %}

修改密碼功能

後端開發邏輯

urls.py文件中開設修改密碼的路由

url(r'^set_password/',views.set_password,name='set_pwd')

views.py文件
from django.shortcuts import reverse	----這個是用來反向解析的模塊
from django.contrib import auth
from django.contrib.auth.decorators import login_required
首先給修改密碼函數添加裝飾器@login_required
若是前端請求方式是post請求:
	獲取前端發送過來的原密碼old_password
    獲取前端發送過來的新密碼new_password
    獲取前端發送過來的確認密碼confirm_password
    #利用auth模塊先判斷前端發送過來的原密碼是否正確,
    #check_password是auth模塊自帶的校驗密碼是否相等的功能
    request.user.check_password(old_password)
    若是原密碼正確:
    	若是新密碼和確認密碼相等:
            利用auth模塊中的set_password設置新的密碼
            而後利用auth模塊中的save方法進行保存
            返回重定向解析到login登陸頁面
        
        若是新密碼和確認密碼不相等:
        	返回文本,兩次密碼不一致
    
    若是原密碼不正確:
    	返回文本,原密碼錯誤

        
注意事項:
	由於沒有導入reverse模塊,而且把reverse寫成了reversed致使的bug
    在退出登陸和修改密碼兩處須要用到的反向解析的地方代碼都是寫成reversed致使的項目bug,以下:
	
    報錯信息以下:
    	NoReverseMatch at /set_password
Reverse for '<reversed object at 0x0000000004EA0E48>' not found. '<reversed object at 0x0000000004EA0E48>' is not a valid view function or pattern name.
		翻譯以下:
    		NoReverseMatch在/ set_password

沒有找到'< Reverse object at 0x0000000004EA0E48>'。''不是有效的視圖函數或模式名。

前端開發邏輯

利用form表單,發送post請求
    反向解析到set_pwd路徑下,{% url 'set_pwd' %}
    利用{% csrf_token	%}	跳過csrf中間件
    下面就是form表單中的5個div,分別是:
    	用戶名,使用disable屬性,默認展現,用戶名不支持修改
        原密碼
        新密碼
        確認密碼
        取消,修改

admin數據錄入功能

後端開發邏輯

1.建立超級用戶,進入後臺管理---Tools---Run manage Task -----createsuperuser

2.去應用下的admin.py文件中註冊你想要管理的模型類
		導入models文件
    	註冊管理的模型類:
        	admin.site.register(models.Userinfo)
            admin.site.register(models.Blog)
            admin.site.register(models.Tag)
            admin.site.register(models.Category)
            admin.site.register(models.Article)
            admin.site.register(models.Article2Tag)
            admin.site.register(models.UpAndDown)
            admin.site.register(models.Comment)
            
3.爲表添加中文別名:
      在models.py文件中,對應的8張表中分別添加
            class Meta:
                verbose_name_plural='用戶表'
                #verbose_name='用戶表'	會自動在末尾加s後綴

            class Meta:
                verbose_name_plural='文章表'
                #verbose_name='用戶表'	會自動在末尾加s後綴
            後面依次以下!!!!

4.在模型表中爲模型表添加雙下str方法打印對象對應的名字,
雙下str只能返回字符串。
		def __str__(self):
        	return self.username
        
        self點對應表中的字段名
        

5.admin數據錄入:
	順序:
		文章表
        	分類和我的站點
            
        用戶表	----綁定對應的站點,在用戶表中填寫blank=True,
        		blank告訴admin後臺管理該字段能夠爲空,不會影響到數據庫,不用執行遷移命令
        
        標籤表
        文章2標籤表

首頁文章展現功能

後端開發邏輯

home函數
查詢當前網站全部的文章,展現到前端頁面上
def home(request):
    #查詢當前網站全部的文章,展現到前端頁面上
    article_queryset = models.Article.objects.all()
    return render(request,'home.html',locals())

前端開發邏輯

前端主要編寫邏輯和主要邏輯代碼以下:
經過for循環article_queryset把文章一篇篇的都讀取出來,所有展現到前端頁面
{% for article in article_queryset	%}

	文章的標題,利用跨表查詢	
    <a href="#">
    	{{ article.title }}
    </a>
    
    文章頭像,利用跨表查詢
    <img class='media-object' src='/media/{{article.blog.userinfo.avatar}}/' height='60'>
    
    文章簡介
    <div class='media-body'> {{	article.desc }}	</div>
    
    文章狀況包括:
    	用戶名(軟件老王)
        <span><a href='#'>{{article.blog.userinfo.username}}</a></span>
        
        發佈於
        <span>發佈於&nbsp;&nbsp;</span>
        
        日期(年月日),須要用到日期過濾器
        <span>{{ article.create_time|date:'Y-m-d' }}</span>
        
        評論數(0)
        <span>{{ article.comment_num }}</span>
        
        點贊數(0)
        <span>{{ article.up_num	}}</span>

用戶頭像展現功能

後端開發邏輯

settings.py文件

media配置,可以將用戶上傳的全部的文件都統一保存在指定的文件夾下
MEDIA_ROOT=os.path.join(BASE_DIR,'media')

urls.py文件
手動開設後端資源,將media文件夾下面全部的資源暴露給外界訪問------固定寫法規則,背下來
導入serve模塊,導入settings配置文件
from django.views.static import serve
from BBS import settings
#謹慎使用
url(r'^media/(?P<path>.*)',serve,{'document_root':settings.MEDIA_ROOT})

前端開發邏輯

文章頭像,利用跨表查詢
    <img class='media-object' src='/media/{{article.blog.userinfo.avatar}}/' height='60'>

圖片防盜鏈,404頁面功能

後端開發邏輯

urls.py文件

配置我的站點的路由
#我的站點
    url(r'^(?P<username>\w+)/$',views.site,name='username')

views.py文件
定義站點函數,接收有名分組username
	經過username查詢用戶對象
    若是用戶對象不存在:
    	返回404頁面

代碼以下:
def site(request,username):
    user_obj=models.Userinfo.objects.filter(username=username).first()
	if not user_obj:	#404頁面
        return render(request,'error.html')

圖片防盜鏈
	經過判斷當前請求以前的所在地址
    若是是本網站的連接正常訪問,若是不是本網站的連接就直接禁止
    若是查看呢?
    	經過請求頭裏面來查看,
        refer	---表示你從哪裏來的
        user-agent	---標識你是不是一個瀏覽器

前端開發邏輯

直接在博客園404頁面右鍵檢查打開,複製代碼,拷貝到BBS項目中的error.html頁面

返回網站首頁,經過反向解析設置到主頁
{% url 'home' %}

------------------------------------------------下面的尚未進行錄音工做---------------------------------------------------------前端

我的站點頁面搭建

後端開發邏輯

urls.py文件

配置我的站點的路由
#我的站點
    url(r'^(?P<username>\w+)/$',views.site,name='username')

views.py文件
定義站點函數,接收有名分組username
	經過username查詢用戶對象   
    若是用戶對象不存在:
    	返回404頁面
        
	經過用戶對象查詢到該用戶的站點
    查詢該用戶站點下的全部文章
    返回數據到site頁面

代碼以下:
def site(request,username):
    #經過username查詢用戶對象   
    user_obj=models.Userinfo.objects.filter(username=username).first()
    
    #若是用戶對象不存在:
    #	返回404頁面
    if not user_obj:
        return render(request,'error.html')
    #經過用戶對象查詢到該用戶的站點
    blog=user_obj.blog
    # 查詢當前用戶站點下的全部文章
    article_list=models.Article.objects.filter(blog=blog)
    
    #返回數據到site頁面
    return render(request,'site.html',locals())

--------------------------------------------------------------------------------------------------------------------------------------------------------------------
只需一招讓你分清QuerySet對象,和用戶字典對象

article_list = models.Article.objects.filter(blog=blog)
user_obj = models.Userinfo.objects.filter(username=username).first()
上面的兩個查詢不太懂
#article_list是可迭代的QuerySet對象,支持for循環
#user_obj這個是用戶字典對象,不支持for循環
全部支持for循環的數據類型必須是可迭代數據類型!!!
# 查詢當前用戶站點下的全部文章
    # article_list = models.Article.objects.filter(blog=blog).first()
    #全部支持for循環的數據類型必須是可迭代數據類型,
    #字典不可迭代,因此for循環取值會報錯:TypeError: 'Article' object is not iterable,
    #因此article_list必須是能夠迭代的對象,QuerySet對象能夠迭代,是可迭代對象,
    #那麼就要把.first()給去掉

------------------這裏是經過E盤本身練習的day53課件本身摸索總結出來的---------------------------
# 2.filter() 獲得可迭代的QuerySet對象,支持for循環取容器內的元素,在django中推薦使用
    # 篩選,至關於你原生sql語句裏面的where關鍵字,返回的結果queryset對象
    res=models.Books.objects.filter(pk=1,title='張三丰') #支持多個參數,and關係
    print(res)      #<QuerySet [<Books: 張三丰>]>
    print('************************')

# 3.get()    在django中不推薦使用
    # 篩選,獲取的是數據對象自己,條件不存在的數據直接報錯,而且查詢條件必須是惟一的
    res=models.Books.objects.get(title='西遊記')
    print(type(res))      #若是是數據庫中不存在的數據進行查詢的話,會報錯,由於數據庫中沒有該數據

    <QuerySet [<Books: 張三丰>]>
    ************************
    <class 'app01.models.Books'>
    //////////////////////////
    (0.001) SELECT `app01_books`.`id`, `app01_books`.`title`, `app01_books`.`price`, `app01_books`.`kucun`, `app01_books`.`maichu` FROM `app01_books` WHERE `app01_books`.`price` = 766 LIMIT 21; args=(Decimal('766'),)
    <QuerySet [<Books: 西遊記>, <Books: 西遊記2>]>
    <class 'django.db.models.query.QuerySet'>
    ************************
    西遊記
    1000
    <class 'app01.models.Books'>
    (0.001) SELECT `app01_books`.`id`, `app01_books`.`title`, `app01_books`.`price`, `app01_books`.`kucun`, `app01_books`.`maichu` FROM `app01_books` WHERE `app01_books`.`price` = 766 ORDER BY `app01_books`.`id` ASC LIMIT 1; args=(Decimal('766'),)

////////////////////////////////////////////////////////////////////////////// 
////////////////////////////////////////////////////////////////////////////// 
////////////////////////////////////////////////////////////////////////////// 
   
# 4.first()
    # 功能一:取queryset 相同數據中的第一個數據對象,
    重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點重點
    					
        						特殊功能以下:
        
    還有一個特殊的功能能夠把QuerySet對象轉換爲字典對象,方便字典經過對象點屬性取值----88顆星的重點
    # id=3,title='西遊記'  id=5,title='西遊記',first取值會優先取id=3的數據
    res = models.Books.objects.filter(price='766')
    print(res)
    print(type(res))

    print('************************')

    res = models.Books.objects.filter(price='766').first()
    print(res)
    print(res.kucun)
    print(type(res))

'''上面的打印結果以下:
    < QuerySet[ < Books: 西遊記 >, < Books: 西遊記2 >] >
    <class 'django.db.models.query.QuerySet'>

    ************************

    西遊記
    1000
    <class 'app01.models.Books'>
'''

前端開發邏輯

知道怎麼回事便可!
site.html我的站點頁面
頁面佈局3,9
<div class="container-fluid">
    <div class="row">
        <div class="col-md-3">
            {% load mytag %}
            {% my_menu username %}
        </div>
        <div class="col-md-9">
            {% block content %}

            {% endblock %}
        </div>
    </div>
</div>

模板繼承
{% extends 'base.html' %}

循環展現我的站點下的全部文章
 {% for article in article_list %}
#posted @ 2019-09-28 17:42 武沛齊 閱讀 (2714) 評論 (4) 編輯#
<br>
<div class="pull-right">
    <span>posted&nbsp;&nbsp;</span>
    <span>@&nbsp;&nbsp;</span>
    <span>{{ article.create_time|date:'Y-m-d' }}&nbsp;&nbsp;</span>
    <span>{{ article.blog.userinfo.username }}&nbsp;&nbsp;</span>

    <span>評論數({{ article.comment_num }})&nbsp;&nbsp;</span>
    <span>點贊數({{ article.up_num }})</span>
    <span><a href="#">編輯</a></span>
</div>

我的站點側邊欄展現功能

後端開發邏輯

共分爲三塊文章分類,文章標籤和日期歸檔
經過orm查詢和django官方提供的日期處理方法來實現
'''
    側邊欄篩選的功能究竟是篩選什麼?
    篩選的是當前這個用戶下面的全部的文章,再進行標籤、分類、日期進行篩選
    本質就是對已經查詢出來的article_list再進行篩選操做。
    
    '''
    1.查詢當前用戶每個分類和分類下的文章數
    category_list=models.Category.objects.filter(blog=blog).annotate(num=Count('article')).values_list('name','num')
    print(category_list)        #<QuerySet [('luzhaoshan的分類1', 1)]>

    2.查詢當前用戶每個標籤和標籤下的文章數
    tag_list=models.Tag.objects.filter(blog=blog).annotate(num=Count('article')).values_list('name','num')
    print(tag_list)             #<QuerySet [('luzhaoshan的標籤1', 1)]>

    3.按照年月分組
    date_list=models.Article.objects.filter(blog=blog).annotate(month=TruncMonth('create_time')).values('month').annotate(num=Count('pk')).values_list('month','num')
    print(date_list)            #<QuerySet [(datetime.date(2019, 12, 1), 1)]>
    備註:(2019,12,1)中的1是django中默認含有的

    經過return返回到前端site.html頁面,由於獲取到的值都是列表套元組,
    在前端能夠經過列表取值的方式,取索引0是名字,取索引1是對應的值。

前端開發邏輯

後端經過return返回到前端site.html頁面,由於獲取到的值都是列表套元組,
    在前端能夠經過列表取值的方式,取索引0是名字,取索引1是對應的值。
分爲三塊,文章分類,文章標籤,日期歸檔
#<QuerySet [('luzhaoshan的分類1', 1)]>
    
    <h3 class="panel-title">文章分類</h3>
    {% for category in category_list %}
    	 <p><a href="#">{{ category.0 }}</a>({{ category.1 }})</p>
    {% endfor %}
-----------------------------------------------------------------
 #<QuerySet [('luzhaoshan的標籤1', 1)]>
    
 	<h3 class="panel-title">文章標籤</h3>
    {% for tag in tag_list %}
    	 <p><a href="#">{{ tag.0 }}</a>({{ tag.1 }})</p>
    {% endfor %}
--------------------------------------------------------------------
#<QuerySet [(datetime.date(2019, 12, 1), 1)]>
備註:(2019,12,1)中的1是django中默認含有的

 	<h3 class="panel-title">日期歸檔</h3>
	{% for date in date_list %}
    	 <p><a href="#">{{ date.0|date:'Y年 - m月' }}</a>({{ date.1 }})</p>
    {% endfor %}

我的站點側邊欄篩選功能

後端開發邏輯

urls.py文件

我的站點側邊欄篩選功能
url(r'^(?P<username>\w+)/category/(?P<param>\d+)/',views.site),
url(r'^(?P<username>\w+)/category/(?P<param>\d+)/',views.site),
url(r'^(P<username>\w+)/category/(?P<param>\d+)',views.site),


優化以下:
url(r'^(?P<username>\w+)/(?P<condition>category|tag|archive)/(?P<param>)',views.site)

views.py文件
經過關鍵字參數(**kwargs)來接收有名分組
因此,site函數中使用:
def site(request,username,**kwargs):
    user_obj = models.Userinfo.objects.filter(username=username).first()

    if not user_obj:  # 404頁面
        return render(request, 'error.html')
    blog = user_obj.blog
    # 有當前用戶全部的文章
    article_list = models.Article.objects.filter(blog=blog)
    if kwargs:			#若是關鍵字參數有值,則能夠按照條件跳轉對應的分類或者標籤或者日期歸檔
        '''
        側邊欄篩選的功能究竟是篩選什麼?
        篩選的是當前這個用戶下面的全部的文章,再進行標籤,分類,日期篩選,
        本質其實就是對該站點進行一個再細分的細化。
        '''
		condition=kwargs.get('condition')
        param=kwargs.get('param')
        if condition=='category':
            #按照分類篩選
            article_list=article_list.filter(category_id=param)
            #按照標籤篩選	,
      注意事項: 
    		注意雙下劃線是在本表中的字段經過雙下跨表到另一張表查詢 
   			tags是文章表中的外鍵字段,經過雙下劃線跨表到tag分類表中
            
            ——————————————————————————————————————————————————————————————————————————————————
            雙下劃線在本表中【普通的字段】進行查詢,叫母表查詢,
            雙下劃線經過本表中的【外鍵字段】查詢是跨表查詢,是跨到另一張表中
 ——————————————————————————————————————————————————————————————————————————————————            
            tag_list=article_list.filter(tag__id=param)	#雙下劃線條件查詢,查詢id=param的數據
            #按照年月日期篩選
            year,month=param.split('-')
            #__year   查詢年份 	__month   查詢月份			#雙下劃線查詢	
            				            article_list=article_list.filter(create_time__year=year,create_time__month=month)

前端開發邏輯

<div class="panel-heading">
    	<h3 class="panel-title">文章分類</h3>
    </div>
    <div class="panel-body">
        {% for category in category_list %}
        <p><a href="/{{ username }}/category/{{ category.2 }}">{{ category.0 }}</a>({{ category.1 }})</p>
        {% endfor %}
    </div>

    
 ——————————————————————————————————————————————————————————————————————————————————     
    
    
    <div class="panel-heading">
    	<h3 class="panel-title">文章標籤</h3>
    </div>
    <div class="panel-body">
        {% for tag in tag_list %}
        <p><a href="/{{ username }}/tag/{{ tag.2 }}">{{ tag.0 }}</a>({{ tag.1 }})</p>
        {% endfor %}
    </div>

    
 ——————————————————————————————————————————————————————————————————————————————————     
    
    <div class="panel-heading">
    	<h3 class="panel-title">日期歸檔</h3>
    </div>
    <div class="panel-body">
        {% for date in date_list %}
        <p><a href="/{{ username }}/archive/{{ date.0|date:'Y-m' }}">{{ date.0|date:'Y年 - m月' }}</a>({{ date.1 }})</p>
        {% endfor %}
    </div>
    
 ——————————————————————————————————————————————————————————————————————————————————

側邊欄製做

後端開發邏輯

### 側邊欄製做
    開發邏輯:
    1.根據文章id,查詢出對應的文章,展現到前端便可
    2.前端用到了模板的繼承,把site.html抽出來作成模板
    3.建立base.html頁面繼承site.html頁面,而後對9的部分進行調整,
        3.1劃定區域經過
            {% block content %}
            aa-這裏是頁面內容
            {% endblock %}

        3.2而後原來的site.html頁面,就繼承base.html頁面
            {% extends 'base.html' %}
            {% block content %}
            而後把aa的內容刪除掉,放在這個地方
            {% endblock %}

        3.3來到article_detail.html頁面
            繼承base.html頁面
            {% extends 'base.html' %}

            {% block content %}
            這裏面是把文章拿過來:
                文章標題,
                文章內容,
            {% endblock %}

    4.自定義標籤過濾器
        4.1建立templatetags文件,和文件下的mytag.py文件
            寫上:
                from django import template
                register=template.Library()
                @register.inclusion_tag('left_menu.html')
                而後建立left_menu.html頁面,裏面內容全刪除掉,空頁面

        4.2繼續建立left_menu函數
            def left_menu(username):
            而後把site我的站點下的下面的代碼剪切到這裏,目的就是爲了方便屢次調用
                user_obj = models.Userinfo.objects.filter(username=username).first()
                    把用戶對象拿過來
                 # 1.查詢當前用戶每個分類和分類下的文章數
                category_list = models.Category.objects.filter(blog=blog).annotate(num=Count('article')).values_list('name', 'num',
                                                                                                                     'pk')
                print(category_list)  # <QuerySet [('luzhaoshan的分類1', 1)]>

                # 2.查詢當前用戶每個標籤和標籤下的文章數
                tag_list = models.Tag.objects.filter(blog=blog).annotate(num=Count('article')).values_list('name', 'num', 'pk')
                print(tag_list)  # <QuerySet [('luzhaoshan的標籤1', 1)]>

                # 3.按照年月分組
                date_list = models.Article.objects.filter(blog=blog).annotate(month=TruncMonth('create_time')).values(
                    'month').annotate(num=Count('pk')).values_list('month', 'num')
                print(date_list)  # <QuerySet [(datetime.date(2019, 12, 1), 1)]>   備註:(2019,12,1)中的1是django中默認含有的

        4.3經過return locals(),把當前全部的內容所有返回到left_menu.html頁面
            而後去base.html頁面把3部分中的內容剪切拿過來,放到left_menu.html頁面中
            而後base.html頁面3的部分中寫上:
                {% load mytag %}
                {% left_menu username %}

        ### 點贊點踩樣式拷貝
        開發邏輯
        拷貝圖標到本地,防止圖片防盜鏈,致使圖片加載失敗
        拷貝點贊點踩的樣式

        ### 點贊點踩的業務邏輯
        1.校驗用戶是否登陸,沒有登陸的狀況下,不可點贊和點踩,提示請先登陸
        2.登陸的用戶,不能給本身的文章點贊和點踩
        3.已經給該文章點過贊和點過踩的用戶,不能再點
        4.不能給本身點贊和點踩
        文章詳情頁:
        點贊點踩經過ajax請求發送給後端,都給點贊和點踩按鈕綁定相同的action,
        而後經過綁定點擊事件實現點贊和點踩的數量增長。
        $('.action').click(function(){
            如何區分你點的是贊仍是點的是踩?
            $(this) 是當前被點擊的這個對象
            $(this).hasClass('diggit') 判斷當前這個點擊的對象有沒有'diggit'這個屬性
            爲了提升代碼的閱讀性,專門爲點贊點踩開設一個url
            經過ajax發送請求到後端
            $.ajax({
                url:'/up_down',
                type:'post',
                data:{
                    'csrfmiddlewaretoken':'{{csrf_token}}',
                    'is_up':isUp,
                    'article_id':"{{article_obj.pk}}"
                },
                success:function(data){
                    如今就能夠在updown視圖函數中進行相關的邏輯處理了
                }
            })

        })

        進入到updown函數中:

點贊點踩業務邏輯

updown函數中:

     直接判斷過來的請求是否是ajax請求,若是是ajax請求的話:
                再判斷請求的方法是否是POST請求,若是是POST請求的話:
                    back_dic={'code':1000,'msg':''}
                    is_up=request.POST.get('is_up')     是一個字符串格式的json數據
                    article_id=request.POST.get('article_id')
                    import json
                    is_up=json.loads(is_up)     是將json格式的字符串格式數據,轉成python對應的格式boolean布爾類型

                    下面是點贊點踩業務邏輯:
                    1.校驗用戶是否登陸,沒有登陸的狀況下,不可點贊和點踩,提示請先登陸
                    if request.user.is_authenticated():
                        2.當前這篇文章是否是當前登陸用戶本身寫的
                        經過拿到當前站點所對應的的用戶對象  是否等於 當前登陸的用戶對象
                        article_obj=models.Article.objects(pk=article_id).first()
                        若是兩個對象不相等的話,表明該文章不是該登陸用戶寫的:
                        if not article_obj.blog.userinfo  ==  request.user:
                            3.那麼,咱們繼續判斷當前這篇文章用戶是否已經點過
                            到點贊點踩表中去查詢數據便可
                            篩選數據,篩選用戶是當前用戶對象,而且文章對象是當前文章對象的數據,
                            若是有值表示已經點過了,若是沒有值表示尚未點過。
                            經過.exists()返回布爾類型,TRUE仍是false
                            is_click=models.UpAndDown.objects.filter(user=request.user,article=article_obj).exists()
                            if not is_click:    若是沒有點過,下面就要開始操做數據庫,完成數據修改了
                                操做數據庫完成數據修改
                                if is_up:
                                    若是爲true,爲點贊數量加1,up_num=原來的數量基礎上加1,經過拿字段咱們使用F查詢
                                    導入from django.db import F
                                    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)

                    下面是點贊點踩的業務邏輯完善
                        後端和前端交互是ajax,那麼後端須要定義一個字典
                        back_dic={'code':1000,'msg':''}放在上面
                            下面是業務完善:
                            若是點過了,就不能再點了
                            else:
                                back_dic['code'] = 2000
                                back_dic['msg'] = '你已經點過了,不能再點了'
                        else:
                            back_dic['code'] = 3000
                            back_dic['msg'] = '你不能本身給本身點贊'
                    else:
                        back_dic['code'] = 4000
                        back_dic['msg'] = '請先<a href="/login/">登陸</a>'
                        ### 因爲請先登陸是一個超連接,那麼咱們在後端能夠添加a標籤,而後後端取消轉義便可
                        ### 後端取消轉義導入模塊 from django.utils.safestring import mark_safe
                        ### 而後經過mark_safe包裹一下,那麼最後優化以下所示:
                        back_dic['msg'] = mark_safe('請先<a href="/login/">登陸</a>')
                    再經過return JsonResponse把字典數據返回給前端
                    return JsonResponse(back_dic)
                    5.操做數據庫,完成數據修改
                        1.點贊點踩添加數據的時候,文章表裏面對應的三個普通字段也得修改
         而後下面就到了article_detail.html頁面,用來處理後端發送過來的請求:
                        success: function (data) {
                            if (data.code == 1000) {
                                // 給span標籤渲染信息
                                $('.info').text(data.msg);
                                // 點贊或點踩成功以後 應該將前端的數字也加一
                                let $span = $target.children();
                                let oldNum = $span.text();
                                $span.text(Number(oldNum) + 1)  // 轉整型相加 否則就是字符串拼接了
                            } else {
                                $('.info').text(data.msg)
                            }

評論功能之根評論

#### 評論功能之根評論
    若是請求是ajax請求:
        請求方式是POST:
            定義back_dic字典,back_dic={'code':1000,'msg':'' }
            獲取文章article_id,
            獲取評論的內容,
            獲取parent_id,
            開啓事務,from django.db import transaction
            with transaction.atomic():
                models.Comment.objects.create(user=request.user,article_id=article_id,content=content,parent_id=parent_id)
                繼續操做Article表中的comment_num字段,數量加 1
                models.Article.objects.filter(pk=article_id).update(comment_num=F('comment_num') + 1,parent_id=parent_id)
                back_dic['msg'] = '評論成功'
            return JsonResponse(back_dic)
            後面就是前端上的處理了,這裏不作總結,詳情能夠去看前端代碼

    ### 評論功能之子評論
    前端點擊回覆按鈕到底發生了幾件事?   詳情看前端代碼
        1.自動獲取當前點擊評論的評論人
        2.拼接 @ + 人名 + \n
        3.將拼接好的內容添加到評論框中,而且評論框自動聚焦
相關文章
相關標籤/搜索