主頁 > 軟體設計 > django搭建個人博客(一)

django搭建個人博客(一)

2021-04-23 11:25:48 軟體設計

準備階段

首先熟悉專案的根本流程,下面是對專案的每個模塊流程階段具體分析

模塊功能
注冊圖形驗證、短信驗證
登陸狀態保存、cookie、session
個人中心圖片上傳、更新資料
發布博客資料入庫
博客首頁資料分頁
博客詳情博客詳情資料展示、評論工程

總體流程介紹完畢開始做準備作業需要資料庫來存盤說用到的資料,根據資料字典創建物件的表格

檔案所使用的靜態資源和一些配置以提供,方便大家使用在這里插入圖片描述

創建py專案

使用pycharm軟體創建一個django專案
在這里插入圖片描述
在這里插入圖片描述

專案創建完之后 可以運行 啟動專案

可能會出現的坑
在這里插入圖片描述
該問題就是把‘/’識別為除號了,兩個str無法進行除號,代碼實際意思是將兩個str進行拼接,進入settings.py進行如下修改:

后面有具體組態檔

  1. ‘DIRS‘: [BASE_DIR / ‘templates‘] TypeError: unsupported operand type(s) for /: ‘str‘ and ‘str‘
    更改地方
    在這里插入圖片描述
    2.可能會有此問題query = query.decode(errors=‘replace‘)

在這里插入圖片描述
專案運行成功顯示界面
在這里插入圖片描述
準備階段

配置資料庫MySQL

打開本地資料庫–> 用戶自行選擇,可以創建一個新的用戶也可以使用root

新建資料庫

create database blog charset=utf8;

創建新用戶或者使用root用戶

create user diangen identified by '123456';

授權新用戶的權限

grant all on blog.* to 'diangen';

授權結束后重繪特權

 flush privileges;

資料庫創建完畢

創建一個django專案找到組態檔里面的settings.py 連接資料庫
在這里插入圖片描述

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql', # 資料庫引擎
        'HOST': '127.0.0.1', # 資料庫主機
        'PORT': 3306, # 資料庫埠
        'USER': 'itheima', # 資料庫用戶名
        'PASSWORD': '123456', # 資料庫用戶密碼
        'NAME': 'blog' # 資料庫名字
    },
}

可能出現的錯誤

Error loading MySQLdb module: No module named ‘MySQLdb’.
出現錯誤的原因:

Django中操作MySQL資料庫需要驅動程式MySQLdb
目前專案虛擬環境中沒有驅動程式MySQLdb

配置并使用資料庫
在這里插入圖片描述

配置Redis資料庫

檢查并安裝django-redis拓展包

  pip install django-redis

settings.py檔案夾中添加

CACHES = {
    "default": { # 默認
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/0",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    },
    "session": { # session
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/1",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    },
}

SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "session"

default:
默認的Redis配置項,采用0號Redis庫,
session:
狀態保持的Redis配置項,采用1號Redis庫,
SESSION_ENGINE
修改session存盤機制使用Redis保存,
SESSION_CACHE_ALIAS:
使用名為"session"的Redis配置項存盤session資料, 配置完成后:運行程式,測驗結果
電腦安裝redis并進入redis檔案目錄
在命令列中運行
在這里插入圖片描述

redis-server

在這里插入圖片描述

redis運行成功

如果運行報錯 看看是不是redis的問題
例如
問題原因是Redis 快照關閉了導致不能存盤,可以通過關閉stop-writes-on-bgsave-error配置來解決,

(1)Windows系統中找到了redis.windows.conf檔案,可以看到如下

# By default Redis will stop accepting writes if RDB snapshots are enabled
# (at least one save point) and the latest background save failed.
# This will make the user aware (in a hard way) that data is not persisting
# on disk properly, otherwise chances are that no one will notice and some
# disaster will happen.
#
# If the background saving process will start working again Redis will
# automatically allow writes again.
#
# However if you have setup your proper monitoring of the Redis server
# and persistence, you may want to disable this feature so that Redis will
# continue to work as usual even if there are problems with disk,
# permissions, and so forth.

stop-writes-on-bgsave-error yes

默認該設定是打開的,可以直接在此處修改為no

stop-writes-on-bgsave-error no

配置日志

settings.py檔案夾中添加(后期報錯方便在日志查看)

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,  # 是否禁用已經存在的日志器
    'formatters': {  # 日志資訊顯示的格式
        'verbose': {
            'format': '%(levelname)s %(asctime)s %(module)s %(lineno)d %(message)s'
        },
        'simple': {
            'format': '%(levelname)s %(module)s %(lineno)d %(message)s'
        },
    },
    'filters': {  # 對日志進行過濾
        'require_debug_true': {  # django在debug模式下才輸出日志
            '()': 'django.utils.log.RequireDebugTrue',
        },
    },
    'handlers': {  # 日志處理方法
        'console': {  # 向終端中輸出日志
            'level': 'INFO',
            'filters': ['require_debug_true'],
            'class': 'logging.StreamHandler',
            'formatter': 'simple'
        },
        'file': {  # 向檔案中輸出日志
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': os.path.join(BASE_DIR, 'logs/blog.log'),  # 日志檔案的位置
            'maxBytes': 300 * 1024 * 1024,
            'backupCount': 10,
            'formatter': 'verbose'
        },
    },
    'loggers': {  # 日志器
        'django': {  # 定義了一個名為django的日志器
            'handlers': ['console', 'file'],  # 可以同時向終端與檔案中輸出日志
            'propagate': True,  # 是否繼續傳遞日志資訊
            'level': 'INFO',  # 日志器接收的最低日志級別
        },
    }
}

創建檔案夾
在這里插入圖片描述
不同的應用程式所定義的日志等級可能會有所差別,分的詳細點的會包含以下幾個等級:

FATAL/CRITICAL = 重大的,危險的
ERROR = 錯誤
WARNING = 警告
INFO = 資訊
DEBUG = 除錯
NOTSET = 沒有設定

urls.py檔案中

創建日志記錄器

import logging
logger = logging.getLogger('django')

輸出日志

logger.debug('測驗logging模塊debug')
logger.info('測驗logging模塊info')
logger.error('測驗logging模塊error')

配置靜態資源

專案檔案夾下創建目錄static檔案
settings.py 檔案中添加
指定靜態檔案加載路徑

STATIC_URL = '/static/'

配置靜態檔案加載路徑

STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]

在這里插入圖片描述

配置完成后:運行程式,測驗結果,
在這里插入圖片描述
在這里插入圖片描述
專案啟動成功
下面開始我們的具體的模塊與應用

創建用戶模塊應用—注冊

創建應用users

python manage.py startapp users

在這里插入圖片描述
創建完之后目錄結構會出現一個檔案夾
在這里插入圖片描述

在工程的setting.py中 注冊用戶模塊應用
在這里插入圖片描述

定義用戶注冊視圖

創建一個子目錄 把注冊的html放進去 并更改配置
將static檔案夾下在register.html拖拽到templates檔案中
設定模板路徑
在這里插入圖片描述
在users.views.py檔案中定義視圖

from django.views import View

class RegisterView(View):
    """用戶注冊"""

    def get(self, request):
        """
        提供注冊界面
        :param request: 請求物件
        :return: 注冊界面
        """
        return render(request, 'register.html')

定義用戶注冊路由

在users子應用中創建urls.py檔案,并定義子路由

from django.urls import path
from users.views import RegisterView

urlpatterns = [
    # 引數1:路由
    # 引數2:視圖函式
    # 引數3:路由名,方便通過reverse來獲取路由
    path('register/',RegisterView.as_view(),name='register'),
]

在工程的urls.py總路由中添加子應用路由引導 dblog中的urls

from django.contrib import admin
from django.urls import path,include

urlpatterns = [
    path('admin/', admin.site.urls),
    # include 引數1要設定為元組(urlconf_module, app_name)
    # namespace 設定命名空間
    path('', include(('users.urls', 'users'), namespace='users')),
]

需要把static中的界面拖拽到templates檔案夾中 因為我們配置的路徑是template存放的界面
在這里插入圖片描述
運行測驗程式,
以上步驟是為了完成 第一個注冊見面并且配置路由
首先配置user里面的視頻viwes 和路由,然后在設定工程的urls.py總路由中添加子應用路由引導
在這里插入圖片描述

修改靜態檔案加載方式

是由于靜態資源加載是相對路徑,因此我們需要修改靜態資源的加載方式

使用static標簽來加載靜態檔案,要使用static標簽,首先需要{% load static %}

# 以下代碼是html的header處修改
    {% load staticfiles %}
    <!-- 引入bootstrap的css檔案 -->
    <link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.min.css' %}">
    <!-- 引入vuejs -->
    <script type="text/javascript" src="{% static 'js/vue-2.5.16.js' %}"></script>
    <script type="text/javascript" src="{% static 'js/axios-0.18.0.min.js' %}"></script>
   
   # 以下代碼是html的footer處修改
    <!-- 引入js -->
    <script type="text/javascript" src="{% static 'js/host.js' %}"></script>
    <script type="text/javascript" src="{% static 'js/common.js' %}"></script>
    <script type="text/javascript" src="{% static 'js/register.js' %}"></script>
# 運行測驗程式,沒有問題

定義用戶模型類

自定義用戶模型類

思考:為什么要自定義用戶模型類?
觀察注冊界面會發現,個人博客注冊頁面中必須有手機號,而且在登錄頁面中也使用手機號進行認證,此外個人中心頁面中有個人頭像和個人簡介欄位,
說白了就是用戶需要自己增加欄位to

如何自定義用戶模型類?

繼承自AbstractUser(可通過閱讀Django默認用戶模型類的原始碼得知) ,
新增手機號欄位,頭像欄位和簡介欄位

這個基類僅有少部分由于Django機制,而設定的函式和常量,

如果連這個都不想繼承的話,直接用自定義,這將有可能導致其余的Django組件無法正常作業,

當然無論是繼承還是自定義,都必須在settings中設定AUTH_USER_MODEL

用戶資訊

from django.db import models

# Create your models here.
from django.contrib.auth.models import AbstractUser

# 用戶資訊
class User(AbstractUser):

    # 電話號碼欄位
    # unique 為唯一性欄位
    mobile = models.CharField(max_length=20, unique=True,blank=True)

    # 頭像
    # upload_to為保存到回應的子目錄中
    avatar = models.ImageField(upload_to='avatar/%Y%m%d/', blank=True)

    # 個人簡介
    user_desc = models.TextField(max_length=500, blank=True)

    # 修改認證的欄位
    USERNAME_FIELD = 'mobile'

    #創建超級管理員的需要必須輸入的欄位
    REQUIRED_FIELDS = ['username','email']

    # 內部類 class Meta 用于給 model 定義元資料
    class Meta:
        db_table='tb_user'              #修改默認的表名
        verbose_name='用戶資訊'         # Admin后臺顯示
        verbose_name_plural=verbose_name # Admin后臺顯示

    def __str__(self):
        return self.mobile

指定本專案用戶模型類

AUTH_USER_MODEL = 'users.User'

在這里插入圖片描述

遷移用戶模型類

在這里插入圖片描述
執行遷移檔案 python manage.py migrate
在這里插入圖片描述
運行測驗程式

圖形驗證碼介面設計和定義

準備captcha包(該包用于生成圖形驗證碼)

將生成圖片驗證碼的庫復制到新建的libs包中,
在這里插入圖片描述
安裝Python處理圖片的庫
在這里插入圖片描述

圖形驗證碼后端介面設計

1.請求方式

選項方案
請求方法GET
請求地址imagecode?uuid=xxxxx-xxxx-xxxxxx

2.請求引數:路徑引數

引數名 型別 是否必傳 說明
uuid string 是 唯一編號
3.回應結果:image/jpeg

圖形驗證碼后端實作
1.圖形驗證碼視圖

from django.http import HttpResponseBadRequest,HttpResponse
from libs.captcha.captcha import captcha
from django_redis import get_redis_connection

class ImageCodeView(View):

    def get(self,request):
        #獲取前端傳遞過來的引數
        uuid=request.GET.get('uuid')
        #判斷引數是否為None
        if uuid is None:
            return HttpResponseBadRequest('請求引數錯誤')
        # 獲取驗證碼內容和驗證碼圖片二進制資料
        text, image = captcha.generate_captcha()
        # 將圖片驗內容保存到redis中,并設定過期時間
        redis_conn = get_redis_connection('default')
        redis_conn.setex('img:%s' % uuid, 300, text)
        # 回傳回應,將生成的圖片以content_type為image/jpeg的形式回傳給請求
        return HttpResponse(image, content_type='image/jpeg')

2.總路由

from django.contrib import admin
from django.urls import path,include

urlpatterns = [
    path('admin/', admin.site.urls),
    # include 引數1要設定為元組(urlconf_module, app_name)
    # namespace 設定命名空間
    path('', include(('users.urls', 'users'), namespace='users')),
]

3.子路由

from django.urls import path
from users.views import ImageCodeView

urlpatterns = [
    # 引數1:路由
    # 引數2:視圖函式
    # 引數3:路由名,方便通過reverse來獲取路由
    path('imagecode/', ImageCodeView.as_view(),name='imagecode'),
]

修改模板中圖片驗證碼HTML代碼

1.html中的原代碼如下

<img src="{% static 'img/image_code.png' %}" @click="generate_image_code" alt="" style="width: 110px;height: 40px;">

2.修改如下

<img :src="image_code_url" @click="generate_image_code" alt="" style="width: 110px;height: 40px;">

路由配置成功在注冊界面可以正常顯示驗證碼并切換

短信驗證碼

短信驗證碼 使用的是容聯云官網 注冊 獲取
在這里插入圖片描述
在這里插入圖片描述

1.集成短信SDK到庫中

CCPRestSDK.py:由容聯云通訊開發者撰寫的官方SDK檔案,包括發送模板短信的方法

ccp_sms.py:呼叫發送模板短信的方法

在這里插入圖片描述

短信驗證碼后端邏輯實作

from django.http import JsonResponse
from utils.response_code import RETCODE
from random import randint
from libs.yuntongxun.sms import CCP
import logging
logger=logging.getLogger('django')

class SmsCodeView(View):

    def get(self,request):
        # 接收引數
        image_code_client = request.GET.get('image_code')
        uuid = request.GET.get('uuid')
        mobile=request.GET.get('mobile')

        # 校驗引數
        if not all([image_code_client, uuid,mobile]):
            return JsonResponse({'code': RETCODE.NECESSARYPARAMERR, 'errmsg': '缺少必傳引數'})

        # 創建連接到redis的物件
        redis_conn = get_redis_connection('default')
        # 提取圖形驗證碼
        image_code_server = redis_conn.get('img:%s' % uuid)
        if image_code_server is None:
            # 圖形驗證碼過期或者不存在
            return JsonResponse({'code': RETCODE.IMAGECODEERR, 'errmsg': '圖形驗證碼失效'})
        # 洗掉圖形驗證碼,避免惡意測驗圖形驗證碼
        try:
            redis_conn.delete('img:%s' % uuid)
        except Exception as e:
            logger.error(e)
        # 對比圖形驗證碼
        image_code_server = image_code_server.decode()  # bytes轉字串
        if image_code_client.lower() != image_code_server.lower():  # 轉小寫后比較
            return JsonResponse({'code': RETCODE.IMAGECODEERR, 'errmsg': '輸入圖形驗證碼有誤'})

        # 生成短信驗證碼:生成6位數驗證碼
        sms_code = '%06d' % randint(0, 999999)
        #將驗證碼輸出在控制臺,以方便除錯
        logger.info(sms_code)
        # 保存短信驗證碼到redis中,并設定有效期
        redis_conn.setex('sms:%s' % mobile, 300, sms_code)
        # 發送短信驗證碼
        CCP().send_template_sms(mobile, [sms_code, 5],1)

        # 回應結果
        return JsonResponse({'code': RETCODE.OK, 'errmsg': '發送短信成功'})

當點擊驗證碼的時候 pycharm控制臺中也會發送一個
在這里插入圖片描述

添加response_code檔案

在工程中新建utils包,將response_code檔案復制到utils中
在這里插入圖片描述

在這里插入圖片描述

用戶注冊實作

from users.models import User
from django.db import DatabaseError
#注冊視圖
class RegisterView(View):

    def get(self,request):

        return render(request,'register.html')

    def post(self,request):
        """
        1.接收資料
        2.驗證資料
            2.1 引數是否齊全
            2.2 手機號的格式是否正確
            2.3 密碼是否符合格式
            2.4 密碼和確認密碼要一致
            2.5 短信驗證碼是否和redis中的一致
        3.保存注冊資訊
        4.回傳回應跳轉到指定頁面
        :param request:
        :return:
        """
        # 1.接收資料
        mobile=request.POST.get('mobile')
        password=request.POST.get('password')
        password2=request.POST.get('password2')
        smscode=request.POST.get('sms_code')
        # 2.驗證資料
        #     2.1 引數是否齊全
        if not all([mobile,password,password2,smscode]):
            return HttpResponseBadRequest('缺少必要的引數')
        #     2.2 手機號的格式是否正確
        if not re.match(r'^1[3-9]\d{9}$',mobile):
            return HttpResponseBadRequest('手機號不符合規則')
        #     2.3 密碼是否符合格式
        if not re.match(r'^[0-9A-Za-z]{8,20}$',password):
            return HttpResponseBadRequest('請輸入8-20位密碼,密碼是數字,字母')
        #     2.4 密碼和確認密碼要一致
        if password != password2:
            return HttpResponseBadRequest('兩次密碼不一致')
        #     2.5 短信驗證碼是否和redis中的一致
        redis_conn = get_redis_connection('default')
        redis_sms_code=redis_conn.get('sms:%s'%mobile)
        if redis_sms_code is None:
            return HttpResponseBadRequest('短信驗證碼已過期')
        if smscode != redis_sms_code.decode():
            return HttpResponseBadRequest('短信驗證碼不一致')
        # 3.保存注冊資訊
        # create_user 可以使用系統的方法來對密碼進行加密
        try:
            user=User.objects.create_user(username=mobile,
                                      mobile=mobile,
                                      password=password)
        except DatabaseError as e:
            logger.error(e)
            return HttpResponseBadRequest('注冊失敗')

        from django.contrib.auth import login
        login(request,user)
        # 4.回傳回應跳轉到指定頁面
        # 暫時回傳一個注冊成功的資訊,后期再實作跳轉到指定頁面

        # redirect 是進行重定向
        # reverse 是可以通過 namespace:name 來獲取到視圖所對應的路由
        response = redirect(reverse('home:index'))
        # return HttpResponse('注冊成功,重定向到首頁')

        #設定cookie資訊,以方便首頁中 用戶資訊展示的判斷和用戶資訊的展示
        response.set_cookie('is_login',True)
        response.set_cookie('username',user.username,max_age=7*24*3600)

        return response

在HTML表單中添加csrf_token
在這里插入圖片描述
在這里插入圖片描述

首頁展示

1.創建首頁應用:home

python manage.py startapp home

2.定義首頁視圖:IndexView—查詢分類資料并展示
2.1.請求方式

選項方案
請求方法GET
請求地址/?cat_id=xxx&page_num=xxx&page_size=xxx

2.2.請求引數

引數名型別是否必傳說明
cat_idstring分類id
page_numstring文章分頁頁碼
page_sizestring文章每頁條目數

2.3.回應結果:HTML

欄位說明
失敗回應錯誤提示
成功展示資料

3.查詢分類文章資料并通過context傳遞給HTML

from django.urls import reverse
from django.views import View
# Create your views here.

class IndexView(View):
    """首頁廣告"""

    def get(self, request):
        """提供首頁廣告界面"""
        return render(request, 'index.html')

4.配置首頁路由

在home子應用中創建urls.py檔案,并定義子路由

from django.urls import path
from home.views import IndexView
urlpatterns = [
    path('', IndexView.as_view(),name='index'),
]

在工程的urls.py總路由中添加子應用路由引導

from django.urls import path, include

urlpatterns = [

    path('', include(('home.urls','home'),namespace='home')),
]

5.重定注冊界面的跳轉到首頁

# 回應注冊結果
return redirect(reverse('home:index'))

用戶登陸

  1. 登錄頁面展示
    1.在users.views.py檔案中定義視圖
from django.views import View

class LoginView(View):

    def get(self,request):
        return render(request,'login.html')

2.在users.urls.py檔案中定義路由

from users.views import LoginView
urlpatterns = [
    # 引數1:路由
    # 引數2:視圖函式
    # 引數3:路由名,方便通過reverse來獲取路由
    path('login/', LoginView.as_view(),name='login'),
]

3.修改login.html中的資源加載方式

<!-- Header部分 -->
{% load staticfiles %}
<!-- 引入bootstrap的css檔案 -->
<link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.min.css' %}">
<!-- 引入vuejs -->
<script type="text/javascript" src="{% static 'js/vue-2.5.16.js' %}"></script>
<script type="text/javascript" src="{% static 'js/axios-0.18.0.min.js' %}"></script>

<!-- Footer部分 -->
<script type="text/javascript" src="{% static 'js/host.js' %}"></script>
<script type="text/javascript" src="{% static 'js/common.js' %}"></script>
<script type="text/javascript" src="{% static 'js/login.js' %}"></script>

<!-- 點擊注冊部分 -->
<small class="form-text text-muted ml-1">還沒有賬號?<a href="{% url 'users:register' %}" style="color: cornflowerblue; ">注冊新賬號</a>

登錄介面實作

from django.contrib.auth import login
from django.contrib.auth import authenticate

class LoginView(View):

    def post(self,request):
        # 接受引數
        mobile = request.POST.get('mobile')
        password = request.POST.get('password')
        remember = request.POST.get('remember')

        # 校驗引數
        # 判斷引數是否齊全
        if not all([mobile, password]):
            return HttpResponseBadRequest('缺少必傳引數')

        # 判斷手機號是否正確
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return HttpResponseBadRequest('請輸入正確的手機號')

        # 判斷密碼是否是8-20個數字
        if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
            return HttpResponseBadRequest('密碼最少8位,最長20位')

        # 認證登錄用戶
        # 認證欄位已經在User模型中的USERNAME_FIELD = 'mobile'修改
        user=authenticate(mobile=mobile, password=password)

        if user is None:
            return HttpResponseBadRequest('用戶名或密碼錯誤')

        # 實作狀態保持
        login(request, user)

        # 回應登錄結果
        response =  redirect(reverse('home:index'))

        # 設定狀態保持的周期
        if remember != 'on':
            # 沒有記住用戶:瀏覽器會話結束就過期
            request.session.set_expiry(0)
            # 設定cookie
            response.set_cookie('is_login', True)
            response.set_cookie('username', user.username, max_age=30 * 24 * 3600)
        else:
            # 記住用戶:None表示兩周后過期
            request.session.set_expiry(None)
            # 設定cookie
            response.set_cookie('is_login', True, max_age=14*24 * 3600)
            response.set_cookie('username', user.username, max_age=30 * 24 * 3600)
        #回傳回應
        return response

注冊登陸設定成功之后現在開始對首頁進行設定用戶名顯示到首頁
在這里插入圖片描述

首頁用戶名展示

用戶名寫入到cookie
Vue渲染首頁用戶名
1.index.html

<!-- 如果用戶已經登錄,則顯示用戶名下拉框 -->
<li class="nav-item dropdown" v-if="is_login">
    <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" @click="show_menu_click">[[username]]</a>
    <div class="dropdown-menu" aria-labelledby="navbarDropdown" style="display: block" v-show="show_menu">
        <a class="dropdown-item" href="../static/write_blog.html">寫文章</a>
        <a class="dropdown-item" href='../static/center.html'>個人資訊</a>
        <a class="dropdown-item" href='#'>退出登錄</a>
    </div>
</li>
<!-- 如果用戶未登錄,則顯示登錄按鈕 -->
<li class="nav-item" v-else>
    <a class="nav-link" href="login.html">登錄</a>
</li>

2.index.js

mounted(){
    //獲取用戶名資訊
    this.username=getCookie('username');
    //獲取是否登錄資訊
    this.is_login=getCookie('is_login');
},

在這里插入圖片描述

退出登錄

logout()方法介紹
退出登錄:

對session操作的,也就是操作redis,所以回傳的要么是空,要么成功,不會出現例外

logout()方法:

只需要傳入一個request物件就行,就會把當前用戶的session清除

logout()位置:

django.contrib.auth.init.py檔案中

from django.contrib.auth import logout
class LogoutView(View):

    def get(self,request):
        # 1.session資料清除
        logout(request)
        # 2.洗掉部分cookie資料
        response=redirect(reverse('home:index'))
        response.delete_cookie('is_login')
        #3.跳轉到首頁
        return response

提示:

由于首頁中登錄狀態是從cookie中讀取的,
所以退出登錄時,需要將cookie中登錄狀態清除,

實作退出登錄

<div class="dropdown-menu" aria-labelledby="navbarDropdown" style="display: block" v-show="show_menu">
    <a class="dropdown-item" href="../static/write_blog.html">寫文章</a>
    <a class="dropdown-item" href='../static/center.html'>個人資訊</a>
    <a class="dropdown-item" href='{% url 'users:logout' %}'>退出登錄</a>
</div>

路由配置 users.urls.py

urlpatterns = [
    # 引數1:路由
    # 引數2:視圖函式
    # 引數3:路由名,方便通過reverse來獲取路由
    path('register/',RegisterView.as_view(),name='register'),
    path('imagecode/', ImageCodeView.as_view(),name='imagecode'),
    # 短信發送
    path('smscode/', SmsCodeView.as_view(), name='smscode'),
    # 登錄路由
    path('login/', LoginView.as_view(), name='login'),
    # 退出登錄
    path('logout/', LogoutView.as_view(), name='logout'),
]

忘記密碼

1.在users.views.py檔案中定義視圖

from django.views import View

class ForgetPasswordView(View):

    def get(self, request):

        return render(request, 'forget_password.html')

2.在users.urls.py檔案中定義路由

from users.views import ForgetPasswordView
path('forgetpassword/', ForgetPasswordView.as_view(),name='forgetpassword'),

3.修改forget_password.html中的資源加載方式

<!-- Header部分 -->
    {% load staticfiles %}
    <!-- 引入bootstrap的css檔案 -->
    <link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.min.css' %}">
    <!-- 引入vuejs -->
    <script type="text/javascript" src="{% static 'js/vue-2.5.16.js' %}"></script>
    <script type="text/javascript" src="{% static 'js/axios-0.18.0.min.js' %}"></script>
    ...
    <!-- Footer部分 -->
    <script type="text/javascript" src="{% static 'js/host.js' %}"></script>
    <script type="text/javascript" src="{% static 'js/common.js' %}"></script>
    <script type="text/javascript" src="{% static 'js/forget_password.js' %}"></script>
    ...
    <!-- 圖片驗證碼部分 -->
    <img :src="image_code_url" @click="generate_image_code" alt="" style="width: 110px;height: 40px;">

4.修改login.html中的忘記密碼的跳轉連接

<small class="form-text text-muted ml-1"><a class="secondaryAction layui-text" href="{% url 'users:forgetpassword' %}">忘記密碼?</a>

忘記密碼介面實作

引數名型別是否必傳說明
mobilestring用戶名
passwordstring密碼
password2string確認密碼
sms_codestring短信驗證碼

代碼

class ForgetPasswordView(View):

    def post(self, request):
        # 接收引數
        mobile = request.POST.get('mobile')
        password = request.POST.get('password')
        password2 = request.POST.get('password2')
        smscode = request.POST.get('sms_code')

        # 判斷引數是否齊全
        if not all([mobile, password, password2, smscode]):
            return HttpResponseBadRequest('缺少必傳引數')

        # 判斷手機號是否合法
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return HttpResponseBadRequest('請輸入正確的手機號碼')

        # 判斷密碼是否是8-20個數字
        if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
            return HttpResponseBadRequest('請輸入8-20位的密碼')

        # 判斷兩次密碼是否一致
        if password != password2:
            return HttpResponseBadRequest('兩次輸入的密碼不一致')

        # 驗證短信驗證碼
        redis_conn = get_redis_connection('default')
        sms_code_server = redis_conn.get('sms:%s' % mobile)
        if sms_code_server is None:
            return HttpResponseBadRequest('短信驗證碼已過期')
        if smscode != sms_code_server.decode():
            return HttpResponseBadRequest('短信驗證碼錯誤')

        # 根據手機號查詢資料
        try:
            user = User.objects.get(mobile=mobile)
        except User.DoesNotExist:
            # 如果該手機號不存在,則注冊個新用戶
            try:
                User.objects.create_user(username=mobile, mobile=mobile, password=password)
            except Exception:
                return HttpResponseBadRequest('修改失敗,請稍后再試')
        else:
            # 修改用戶密碼
            user.set_password(password)
            user.save()

        # 跳轉到登錄頁面
        response = redirect(reverse('users:login'))

        return response

路由配置 urts.py

 # 忘記密碼
    path('forgetpassword/', ForgetPasswordView.as_view(),name='forgetpassword'),

在這里插入圖片描述

用戶中心展示

  1. 頁面展示

1.在users.views.py檔案中定義視圖

from django.views import View

class UserCenterView(View):

    def get(self,request):

        return render(request,'center.html')

2.在users.urls.py檔案中定義路由

from users.views import UserCenterView
urlpatterns = [
    # 引數1:路由
    # 引數2:視圖函式
    # 引數3:路由名,方便通過reverse來獲取路由
    path('center/', UserCenterView.as_view(),name='center'),
]

3.修改center.html中的資源加載方式


    <!-- Header部分 -->
    {% load staticfiles %}
    <!-- 引入bootstrap的css檔案 -->
    <link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.min.css' %}">
    <!-- 引入vuejs -->
    <script type="text/javascript" src="{% static 'js/vue-2.5.16.js' %}"></script>
    <script type="text/javascript" src="{% static 'js/axios-0.18.0.min.js' %}"></script>
    ...
    <!-- Footer部分 -->
    <script type="text/javascript" src="{% static 'js/host.js' %}"></script>
    <script type="text/javascript" src="{% static 'js/common.js' %}"></script>
    <script type="text/javascript" src="{% static 'js/center.js' %}"></script>
    ...
    <!-- 頁面跳轉部分 -->
    <a class="dropdown-item" href='{% url 'users:center' %}'>個人資訊</a>
    <a class="dropdown-item" href='{% url 'users:logout' %}'>退出登錄</a>

4.修改index.html中的的跳轉連接

<a class="dropdown-item" href='{% url 'users:center' %}'>個人資訊</a>

在這里插入圖片描述

判斷用戶是否登錄

根據是否登錄的結果,決定用戶是否可以訪問用戶中心,
Django用戶認證系統提供了方法

request.user.is_authenticated()來判斷用戶是否登錄,如果通過登錄驗證則回傳True,反之,回傳False,
LoginRequiredMixin封裝了判斷用戶是否登錄的操作,

1.用戶中心使用LoginRequiredMixin

from django.views import View
from django.contrib.auth.mixins import LoginRequiredMixin

class UserCenterView(LoginRequiredMixin,View):

    def get(self,request):

        return render(request,'center.html')

2.設定未登錄用戶跳轉的路由

#在工程的settings.py檔案中,添加以下配置,
LOGIN_URL = '/login/'

3.根據登錄的next引數設定登錄跳轉路由

實作狀態保持

login(request, user)

回應登錄結果

next = request.GET.get('next')
if next:
    response= redirect(next)
else:
    response =  redirect(reverse('home:index'))

1.獲取用戶資訊,模板渲染資料users.views.py 中添加功能

from django.contrib.auth.mixins import LoginRequiredMixin
class UserCenterView(LoginRequiredMixin,View):

    def get(self,request):
        # 獲取用戶資訊
        user = request.user

        #組織模板渲染資料
        context = {
            'username': user.username,
            'mobile': user.mobile,
            'avatar': user.avatar.url if user.avatar else None,
            'user_desc': user.user_desc
        }

        return render(request,'center.html',context=context)

2.修改center.html中的資料顯示

<form method="post" enctype="multipart/form-data">
    <!-- username -->
    <div class="form-group col-md-4">
        <label for="username">用戶名</label>
        <input type="text" class="form-control" id="username" name="username" value="{{ username }}" >
    </div>
    {% if avatar %}
        <br> <div class="col-md-4">頭像</div>
        <img src="{{ avatar }}" style="max-width: 20%; border-radius: 15%;" class="col-md-4"><br>
        {% else %}
        <br><h5 class="col-md-4">暫無頭像</h5><br>
    {% endif %}
    <!-- avatar -->
    <div class="form-group col-md-4">
        <label for="avatar">上傳頭像</label>
        <input type="file" class="form-control-file" name="avatar" id="avatar">
    </div>

    <!-- phone -->
    <div class="form-group col-md-4">
        <label for="phone">電話</label>
        <input type="text" class="form-control" disabled="disabled" id="phone" name="phone" value="{{ mobile }}">
    </div>
    <!-- desc -->
    <div class="form-group col-md-4">
        <label for="desc">簡介</label>
        <!-- 文本區域 -->
        <textarea type="text" class="form-control" id="desc" name="desc" rows="12" >{{ user_desc }}</textarea>
    </div>
    <!-- 提交按鈕 -->
    <button type="submit" class="btn btn-primary" style="margin-left: 12px" >修改</button>
</form>

用戶中心修改

  1. 用戶中心介面設計
    1.請求方式
選項方案
請求方法POST
請求地址/center/

2.請求引數:表單

引數名型別是否必傳說明
username string用戶名
avatarfile頭像
descstring個人簡介

3.回應結果:HTML

欄位說明
修改失敗回應錯誤提示
修改成功重繪展示
  1. 用戶中心修改介面實作
from django.contrib.auth.mixins import LoginRequiredMixin
class UserCenterView(LoginRequiredMixin,View):

    def post(self,request):
        # 接收資料
        user = request.user
        avatar = request.FILES.get('avatar')
        username = request.POST.get('username',user.username)
        user_desc = request.POST.get('desc',user.user_desc)

        # 修改資料庫資料
        try:
            user.username=username
            user.user_desc=user_desc
            if avatar:
                user.avatar=avatar
            user.save()
        except Exception as e:
            logger.error(e)
            return HttpResponseBadRequest('更新失敗,請稍后再試')

        # 回傳回應,重繪頁面
        response = redirect(reverse('users:center'))
        #更新cookie資訊
        response.set_cookie('username',user.username,max_age=30*24*3600)
        return response
  1. 用戶中心頭像的上傳和展示
    1.在settings.py檔案中設定圖片上傳的路徑并新建檔案夾media
MEDIA_ROOT = os.path.join(BASE_DIR, 'media/')

在settings.py檔案中設定

圖片的統一路由

MEDIA_URL = '/media/'

設定路由匹配規則,在工程的urls.py檔案中設定

from django.contrib import admin
from django.urls import path,include

urlpatterns = [
    path('admin/', admin.site.urls),
    # include 引數1要設定為元組(urlconf_module, app_name)
    # namespace 設定命名空間
    path('', include(('users.urls', 'users'), namespace='users')),
    path('', include(('home.urls','home'),namespace='home')),
]
#以下代碼為設定圖片訪問路由規則
from django.conf import settings
from django.conf.urls.static import static
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

在這里插入圖片描述

寫博客頁面展示

  1. 頁面展示
    1.在users.views.py檔案中定義視圖
from django.views import View

class WriteBlogView(LoginRequiredMixin,View):

    def get(self,request):

        return render(request,'write_blog.html')

2.在users.urls.py檔案中定義路由

from users.views import WriteBlogView
urlpatterns = [
    # 引數1:路由
    # 引數2:視圖函式
    # 引數3:路由名,方便通過reverse來獲取路由
    path('writeblog/', WriteBlogView.as_view(),name='writeblog'),
]

3.修改center.html中的資源加載方式

<!-- Header部分 -->
{% load staticfiles %}
<!-- 引入bootstrap的css檔案 -->
<link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.min.css' %}">
<!-- 引入vuejs -->
<script type="text/javascript" src="{% static 'js/vue-2.5.16.js' %}"></script>
<script type="text/javascript" src="{% static 'js/axios-0.18.0.min.js' %}"></script>
<!-- Footer部分 -->
<!--ckeditor-->
<script type="text/javascript" src="{% static 'ckeditor/ckeditor-init.js' %}" data-ckeditor-basepath="{% static 'ckeditor/ckeditor/' %}" id="ckeditor-init-script"></script>
<script type="text/javascript" src="{% static 'ckeditor/ckeditor/ckeditor.js' %}"></script>
<!-- 引入js -->
<script type="text/javascript" src="{% static 'js/host.js' %}"></script>
<script type="text/javascript" src="{% static 'js/common.js' %}"></script>
<script type="text/javascript" src="{% static 'js/write_blog.js' %}"></script>
<!-- 頁面跳轉部分 -->
<a class="dropdown-item" href="{% url 'users:writeblog' %}">寫文章</a>
<a class="dropdown-item" href='{% url 'users:center'%}'>個人資訊</a>
<a class="dropdown-item" href='{% url 'users:center' %}'>退出登錄</a>

4.修改index.html中的的跳轉連接

<a class="dropdown-item" href="{% url 'users:writeblog' %}">寫文章</a>

在這里插入圖片描述

文章分類模型

  1. 定義模型類
    在home子應用的models.py模型中定義文章分類模型
from django.db import models
from django.utils import timezone

class ArticleCategory(models.Model):
    """
    文章分類
    """
    # 欄目標題
    title = models.CharField(max_length=100, blank=True)
    # 創建時間
    created = models.DateTimeField(default=timezone.now)

    def __str__(self):
        return self.title

    class Meta:
        db_table='tb_category'
        verbose_name = '類別管理'
        verbose_name_plural = verbose_name
  1. 遷移模型類
1.創建遷移檔案

python manage.py makemigrations
2.執行遷移檔案

python manage.py migrate

文章分類后臺管理

當剛創建的時候,首頁是沒有資料,我們需要從后臺管理界面進行文章分類,從而展示到主界面
網站的管理員負責查看、添加、修改、洗掉資料

Django能夠根據定義的模型類自動地生成管理模塊
登陸站點:http://127.0.0.1:8000/admin
在setting中設定中文資訊

LANGUAGE_CODE = 'zh-Hans'   #原配置資訊為'en-us'

TIME_ZONE = 'Asia/Shanghai'#原配置資訊為'UTC'

創建管理員
1.我們需要在User模型中設定 REQUIRED_FIELDS
在 users.modle.py中設定配置資訊

#創建超級管理員的需要必須輸入的欄位
REQUIRED_FIELDS = ['username','email']

在這里插入圖片描述

2.在終端創建超級管理員

創建管理員的命令 :

  python manage.py createsuperuser

在這里插入圖片描述
然后重新登陸進入 站點服務
注冊模型類
在應用的admin.py檔案中注冊模型類

需要匯入模型模塊 :from home.models import ArticleCategory
在這里插入圖片描述

模型注冊完之后我們即可在站點管理進行對分類操作
在這里插入圖片描述
模型類展示我們輸入的內容是因為我們在模型中實作了__str_方法_
home.model.py

class ArticleCategory(models.Model):
    """
    文章分類
    """
    # 欄目標題
    title = models.CharField(max_length=100, blank=True)
    # 創建時間
    created = models.DateTimeField(default=timezone.now)

    def __str__(self):
        return self.title

寫博客頁面展示分類

  1. 查詢資料并展示
    1.查詢分類文章資料并通過context傳遞給HTML
from home.models import ArticleCategory
class WriteBlogView(LoginRequiredMixin,View):

    def get(self,request):
        # 獲取博客分類資訊
        categories = ArticleCategory.objects.all()

        context = {
            'categories': categories
        }
        return render(request,'write_blog.html',context=context)

2.在write_blog.html檔案中使用模板語言展示資料

<!-- 文章欄目 -->
<div class="form-group">
    <label for="column">欄目</label>
    <select class="form-control col-3" id="category" name="category">
            {% for category in categories %}
                <option value="{{ category.id }}">{{ category.title }}</option>
            {% endfor %}
    </select>
</div>

在這里插入圖片描述

文章模型

在home子應用的models.py模型中定義文章模型

from users.models import User 
class Article(models.Model):
    """
    文章
    """
    # 定義文章作者, author 通過 models.ForeignKey 外鍵與內建的 User 模型關聯在一起
    # 引數 on_delete 用于指定資料洗掉的方式,避免兩個關聯表的資料不一致,
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    # 文章標題圖
    avatar = models.ImageField(upload_to='article/%Y%m%d/', blank=True)
    # 文章欄目的 “一對多” 外鍵
    category = models.ForeignKey(
        ArticleCategory,
        null=True,
        blank=True,
        on_delete=models.CASCADE,
        related_name='article'
    )
    # 文章標簽
    tags = models.CharField(max_length=20,blank=True)
    # 文章標題,
    title = models.CharField(max_length=100,null=False,blank=False)
    # 概要
    sumary = models.CharField(max_length=200,null=False,blank=False)
    # 文章正文,
    content = models.TextField()
    # 瀏覽量
    total_views = models.PositiveIntegerField(default=0)
    # 文章評論數
    comments_count = models.PositiveIntegerField(default=0)
    # 文章創建時間,
    # 引數 default=timezone.now 指定其在創建資料時將默認寫入當前的時間
    created = models.DateTimeField(default=timezone.now)
    # 文章更新時間,
    # 引數 auto_now=True 指定每次資料更新時自動寫入當前時間
    updated = models.DateTimeField(auto_now=True)

    # 內部類 class Meta 用于給 model 定義元資料
    class Meta:
        # ordering 指定模型回傳的資料的排列順序
        # '-created' 表明資料應該以倒序排列
        ordering = ('-created',)
        db_table='tb_article'
        verbose_name='文章管理'
        verbose_name_plural=verbose_name
    # 函式 __str__ 定義當呼叫物件的 str() 方法時的回傳值內容
    # 它最常見的就是在Django管理后臺中做為物件的顯示值,因此應該總是為 __str__ 回傳一個友好易讀的字串
    def __str__(self):
        # 將文章標題回傳
        return self.title

遷移模型類
1.創建遷移檔案

python manage.py makemigrations

2.執行遷移檔案

python manage.py migrate

博客保存

博客保存介面設計
1.請求方式

選項方案
請求方法POST
請求地址/writeblog/

2.請求引數:表單

引數名型別是否必傳說明
title string標題
avatar file標題圖
categorystring欄目分類
tagsstring標簽
sumarystring文章摘要
contentstring文章內容

3.回應結果:HTML

欄位說明
提交失敗回應錯誤提示
提交成功跳轉到詳情頁面
  1. 用戶中心修改介面實作
from home.models import ArticleCategory,Article
class WriteBlogView(LoginRequiredMixin,View):

    def post(self,request):
        #接收資料
        avatar=request.FILES.get('avatar')
        title=request.POST.get('title')
        category_id=request.POST.get('category')
        tags=request.POST.get('tags')
        sumary=request.POST.get('sumary')
        content=request.POST.get('content')
        user=request.user

        #驗證資料是否齊全
        if not all([avatar,title,category_id,sumary,content]):
            return HttpResponseBadRequest('引數不全')

        #判斷文章分類id資料是否正確
        try:
            article_category=ArticleCategory.objects.get(id=category_id)
        except ArticleCategory.DoesNotExist:
            return HttpResponseBadRequest('沒有此分類資訊')

        #保存到資料庫
        try:
            article=Article.objects.create(
                author=user,
                avatar=avatar,
                category=article_category,
                tags=tags,
                title=title,
                sumary=sumary,
                content=content
            )
        except Exception as e:
            logger.error(e)
            return HttpResponseBadRequest('發布失敗,請稍后再試')

        #回傳回應,跳轉到文章詳情頁面
        #暫時先跳轉到首頁
        return redirect(reverse('home:index'))

在這里插入圖片描述

首頁分類資料展示

2.定義首頁視圖:IndexView—查詢分類資料并展示
2.1.請求方式

選項方案
請求方法GET
請求地址/?cat_id=xxx&page_num=xxx&page_size=xxx

2.2.請求引數

引數名型別是否必傳說明
cat_idstring分類id
page_numstring文章分頁頁碼
page_sizestring文章每頁條目數

2.3.回應結果:HTML

欄位說明
失敗回應錯誤提示
成功展示資料

1.查詢分類文章資料并通過context傳遞給HTML
home.views.py


from home.models import ArticleCategory
from django.http import HttpResponseNotFound

class IndexView(View):
    """首頁廣告"""

    def get(self, request):
        """提供首頁廣告界面"""
        #?cat_id=xxx&page_num=xxx&page_size=xxx
        cat_id=request.GET.get('cat_id',1)

        #判斷分類id
        try:
            category = ArticleCategory.objects.get(id=cat_id)
        except ArticleCategory.DoesNotExist:
            return HttpResponseNotFound('沒有此分類')

        # 獲取博客分類資訊
        categories = ArticleCategory.objects.all()

        context = {
            'categories':categories,
            'category':category
        }

        return render(request, 'index.html',context=context)

2.在index.html檔案中使用模板語言展示分類資料

<ul class="nav navbar-nav">
    {% for cat in categories %}
        {% if cat.id == category.id %}
            <li class="nav-item active">
                <a class="nav-link mr-2" href="/?cat_id={{ cat.id }}">{{ cat.title }}</a>
            </li>
        {% else %}
            <li class="nav-item">
                <a class="nav-link mr-2" href="/?cat_id={{ cat.id }}">{{ cat.title }}</a>
            </li>
        {% endif %}
    {% endfor %}
</ul>

查詢分頁資料并展示

from home.models import ArticleCategory,Article
from django.http import HttpResponseNotFound
from django.core.paginator import Paginator,EmptyPage

class IndexView(View):
    """首頁廣告"""

    def get(self, request):
        """提供首頁廣告界面"""
        #?cat_id=xxx&page_num=xxx&page_size=xxx
        cat_id=request.GET.get('cat_id',1)
        page_num = request.GET.get('page_num', 1)
        page_size = request.GET.get('page_size', 10)
        #判斷分類id
        try:
            category = ArticleCategory.objects.get(id=cat_id)
        except ArticleCategory.DoesNotExist:
            return HttpResponseNotFound('沒有此分類')

        # 獲取博客分類資訊
        categories = ArticleCategory.objects.all()

        #分頁資料
        articles = Article.objects.filter(
            category=category
        )

        # 創建分頁器:每頁N條記錄
        paginator = Paginator(articles, page_size)
        # 獲取每頁商品資料
        try:
            page_articles = paginator.page(page_num)
        except EmptyPage:
            # 如果沒有分頁資料,默認給用戶404
            return HttpResponseNotFound('empty page')
        # 獲取串列頁總頁數
        total_page = paginator.num_pages

        context = {
            'categories':categories,
            'category':category,
            'articles': page_articles,
            'page_size': page_size,
            'total_page': total_page,
            'page_num': page_num,
        }

        return render(request, 'index.html',context=context)

2.在index.html檔案中使用模板語言展示分類資料

<div class="container">
    <!-- 串列回圈 -->
     {% for article in articles %}
        <div class="row mt-2">
            <!-- 文章內容 -->
            <!-- 標題圖 -->
            <div class="col-3">
                <img src="{{ article.avatar.url }}" alt="avatar" style="max-width:100%; border-radius: 20px">
            </div>
            <div class="col">
                <!-- 欄目 -->
                <a  role="button" class="btn btn-sm mb-2 btn-warning">{{ article.category.title }}</a>
            <!-- 標簽 -->
                <span>
                        <a class="badge badge-secondary">{{ article.tags }}</a>
                </span>
                <!-- 標題 -->
                <h4>
                    <b><a href="./detail.html" style="color: black;">{{ article.title }}</a></b>
                </h4>
                <!-- 摘要 -->
                <div>
                    <p style="color: gray;">
                        {{ article.sumary }}
                    </p>
                </div>
                <!-- 注腳 -->
                <p>
                    <!-- 查看、評論、時間 -->
                    <span><i class="fas fa-eye" style="color: lightskyblue;"></i>{{ article.total_views }}&nbsp;&nbsp;&nbsp;</span>
                    <span><i class="fas fa-comments" style="color: yellowgreen;"></i>{{ article.comments_count }}&nbsp;&nbsp;&nbsp;</span>
                    <span><i class="fas fa-clock" style="color: pink;"></i>{{ article.created | date }}</span>
                </p>
            </div>
            <hr style="width: 100%;"/>
    </div>
    {% endfor %}
    <!-- 頁碼導航 -->
    <div class="pagenation" style="text-align: center">
        <div id="pagination" class="page"></div>
    </div>
</div>

3.修改底部js分頁代碼

<script type="text/javascript">
    $(function () {
        $('#pagination').pagination({
            currentPage: {{ page_num }},
            totalPage: {{ total_page }},
            callback:function (current) {

                location.href = '/?cat_id={{ category.id }}&page_size={{ page_size }}&page_num='+current;
            }
        })
    });
</script>

博客詳情

詳情頁面展示

  1. 頁面展示
    1.在home.views.py檔案中定義視圖
from django.views import View

class DetailView(View):

    def get(self,request):


        return render(request,'detail.html')

2.在home.urls.py檔案中定義路由

from users.views import DetailView
urlpatterns = [
    # 引數1:路由
    # 引數2:視圖函式
    # 引數3:路由名,方便通過reverse來獲取路由
    path('detail/', DetailView.as_view(),name='detail'),
]

3.修改detail.html中的資源加載方式

    <!-- Header部分 -->
       {% load staticfiles %}
    <!-- 引入bootstrap的css檔案 -->
    <link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.min.css' %}">
    <!--詳情頁面匯入-->
    <script src="{% static 'ckeditor/ckeditor/plugins/prism/lib/prism/prism_patched.min.js' %}"></script>
    <link rel="stylesheet" href="{% static 'prism/prism.css' %}">
    <!--匯入css-->
    <link rel="stylesheet" href="{% static 'common/common.css' %}">
    <link rel="stylesheet" href="{% static 'common/jquery.pagination.css' %}">
    <!-- 引入vuejs -->
    <script type="text/javascript" src="{% static 'js/vue-2.5.16.js' %}"></script>
    <script type="text/javascript" src="{% static 'js/axios-0.18.0.min.js' %}"></script>
    <script type="text/javascript" src="{% static 'js/jquery-1.12.4.min.js' %}"></script>
    ...
    <!-- Footer部分 -->
    <!--ckeditor-->
    <script type="text/javascript" src="{% static 'ckeditor/ckeditor-init.js' %}" data-ckeditor-basepath="{% static 'ckeditor/ckeditor/' %}" id="ckeditor-init-script"></script>
    <script type="text/javascript" src="{% static 'ckeditor/ckeditor/ckeditor.js' %}"></script>
    <!-- 引入js -->
    <script type="text/javascript" src="{% static 'js/host.js' %}"></script>
    <script type="text/javascript" src="{% static 'js/common.js' %}"></script>
    <script type="text/javascript" src="{% static 'js/detail.js' %}"></script>
    <script type="text/javascript" src="{% static 'js/jquery.pagination.min.js' %}"></script>
    ...
    <!-- 頁面跳轉部分 -->
    <a class="dropdown-item" href="{% url 'users:writeblog' %}">寫文章</a>
    <a class="dropdown-item" href='{% url 'users:center'%}'>個人資訊</a>
    <a class="dropdown-item" href='{% url 'users:center' %}'>退出登錄</a>
  1. 查詢分類資料并展示
    1.查詢文章資料并通過context傳遞給HTML
class DetailView(View):

    def get(self,request):
        # detail/?id=xxx&page_num=xxx&page_size=xxx
        #獲取檔案id
        id=request.GET.get('id')

        # 獲取博客分類資訊
        categories = ArticleCategory.objects.all()

        try:
            article=Article.objects.get(id=id)
        except Article.DoesNotExist:
            return render(request,'404.html')

        context = {
            'categories':categories,
            'category':article.category,
            'article':article,
        }

        return render(request,'detail.html',context=context)

2.在detail.html檔案中使用模板語言展示文章資料

#分類資料展示
 <div>
    <ul class="nav navbar-nav">
        {% for cat in categories %}
            {% if cat.id == category.id %}
                <li class="nav-item active">
                    <a class="nav-link mr-2" href="/?cat_id={{ cat.id }}">{{ cat.title }}</a>
                </li>
            {% else %}
                <li class="nav-item">
                    <a class="nav-link mr-2" href="/?cat_id={{ cat.id }}">{{ cat.title }}</a>
                </li>
            {% endif %}
        {% endfor %}
    </ul>
</div>

#詳情資料展示
 <!-- 標題及作者 -->
<h1 class="mt-4 mb-4">{{ article.title }}</h1>
<div class="alert alert-success"><div>作者:<span>{{ article.author.username }}</span></div><div>瀏覽:{{ article.total_views }}</div></div>
<!-- 文章正文 -->
<div class="col-12" style="word-break: break-all;word-wrap: break-word;">
    {{ article.content|safe }}
</div>
<br>

在這里插入圖片描述

  1. 修改首頁跳轉到詳情頁面的鏈接

<!-- 標題 -->
<h4>
    <b><a href="{% url 'home:detail' %}?id={{ article.id }}" style="color: black;">{{ article.title }}</a></b>
</h4>

推薦文章資料展示

  1. 添加文章瀏覽量資料
    1.每次請求文章詳情時給瀏覽量+1
try:
    article=Article.objects.get(id=id)
except Article.DoesNotExist:
    return render(request,'404.html')
else:
    article.total_views+=1
    article.save()
  1. 查詢推薦文章并展示
    1.查詢推薦文章資料并通過context傳遞給HTML
class DetailView(View):

    def get(self,request):
        # detail/?id=xxx&page_num=xxx&page_size=xxx
        #獲取檔案id
        id=request.GET.get('id')

        # 獲取博客分類資訊
        categories = ArticleCategory.objects.all()

        try:
            article=Article.objects.get(id=id)
        except Article.DoesNotExist:
            return render(request,'404.html')
        else:
            article.total_views+=1
            article.save()

        # 獲取熱點資料
        hot_articles = Article.objects.order_by('-total_views')[:9]

        context = {
            'categories':categories,
            'category':article.category,
            'article':article,
            'hot_articles':hot_articles
        }
        return render(request,'detail.html',context=context)

2.在detail.html檔案中使用模板語言展示推薦資料

<div class="sidebar__inner">
    <h4><strong>推薦</strong></h4>
    <hr>
    {% for hot_article in hot_articles %}
        <a href="{% url 'home:detail' %}?id={{ hot_article.id }}" style="color: black">{{ hot_article.title }}</a><br>
    {% endfor %}
    </div>
</div>

評論模型

  1. 定義模型類
    在home子應用的models.py模型中定義評論模型
class Comment(models.Model):
    #評論內容
    content=models.TextField()
    #評論的文章
    article=models.ForeignKey(Article,
                              on_delete=models.SET_NULL,
                              null=True)
    #發表評論的用戶
    user=models.ForeignKey('users.User',
                           on_delete=models.SET_NULL,
                           null=True)
    #評論發布時間
    created=models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.article.title

    class Meta:
        db_table='tb_comment'
        verbose_name = '評論管理'
        verbose_name_plural = verbose_name
  1. 遷移模型類
    1.創建遷移檔案
python manage.py makemigrations

2.執行遷移檔案

python manage.py migrate

發表評論

  1. 發表評論介面設計
    1.請求方式
選項方案
請求方法POST
請求地址/detail/

2.請求引數:表單

引數名型別是否必傳說明
user_idstring發表評論的用戶id
article_idstring評論的文字id
contentstring評論內容

3.回應結果:HTML

欄位說明
提交失敗回應錯誤提示
提交成功重繪頁面展示
  1. 發表評論介面實作
    1.發表評論實作
from home.models import Comment,Article
class DetailView(View):

    def post(self,request):
        #獲取用戶資訊
        user=request.user

        #判斷用戶是否登錄
        if user and user.is_authenticated:
            #接收資料
            id=request.POST.get('id')
            content=request.POST.get('content')

            #判斷文章是否存在
            try:
                article = Article.objects.get(id=id)
            except Article.DoesNotExist:
                return HttpResponseNotFound('沒有此文章')

            #保存到資料
            Comment.objects.create(
                content=content,
                article=article,
                user=user
            )
            #修改文章評論數量
            article.comments_count+=1
            article.save()
            #拼接跳轉路由
            path=reverse('home:detail')+'?id={}'.format(article.id)
            return redirect(path)
        else:
            #沒有登錄則跳轉到登錄頁面
            return redirect(reverse('users:login'))

2.detail.html修改

 <form method="POST">
    {% csrf_token %}
    <input type="hidden" name="id" value="{{ article.id }}">
    <div class="form-group"><label for="body"><strong>我也要發言:</strong></label>
        <div>
            <div class="django-ckeditor-widget" data-field-id="id_body" style="display: inline-block;">
                <textarea cols="40" id="id_body" name="content" rows="10" required data-processed="0" :data-config="data_config" data-external-plugin-resources="[]" data-id="id_body" data-type="ckeditortype">

                </textarea>
            </div>
        </div>
    </div>
    <!-- 提交按鈕 -->
    <button type="submit" class="btn btn-primary ">發送</button>
</form>

詳情評論資料展示

  1. 查詢評論資料并展示
    1.查詢評論資料并通過context傳遞給HTML
    home.views.py
from home.models import Comment
from django.shortcuts import redirect,reverse
class DetailView(View):

    def get(self,request):
        # detail/?id=xxx&page_num=xxx&page_size=xxx
        #獲取檔案id
        id=request.GET.get('id')
        page_num=request.GET.get('page_num',1)
        page_size=request.GET.get('page_size',5)
        # 獲取博客分類資訊
        categories = ArticleCategory.objects.all()

        try:
            article=Article.objects.get(id=id)
        except Article.DoesNotExist:
            return render(request,'404.html')
        else:
            article.total_views+=1
            article.save()

        # 獲取熱點資料
        hot_articles = Article.objects.order_by('-total_views')[:9]

        # 獲取當前文章的評論資料
        comments = Comment.objects.filter(
            article=article
        ).order_by('-created')
        #獲取評論總數
        total_count = comments.count()

        # 創建分頁器:每頁N條記錄
        paginator = Paginator(comments, page_size)
        # 獲取每頁商品資料
        try:
            page_comments = paginator.page(page_num)
        except EmptyPage:
            # 如果page_num不正確,默認給用戶404
            return HttpResponseNotFound('empty page')
        # 獲取串列頁總頁數
        total_page = paginator.num_pages

        context = {
            'categories':categories,
            'category':article.category,
            'article':article,
            'hot_articles':hot_articles,
            'total_count': total_count,
            'comments': page_comments,
            'page_size': page_size,
            'total_page': total_page,
            'page_num': page_num,
        }

        return render(request,'detail.html',context=context)

在這里插入圖片描述

2.在index.html檔案中使用模板語言展示分類資料

<!-- 顯示評論 -->
<h4>共有{{ total_count }}條評論</h4>
<div class="row">
     {% for comment in comments %}
        <div class="col-12" >
            <hr><p><strong style="color: pink"></strong></p>
            <div>
                <div><span><strong>{{ comment.user.username }}</strong></span>&nbsp;<span style="color: gray">{{ comment.created | date:'Y:m:d H:i:s' }}</span></div>
                <br>
                <p>{{ comment.content|safe }}</p>
            </div>
        </div>
    {% endfor %}
    <div class="pagenation" style="text-align: center">
        <div id="pagination" class="page"></div>
    </div>
</div>

3.修改底部js分頁代碼

<script type="text/javascript">
    $(function () {
        $('#pagination').pagination({
           currentPage: {{ page_num }},
            totalPage: {{ total_page }},
            callback:function (current) {
                location.href = '/detail/?id={{ article.id }}&page_size={{ page_size }}&page_num='+current;
            }
        })
    });
</script>

現在就是完成基本使用功能一個完整的**登錄 注冊 退出 發表文章 評論文章 **
后期待完善

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/279307.html

標籤:其他

上一篇:【C語言基礎學習筆記】三、函式(1)

下一篇:程式員吞噬零售業,成也中臺敗也中臺 | 零售十年變遷路

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more