欢迎来到Felix的博客

Do the right things! And talk is cheap,show me your code!

搭建自己的博客(十八):添加热门博客展示

1、变化的部分

2、上代码:

from django.db import models
from django.contrib.auth.models import User
from django.contrib.contenttypes.fields import GenericRelation
from ckeditor_uploader.fields import RichTextUploadingField
from django.contrib.contenttypes.models import ContentType
from read_statistics.models import ReadNumExpandMethod, ReadDetail


# Create your models here.

# 博客分类
class BlogType(models.Model):
    type_name = models.CharField(max_length=15)  # 博客分类名称

    def __str__(self):  # 显示标签名
        return self.type_name


# 博客
class Blog(models.Model, ReadNumExpandMethod):
    title = models.CharField(max_length=50)  # 博客标题
    blog_type = models.ForeignKey(BlogType, on_delete=models.DO_NOTHING)  # 博客分类
    content = RichTextUploadingField()  # 博客内容,使用富文本编辑
    author = models.ForeignKey(User, on_delete=models.DO_NOTHING)  # 博客作者
    read_details = GenericRelation(ReadDetail)  # 关联到阅读表
    created_time = models.DateTimeField(auto_now_add=True)  # 博客创建时间
    last_updated_time = models.DateTimeField(auto_now=True)  # 博客更新事件

    def __str__(self):  # 显示标题名
        return "<Blog:{}>".format(self.title)

    class Meta:
        ordering = ['-created_time']  # 定义排序规则,按照创建时间倒序
models.py
"""
Django settings for myblog project.

Generated by 'django-admin startproject' using Django 2.1.3.

For more information on this file, see
https://docs.djangoproject.com/en/2.1/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.1/ref/settings/
"""

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'ea+kzo_5k^6r7micfg@lar1(rfdc08@b4*+w5d11=0mp1p5ngr'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []

# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'ckeditor',
    'ckeditor_uploader',
    'blog.apps.BlogConfig',  # 将自己创建的app添加到设置中
    'read_statistics.apps.ReadStatisticsConfig',  # 注册阅读统计app

]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'blog.middleware.mymiddleware.My404',  # 添加自己的中间件
]

ROOT_URLCONF = 'myblog.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            os.path.join(BASE_DIR, 'templates'),
        ],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'myblog.wsgi.application'

# Database
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases

DATABASES = {
    # 'default': {
    #     'ENGINE': 'django.db.backends.sqlite3',
    #     'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    # }
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'myblogs',  # 要连接的数据库,连接前需要创建好
        'USER': 'root',  # 连接数据库的用户名
        'PASSWORD': 'felixwang',  # 连接数据库的密码
        'HOST': '127.0.0.1',  # 连接主机,默认本级
        'PORT': 3306  # 端口 默认3306
    }
}

# Password validation
# https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]

# Internationalization
# https://docs.djangoproject.com/en/2.1/topics/i18n/

# LANGUAGE_CODE = 'en-us'
# 语言
LANGUAGE_CODE = 'zh-hans'

# TIME_ZONE = 'UTC'
# 时区
TIME_ZONE = 'Asia/Shanghai'

USE_I18N = True

USE_L10N = True

# 不考虑时区
USE_TZ = False

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.1/howto/static-files/

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

# media
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

# 配置ckeditor
CKEDITOR_UPLOAD_PATH = 'upload/'

# 自定义参数
EACH_PAGE_BLOGS_NUMBER = 7


# 设置数据库缓存
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
        'LOCATION': 'my_read_num_cache_table',
    }
}
settings.py
# -*- coding: utf-8 -*-
# @Time    : 18-11-7 下午4:12
# @Author  : Felix Wang

from django.shortcuts import render_to_response
from django.contrib.contenttypes.models import ContentType
from read_statistics.utils import get_seven_days_read_data, get_x_days_hot_data
from blog.models import Blog


def home(requests):
    blog_content_type = ContentType.objects.get_for_model(Blog)
    dates, read_nums = get_seven_days_read_data(blog_content_type)

    context = {
        'read_nums': read_nums,
        'dates': dates,
        'today_hot_data': get_x_days_hot_data(0),  # 获取今日热门
        'yesterday_hot_data': get_x_days_hot_data(1),  # 获取昨日热门
        'seven_days_hot_data': get_x_days_hot_data(7),  # 获取周热门
        'one_month_hot_data': get_x_days_hot_data(30),  # 获取月热门
    }
    return render_to_response('home.html', context)
views.py
# -*- coding: utf-8 -*-
# @Time    : 18-11-17 下午10:03
# @Author  : Felix Wang
import datetime
from django.contrib.contenttypes.models import ContentType
from django.db.models import Sum
from django.utils import timezone
from django.core.cache import cache
from .models import ReadNum, ReadDetail
from blog.models import Blog


def read_statistics_once_read(requests, obj):
    ct = ContentType.objects.get_for_model(obj)
    key = '{}_{}_read'.format(ct.model, obj.pk)

    # 获取并处理阅读计数
    if not requests.COOKIES.get(key):
        # 总阅读量+1
        readnum, created = ReadNum.objects.get_or_create(content_type=ct, object_id=obj.pk)
        # 处理阅读量
        readnum.read_num += 1
        readnum.save()

        # 当天阅读量+1
        date = timezone.now().date()
        readDetail, created = ReadDetail.objects.get_or_create(content_type=ct, object_id=obj.pk, date=date)
        readDetail.read_num += 1
        readDetail.save()

    return key


def get_seven_days_read_data(content_type):
    today = timezone.now().date()
    dates = []
    read_nums = []
    for i in range(7, 0, -1):  # 统计7天的阅读量
        date = today - datetime.timedelta(days=i)
        dates.append(date.strftime('%m/%d'))  # 将日期格式转成字符串
        read_details = ReadDetail.objects.filter(content_type=content_type, date=date)
        result = read_details.aggregate(read_num_sum=Sum('read_num'))
        read_nums.append(result['read_num_sum'] or 0)  # 如果有阅读量则阅读量否则置0
    return dates, read_nums


# 获取某天的阅读量综合
def get_x_days_hot_data(x_days):
    # 如果缓存有值,取缓存的值
    cache_data = cache.get('cache_data_for_{}_days'.format(x_days))
    if cache_data:
        return cache_data

    today = timezone.now().date()
    if x_days == 0:  # 表示今天
        date_blogs = Blog.objects.filter(read_details__date=today)
    else:  # 不等于0表示往前多少天
        date = today - datetime.timedelta(days=x_days)
        date_blogs = Blog.objects.filter(read_details__date__lt=today, read_details__date__gte=date)

    read_details = date_blogs.values('id', 'title').annotate(read_num_sum=Sum('read_details__read_num')).order_by(
        '-read_num_sum')[:7]  # 分组,求和

    cache.set('cache_data_for_{}_days'.format(x_days), read_details,
              3600 * 3 if x_days else 1800)  # 设置缓存,三小时更新一次,如果是当日阅读量半小时更新一次
    return read_details
utils.py
.home-content {
    font-size: 222%;
    text-align: center;
    margin-top: 4em;
    margin-bottom: 2em;
}

div#container {
    margin: 0 auto;
    height: 20em;
    min-width: 20em;
    max-width: 30em;
}

div.hot-data {
    text-align: center;
    margin-top: 2em;
}

div.hot-data ul {
    list-style-type: none;
}
home.css
{% extends 'base.html' %}
{% load staticfiles %}


{% block header_extends %}
    <link rel="stylesheet" href="{% static 'css/home.css' %}">
    <script src="{% static 'Highcharts-6.2.0/code/highcharts.js' %}"></script>
{% endblock %}

{% block title %}
    我的博客|首页
{% endblock %}

{% block content %}
    <h1 class="home-content">欢迎访问我的博客</h1>
    <!-- 图表容器 DOM -->
    <div id="container"></div>

    <div class="hot-data">
        <!-- 今天24小时内的热门博客 -->
        <h3>今日热门博客</h3>
        <ul>
            {% for hot_data in today_hot_data %}
                <li><a href="{% url 'blog_detail' hot_data.id %}">{{ hot_data.title }}</a>
                    ({{ hot_data.read_num_sum }})
                </li>
            {% empty %}
                <li>今日暂时没有热门博客</li>
            {% endfor %}
        </ul>
    </div>
    <div class="hot-data">
        <!-- 昨日的热门博客 -->
        <h3>昨日热门博客</h3>
        <ul>
            {% for hot_data in yesterday_hot_data %}
                <li><a href="{% url 'blog_detail' hot_data.id %}">{{ hot_data.title }}</a>
                    ({{ hot_data.read_num_sum }})
                </li>
            {% empty %}
                <li>昨日暂时没有热门博客</li>
            {% endfor %}
        </ul>
    </div>
    <div class="hot-data">
        <!-- 七日内的热门博客 -->
        <h3>单周热门博客</h3>
        <ul>
            {% for hot_data in seven_days_hot_data %}
                <li><a href="{% url 'blog_detail' hot_data.id %}">{{ hot_data.title }}</a>
                    ({{ hot_data.read_num_sum }})
                </li>
            {% empty %}
                <li>单周暂时没有热门博客</li>
            {% endfor %}
        </ul>
    </div>
    <div class="hot-data">
        <!-- 七日内的热门博客 -->
        <h3>单月热门博客</h3>
        <ul>
            {% for hot_data in one_month_hot_data %}
                <li><a href="{% url 'blog_detail' hot_data.id %}">{{ hot_data.title }}</a>
                    ({{ hot_data.read_num_sum }})
                </li>
            {% empty %}
                <li>单月暂时没有热门博客</li>
            {% endfor %}
        </ul>
    </div>
{% endblock %}

{% block js %}
    {# 表格操作 #}
    {#    <!-- 引入 highcharts.js -->#}
    <script>
        // 图表配置
        let options = {
            chart: {
                type: 'line' //指定图表的类型,默认是折线图(line)
            },
            title: {
                text: null // 标题
            },
            xAxis: {
                categories: {{ dates|safe }}, // x 轴分类
                tickmarkPlacement: 'on',
                title: {
                    text: '前7日阅读量'
                }
            },
            yAxis: {
                title: {
                    text: null // y 轴标题
                },
                labels: {
                    enabled: false
                },
                gridLineDashStyle: 'Dash',
            },
            plotOptions: {
                line: {
                    dataLabels: {
                        enabled: true
                    }
                }
            },
            credits: {
                enabled: false // 禁用版权信息
            },
            series: [{ // 数据列
                name: '阅读量', // 数据列名
                data: {{ read_nums }},// 数据
                showInLegend: false, // 设置为 false 即为不显示在图例中
            },]
        };
        // 图表初始化函数
        let chart = Highcharts.chart('container', options);
    </script>


    {# 将首页这个按钮设置激活状态 #}
    <script>
        $(".nav-home").addClass("active").siblings().removeClass("active");
    </script>
{% endblock %}
home.html

3、解释

(1)、使用了django的数据库缓存功能,不用每次都从数据库中读取数据,增加访问速度。具体缓存见官网:官网地址

(2)、使用contenttype中的GenericRelation来关联外表建立反向关系:具体件官网:官网地址

posted @ 2018-11-19 11:26  寂静的天空  阅读(171)  评论(0编辑  收藏  举报
个人感悟: 一个人最好的镜子就是自己,你眼中的你和别人眼中的你,不是一回事。有人夸你,别信;有人骂你,别听。一根稻草,扔街上就是垃圾;捆上白菜就是白菜价;捆上大闸蟹就是大闸蟹的价。 一个人,不狂是没有出息的,但一直狂,肯定是没有出息的。雨打残花风卷流云,剑影刀光闪过后,你满脸冷酷的站在珠峰顶端,傲视苍生无比英武,此时我问你:你怎么下去? 改变自己就是改变自己的心态,该沉的时候沉下去,该浮的时候浮上来;不争名夺利,不投机取巧,不尔虞我诈;少说、多听、多行动。人每所谓穷通寿夭为命所系,岂不知造物之报施,全视人之自取。 座佑铭:每一个不曾起舞的日子,都是对生命的辜负。