用Django全棧開發——24. CMS的真正的Dashboard開發

大家好,這是皮爺給大家帶來的最新的學習Python能幹啥?之Django教程,從零開始,到最後成功部署上線的項目。這一節,用AdminLTE編寫Dashboard。

Peekpa.com的官方地址:http://peekpa.com

皮爺的每一篇文章,都配置相對應的代碼。這篇文章的代碼Tag是Post_024

在這裏插入圖片描述

目前,當我們登錄我們的CMS界面,我們看到的是這個樣子:

在這裏插入圖片描述

很醜,不美觀。所以這幾節,我們將要開發一款真正意義上的CMS Dashboard。具體開發完成長這個樣子:

在這裏插入圖片描述

我們看到,具體分爲這麼幾個部分:

  • 用戶訪問
  • 文章統計
  • 若是想加,還可以加其他的功能

這些東西我們其實都是可以做出來的,接下來的幾節課,我們就來講講每一個部分都是怎麼實現的。

用戶訪問統計

首先是用戶訪問統計,我們要統計每一個用戶的訪問請求,同時要記錄每天網站訪問人數,那麼我們就需要創建模型了。

這個模型應該是屬於全站使用,所以我們就在basefucntion/models.py下面的創建:

class UserIP(models.Model):
    ip_address = models.CharField(max_length=30)
    ip_location = models.CharField(max_length=30)
    end_point = models.CharField(default='/', max_length=30)
    day = models.DateField(default=timezone.now)


# 網站總訪問次數
class VisitNumber(models.Model):
    count = models.IntegerField(default=0)  # 網站訪問總次數


# 單日訪問量統計
class DayNumber(models.Model):
    day = models.DateField(default=timezone.now)
    count = models.IntegerField(default=0)  # 網站訪問總次數

接着,我們在base應用下,創建一個tracking_view.py,裏面要放我們的更新方法,即最終網站訪問數量的方法:

def peekpa_tracking(func):
    def wrapper(request, *args, **kwargs):
        tacking_info(request)
        return func(request, *args, **kwargs)
    return wrapper


def tacking_info(request):
    update_visit_number()
    update_user_ip(request)
    update_day_visit_number()


def update_visit_number():
    count_nums = VisitNumber.objects.filter(id=1)
    if count_nums:
        count_nums = count_nums[0]
        count_nums.count = F('count') + 1
    else:
        count_nums = VisitNumber()
        count_nums.count = 1
    count_nums.save()


def update_user_ip(request):
    if 'HTTP_X_FORWARDED_FOR' in request.META:  # 獲取 ip
        client_ip = request.META['HTTP_X_FORWARDED_FOR']
        client_ip = client_ip.split(",")[0]  # 所以這裏是真實的 ip
    else:
        client_ip = request.META['REMOTE_ADDR']  # 這裏獲得代理 ip

    UserIP().objects.create(ip=client_ip, end_point=request.path, ip_address="TBA", day=timezone.now().date())


def update_day_visit_number():
    date = timezone.now().date()
    today = DayNumber.objects.filter(day=date)
    if today:
        temp = today[0]
        temp.count += 1
    else:
        temp = DayNumber()
        temp.dayTime = date
        temp.count = 1
    temp.save()

首先,這裏我們要通過裝飾器的方式來實現,當request進來的時候,我們做了三步處理:

  1. 更新總網站訪問量;
  2. 更新個人訪問記錄;
  3. 更新每天訪問數量。

然後再將request交給傳入的func去做接下來該做的事兒。

我們這個時候可以在文章的detail信息做一下驗證:

@peekpa_tracking
def detail(request, time_id):

這個時候,我們再去打開之前發佈的任何一個文章:

在這裏插入圖片描述

訪問成功之後,我們看數據庫裏面:

在這裏插入圖片描述

就能看到我們跟蹤的信息已經存到了數據庫中了。說明成功了。接下來我們就要來做Dashboard的統計工作。

頁面統計的顯示

我們在最開始的預覽圖裏面看到,我們的Dashboard頁面是有網站點擊相關統計的內容顯示的,接下來,我們就將這些東西顯示出來。

如果我們要將數據顯示到Dashboard上,那麼就應該在Dashboard的home視圖函數中,先提前將數據準備好,然後再傳遞給頁面即可。

所以,我們的主要邏輯就應該在cms_dashboard(request)視圖方法中寫。

@peekpa_login_required
def cms_dashboard(request):
    context = {}
    context.update(get_dashboard_top_data())
    context.update(get_dashboard_visitor_ip_table())
    return render(request, 'cms/home/home.html', context=context)
    
def get_dashboard_top_data():
    post_num = Post.objects.all().count()
    day_visit_ip_set = set()
    day_visit_ip_list = UserIP.objects.filter(day=timezone.now().date())
    if day_visit_ip_list:
        for user_ip_item in day_visit_ip_list:
            if user_ip_item.ip_address not in day_visit_ip_set:
                day_visit_ip_set.add(user_ip_item.ip_address)
    day_visit_ip_num = len(day_visit_ip_set)
    day_visit_num = DayNumber.objects.filter(day=timezone.now().date())[0].count
    total_visit_num = VisitNumber.objects.filter(id=1)[0].count
    context = {
        "post_num": post_num,
        "day_visit_ip_num": day_visit_ip_num,
        "day_visit_num": day_visit_num,
        "total_visit_num": total_visit_num
    }
    return context
    
def get_dashboard_visitor_ip_table():
    visitor_data = UserIP.objects.filter(day=timezone.now().date())
    if len(visitor_data):
        visitor_data = visitor_data[:7]
    context = {
        'visitor_data_list': visitor_data,
    }
    return context

可以看到,我們這裏使用兩個方法get_dashboard_top_dataget_dashboard_visitor_ip_table來分別獲取頂部四個小模塊的數據還有Visitor IP Table的數據。我們拿到數據之後,就展示到前端:

在這裏插入圖片描述

接下來,我們就要完善Chart的內容了。

圖表顯示

這裏可以看到,我們的Dashboard用到了一個非常美麗的表單,這個是Chart.js,是一個非常好用的圖表庫。官網地址:

https://www.chartjs.org/

因爲我們首先需要將值從後臺傳給前端,然後這個圖標是個js文檔,所以,我們應該在html代碼中,編寫一些js代碼。我們的home_visit_chat.html代碼就應該變成下面這樣:

<script>
    $(document).ready(function () {
        var ticksStyle = {
            fontColor: '#495057',
            fontStyle: 'bold'
        }
        var $visitorsChart = $('#visitors-chart')
        var visitorsChart2 = new Chart($visitorsChart, {
            data: {
                labels: {{ date_time_list }},
                datasets: [{
                    type: 'line',
                    data: {{ week_data_list }},
                    backgroundColor: 'transparent',
                    borderColor: '#007bff',
                    pointBorderColor: '#007bff',
                    pointBackgroundColor: '#007bff',
                    fill: true
                }]
            },
            options: {
                maintainAspectRatio: false,
                tooltips: {
                    mode: 'index',
                    intersect: true
                },
                hover: {
                    mode: 'index',
                    intersect: true
                },
                legend: {
                    display: false
                },
                scales: {
                    yAxes: [{
                        // display: false,
                        gridLines: {
                            display: true,
                            lineWidth: '4px',
                            color: 'rgba(0, 0, 0, .2)',
                            zeroLineColor: 'transparent'
                        },
                        ticks: $.extend({
                            beginAtZero: false,
                            suggestedMax: {{ suggested_max }}
                        }, ticksStyle)
                    }],
                    xAxes: [{
                        display: true,
                        gridLines: {
                            display: false
                        },
                        ticks: ticksStyle
                    }]
                }
            }
        })
    })
</script>

可以看到,這裏面有好多數據都是需要後盾傳送給前端,所以,我們將這些數據都讀取出來,添加給cms_dashboard()視圖函數即可:

def get_dashboard_visitor_chart():
    days_list = []
    visit_list = []
    max_num = 0
    week_total_num = 0
    for index in range(6, -1, -1):
        day, format_date = get_before_date(index)
        days_list.append(int(day))
        day_visit_num = 0
        daynumber_item = DayNumber.objects.filter(day=format_date)
        if daynumber_item:
            day_visit_num = daynumber_item[0].count
        visit_list.append(day_visit_num)
        week_total_num += day_visit_num
        max_num = day_visit_num if day_visit_num > max_num else max_num
    context = {
        'visit_week_total_number': day_visit_num,
        'date_time_list': days_list,
        'week_data_list': visit_list,
        'suggested_max': max_num
    }
    return context

這樣,我們的圖表就製作完成了。看一下效果:

在這裏插入圖片描述

最後一個,就是我們的文章瀏覽情況了。

文章閱讀情況

文章閱讀,我們既然已經知道了每一天的訪問量,而且他們訪問的地址我們也知道,所以,文章的訪問我們就能夠很輕易的做出來。

同樣,還是在cms_dashboard視圖函數裏面添加數據即可。

def get_dashboard_post_view_table():
    visitor_day_data = UserIP.objects.filter(day=timezone.now().date(), end_point__contains='/detail/')
    post_map = {}
    post_view_table_list = []
    if visitor_day_data:
        for item in visitor_day_data:
            post_id = item.end_point.split('/')[2]
            if post_id in post_map:
                post_map[post_id] += 1
            else:
                post_map[post_id] = 1
    if post_map:
        for key in post_map:
            key = key
            post_item = Post.objects.filter(time_id=key)
            if post_item:
                post_item[0].inscrease = post_map[key]
                post_view_table_list.append(post_item[0])
    if post_view_table_list:
        post_view_table_list.sort(key=lambda x: x.inscrease, reverse=True)
    context = {
        'post_view_table_list': post_view_table_list,
    }
    return context

這裏我們操作比較繁瑣,主要是經歷了這麼幾步驟:

  1. 讀取當天文章點擊情況;
  2. 獲取出來文章列表,並將這些數據存儲在一個字典中,key是id,name是文章個數;
  3. 從Post裏面撈出來這些文章,然後排序返回給前端展示。

最後,我們來看一下整體的效果:

在這裏插入圖片描述

看到整個頁面分爲四個板塊,然後左側還做了Monitor,裏面分別對應的UserIP管理還有文章閱讀詳情。其實Dashboard的編寫,還能更加多變靈活,關鍵還是要根據自己的實際需求來完成。

文章統計

最後我們再來加一章節,來說一下文章統計的事兒。

細心讀文章的同學肯定發現了,我們之前的文章統計的數值是不對的,那麼想要實現文章統計,我們有這麼幾個思路:

  1. 每一次請求文章的時候,我們從數據庫讀取文章對象,然後把Post裏面的read_num喜加一,再存進去;
  2. 稍微設置一下緩存,通過的F表達式,來做懶加載處理;
  3. 將文章的“喜加一”功能,放到MemoryCache或者Reddis裏面做處理。

這幾種思路,他們的特點分別是:

  1. 實現簡單,但是耗費數據庫開銷嚴重;
  2. 實現一般,可緩解一定的數據庫開銷,但是當網站規模增大的時候,高併發會出問題;
  3. 實現有難度,但是可以扛得住高併發的問題。

既然,我們的peekpa.com也不是什麼一般的小網站,這裏我就選擇第二種實現方式給大家看看。

這裏來簡單說一下思路:

當用戶訪問文章詳情的時候,會針對用戶生成一個uid,這個uid在不同的文章裏面,緩存1分鐘。1分鐘之內用戶重複訪問,不算訪問量。然後懶加載更新數據庫。

這回,我們就要用到了middleware了。首先在Post應用下,創建一個middleware python包,然後在裏面實現一個叫做user_idmiddleware:

在這裏插入圖片描述

class UserIDMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        uid = self.generate_uid(request)
        request.uid = uid
        response = self.get_response(request)
        response.set_cookie(USER_KEY, uid, max_age=TEN_YEARS, httponly=True)
        return response

    def generate_uid(self, request):
        try:
            uid = request.COOKIES[USER_KEY]
        except KeyError:
            uid = uuid.uuid4().hex
        return uid

然後,我們要在settings.py文件裏面的MIDDLEWARE中,在第一個位置添加這個middleware:

MIDDLEWARE = [
    'apps.poster.middleware.user_id.UserIDMiddleware',
]

最後,我們就在文章詳情請求頁裏面,來加入我們上面所講的邏輯:

@peekpa_tracking
def detail(request, time_id):
    handle_visited(request, time_id)
    return render(request, 'post/detail.html', context=context)
    
def handle_visited(request, time_id):
    increase_post_view = True
    uid = request.uid
    pv_key = 'pv:%s:%s' % (uid, request.path)
    if not cache.get(pv_key):
        increase_post_view = True
        cache.set(pv_key, 1, 2*60)

    if increase_post_view:
        Post.objects.filter(time_id=time_id).update(read_num=F('read_num') + 1)

這樣,我們就完美的實現了頁面訪問喜加一的功能。

技術總結

最後總結一下,

編寫CMS的Dashboard:

  1. 用戶統計,需要創建模型來存儲管理,我們這裏創建了UserIP,VisitNumber還有DayVisitNumberr,分別管理單次點擊,整個點擊還有每天的點擊量;
  2. Dashboard還是要拆開分結構,頂部四個小方塊,然後拆成四個表格;
  3. cms_dashboard()這個視圖函數裏面添加數據返回前端;
  4. 添加數據,按照模塊一步一步的處理;
  5. Chartjs,這裏我們在html模板裏面寫了JavaScript代碼,同樣也是;
  6. 文章排序,則是通過每日訪問裏面的end_point值獲取到文章id,然後再去Post裏面根據ID找文章,最後展示出來;
  7. 頁面喜加一,則是用到了MiddleWare來給request添加一個uid,然後當request到達視圖函數的時候,通過uid是否在緩存裏來判斷是否需要喜加一;
  8. 完畢

獲取代碼的唯一途徑:關注『皮爺擼碼』,回覆『代碼』即可獲得。

長按下圖二維碼關注,如文章對你有啓發,歡迎在看與轉發。

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章