django搭建博客十博客首页

该系列文章

django搭建博客一前言
django搭建博客二之初始化工程
django搭建博客三初始化应用
django搭建博客四页面布局和自定义异常视图
django搭建博客五系统模型建立与注册
django搭建博客六邮件模板和邮件工具类
django搭建博客七用户注册
django搭建博客八用户登录
django搭建博客九用户密码重置
django搭建博客十博客首页

文章应用模型建立

编辑\myblog\article\models.py

内容如下:

import logging
import os
from django.db import models
from django.db.models import F
from django.urls import reverse

from django.utils.safestring import mark_safe
from django.conf import settings
from django.utils.html import strip_tags
from django.contrib.auth import get_user_model
from mdeditor.fields import MDTextField

from system.models import BaseModel

logger = logging.getLogger('django')
User = get_user_model()


class Category(BaseModel):
    name = models.CharField(verbose_name='类别名称', max_length=20, unique=True)
    slug = models.SlugField(verbose_name="分类slug", unique=True)
    level = models.IntegerField(verbose_name="分类层级", default=1)
    parent = models.ForeignKey('self', verbose_name='父级导航', related_name='parent_category', null=True, blank=True,
                               on_delete=models.CASCADE, help_text="父级导航可以为空",
                               limit_choices_to={"parent__isnull": True})
    show = models.BooleanField(verbose_name="是否显示", default=True)
    icon = models.CharField(verbose_name="导航图标", max_length=30, help_text="目前导航图标仅支持font-awesome", null=True,
                            blank=True)
    order = models.IntegerField(verbose_name="序号", help_text="序号越大的越靠前", default=1)

    class Meta:
        db_table = "article_category"
        verbose_name = '文章分类'
        verbose_name_plural = verbose_name
        ordering = ['-order']

    def __str__(self):
        return self.name

    def save(self, *args, **kwargs):
        if self.parent:
            self.level = self.parent.level + 1
        super(Category, self).save(*args, **kwargs)


class Tag(BaseModel):
    name = models.CharField(verbose_name='标签名', max_length=20, unique=True)
    slug = models.SlugField(verbose_name="标签slug", unique=True)
    show = models.BooleanField(verbose_name="是否显示", default=True)

    class Meta:
        db_table = "article_tag"
        verbose_name = '标签'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name

    def get_article_count(self):
        return self.tag_article.count()


class Article(BaseModel):
    slug = models.SlugField(verbose_name="文章slug", unique=True)
    title = models.CharField(max_length=500, verbose_name='文章标题')
    summary = models.TextField(verbose_name='文章摘要', help_text="系统自动生成摘要", null=True, blank=True)
    content = MDTextField(verbose_name="内容", null=True, blank=True)
    cover = models.ImageField(verbose_name="文章封面", null=True, blank=True)
    views = models.IntegerField(verbose_name='浏览数', default=0, editable=False)
    loves = models.IntegerField(verbose_name='点赞数', default=0, editable=False)
    category = models.ForeignKey(Category, verbose_name='分类', on_delete=models.DO_NOTHING,
                                 related_name="category_article", limit_choices_to={'level__gt': 1},
                                 db_constraint=False, null=True, blank=True)
    tags = models.ManyToManyField(Tag, verbose_name='标签', related_name='tag_article', blank=True,
                                  db_table="sys_article_tag")
    show = models.BooleanField(verbose_name="是否显示", default=True)
    can_comment = models.BooleanField(verbose_name="允许评论", default=True)
    auto_summary = models.BooleanField(verbose_name="自动生成摘要", help_text="如果不勾选,则不会自动生成摘要信息", default=True)
    place_top = models.BooleanField(verbose_name="是否置顶", default=False)

    class Meta:
        db_table = "article"
        verbose_name = "文章"
        verbose_name_plural = verbose_name
        ordering = ["-place_top", "-create_time"]

    def __str__(self):
        return self.title[:20]

    def get_previous_article(self):
        previous_article = Article.objects.only("id", "slug").filter(id__lt=self.id).last()
        return previous_article

    def get_next_article(self):
        next_article = Article.objects.only("id", "slug").filter(id__gt=self.id).last()
        return next_article

    def increase_views(self):
        Article.objects.filter(id=self.id).update(views=F('views') + 1)

    def increase_loves(self):
        Article.objects.filter(id=self.id).update(loves=F('loves') + 1)

    def get_tag_list(self):
        return self.tags.all()

    def show_image(self):
        url = os.path.join(settings.MEDIA_URL, self.cover.url)
        return mark_safe("<img src='{url}' width=75></a>".format(url=url))

    show_image.short_description = "封面"

    def save(self, *args, **kwargs):
        if self.auto_summary:
            summary = strip_tags(self.content).replace(" ", "").replace("\n", "").replace("&nbsp;", " ")
            self.summary = summary[:200]
        super(Article, self).save(*args, **kwargs)

admin后台配置

新增\myblog\article\admin.py

内容如下

from django.contrib.admin import register

from article.models import Article, Tag, Category
from system.admin import BaseModelAdmin


@register(Category)
class CategoryModelAdmin(BaseModelAdmin):
    list_display = ['id', 'name', 'slug', 'show', 'parent', 'order']
    list_filter = ["show", "level"]
    search_fields = ['name', 'slug']
    list_editable = ['name', 'slug', 'show', 'order']


@register(Tag)
class TagModelAdmin(BaseModelAdmin):
    list_display = ['id', 'name', 'slug', 'show']
    search_fields = ['name', 'slug']
    list_editable = ['name', 'show']


@register(Article)
class ArticleModelAdmin(BaseModelAdmin):
    actions = ["batch_show_action", "batch_hide_action"]
    list_display = ['title', 'category', 'views', 'loves', 'can_comment', 'show', 'place_top','show_image']
    list_filter = ['title', 'category', 'tags', 'show']
    search_fields = ['title', 'content']
    list_editable = ['show', 'place_top', 'can_comment']
    list_per_page = 10
    list_order_field = ["views", "loves"]

    filter_horizontal = ("tags",)

    preserve_filters = ("category",)

    def batch_show_action(self, queryset):
        for obj in queryset:
            obj.show = True
            obj.save()
        return None

    batch_show_action.short_description = "批量显示所选的"

    def batch_hide_action(self, queryset):
        for obj in queryset:
            obj.show = False
            obj.save()
        return None

    batch_hide_action.short_description = "批量隐藏所选的"

    def queryset(self):
        """控制用户只能查看自己的文章(除了超级管理员)"""
        qs = super().queryset()
        if not self.request.user.is_superuser:
            qs = qs.filter(creator=self.request.user)
        return qs

meditor路由配置

编辑\myblog\myblog\urls.py

由于在Article模型里面用到了富文本编辑器应用django-meditor,所以这里需要在urlpatterns列表里面添加以下内容

path(r'mdeditor/', include('mdeditor.urls')),

后台效果图

文章分类效果图

在这里插入图片描述

文章标签效果图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ibnEjIsX-1656230280008)(assets/1656127970807.png)]

文章效果图

在这里插入图片描述

文章编辑效果图

在这里插入图片描述

自定义文章模板标签

新增\myblog\article\templatetags模块文件夹

新增\myblog\article\templatetags\article_tags.py文件

添加如下内容

from django import template
from article.models import Article, Tag
import logging

logger = logging.getLogger('django')
register = template.Library()


@register.simple_tag
def get_article_date_list():
    article_date_list = Article.objects.only("id", "slug", "title").filter(show=True).datetimes('create_time', 'month',
                                                                                                order='DESC')
    return article_date_list


@register.simple_tag
def get_article_by_date(year, month):
    article_list = Article.objects.only("id", "slug", "title").filter(show=True, create_time__year=year,
                                                                      create_time__month=month)
    return article_list


@register.simple_tag
def get_tag_list():
    """返回标签列表"""
    from django.db.models import Count
    tag_list = Tag.objects.annotate(article_nums=Count('tag_article')).filter(article_nums__gt=0).order_by('name')
    return tag_list


@register.simple_tag
def get_new_article_list():
    return Article.objects.only("id", "slug", "title").filter(show=True).order_by('-create_time')[:5]


@register.simple_tag
def get_hot_article_list():
    return Article.objects.only("id", "slug", "title").filter(show=True).order_by('-views')[:5]

自定义模板上下文

编辑\myblog\system\template_context_processors.py

import logging
from system.models import User

logger = logging.getLogger("django")


def template_context(request):
    superuser = User.objects.filter(is_superuser=True).first()
    context = {"superuser": superuser}
    return context

编辑\myblog\myblog\settings.py,修改TEMPLATES注册自己定义的上下文处理器

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',
                # 把media全局注册进template模板中
                'django.template.context_processors.media',
                'system.template_context_processors.template_context',
            ],
        },
    },
]

首页控件

文章分页控件

编写\myblog\templates\layout\pagination.html

添加以下内容

{% load  system_tags %}
{% if is_paginated %}
    <nav aria-label="Page navigation example">
        <ul class="pagination justify-content-center my-3 pagination-lg">
            {% if page_obj.has_previous %}
                <li class="page-item">
                    <a href="?page={{ page_obj.previous_page_number }}" aria-label="Previous">
                        <span aria-hidden="true">&laquo;</span>
                    </a>
                </li>
            {% endif %}
            {% get_elided_page_range paginator page_obj.number as page_list %}
            {% for page in page_list %}
                {% ifequal page page_obj.number %}
                    <li class="page-item active"><span>{{ page }}</span></li>
                {% else %}
                    {% ifequal page page_obj.paginator.ELLIPSIS %}
                        <li class="page-item"><span>{{ page }}</span></li>
                    {% else %}
                        <li class="page-item"><a href="?page={{ page }}&q={{ query }}">{{ page }}</a></li>
                    {% endifequal %}
                {% endifequal %}
            {% endfor %}

            {% if page_obj.has_next %}
                <li class="page-item">
                    <a href="?page={{ page_obj.next_page_number }}" aria-label="Next">
                        <span aria-hidden="true">&raquo;</span>
                    </a>
                </li>
            {% endif %}
        </ul>
    </nav>
{% endif %}

效果图

在这里插入图片描述

文章列表控件

编写\myblog\templates\article\article-query-list.html

添加以下内容

{% for article in articles %}
    <article class="d-block bg-white hvr-float">

        <div class="media d-block">
            <div class="article-thumb">
                <a href="#"><img src="{{ MEDIA_URL }}{{ article.cover }}" class="mr-3"></a>
            </div>
            <div class="media-body py-3 pl-1 overflow-hidden">
                <div class="index-article-header">
                    <a href="#" class="label ">{{ article.category.name }}<i
                            class="position-absolute c-arrow"></i></a>
                    <h2 class="d-inline article-title"><a href="#"
                                                          class="text-dark">{{ article.title }}</a></h2>
                    {% if article.place_top %}<span class="new-icon">置顶</span>{% endif %}
                </div>

                <div class="article-summary">
                    <p class="text-wrap text-secondary">
                        {{ article.summary|truncatechars:170 }}{% if article.summary|length > 150 %}
                            ...{% endif %}</p>
                </div>
            </div>

            <div class="article-relative-meta">

        <span class="float-left">
            <a href="#"><i class="fa fa-calendar c4"></i>{{ article.create_time|date:'Y年m月d日' }}</a>
            <a href="#"><i
                    class="fa fa-comments c5"></i>{{ 0 }}评论</a>
            <a href="#"><i class="fa fa-eye c6"></i>{{ article.views|default:0 }}阅读</a>
        </span>
                <span class="visible-xs float-left">
            <a href="#"><i class="fa fa-heart c7"></i>{{ article.loves|default:0 }}喜欢</a>
            <a {% if article.creator.link %}href="{{ article.creator.link }}"{% endif %}><i
                    class="fa fa-user c8"></i>{% firstof article.creator.nickname article.creator.username %}</a>
        </span>
                <span class="float-right">
            <a class="mr-0" href="#" title="阅读全文">阅读全文<i class="fa fa-chevron-circle-right"></i></a>
        </span>
            </div>
        </div>
    </article>
{% empty %}
    <div class="d-block bg-white mb-3 text-center"
         style="width: 100%;font-size: 18px;font-weight: bold;padding-top: 15%;height: 300px;">暂时还没有发布的文章!
    </div>
{% endfor %}

效果图

在这里插入图片描述

首页侧边栏控件

编写\myblog\templates\layout\aside\page_aside.html

添加以下内容

<aside class="col-lg-4 col-xl-4 hidden-xs hidden-sm" id="aside">
    <div class="sidebar" id="sidebar">
        {% include 'layout/aside/sentence.html' %}
        {% include 'layout/aside/aboutme.html' %}
        {% include 'layout/aside/recommendation-article.html' %}
        {% include 'layout/aside/article-tag.html' %}
    </div>
</aside>

侧边栏每日一句控件

编写\myblog\templates\layout\aside\sentence.html

添加以下内容

{% load system_tags %}
<aside class="mb-2">
    <div class="sentence">
        <strong>随机一句</strong>
        <h2>{% now "Y年m月d日" %}</h2>
        <p>己所不欲,勿施于人。</p>
    </div>
</aside>

效果图

在这里插入图片描述

侧边栏关于我控件

编写\myblog\templates\layout\aside\aboutme.html

添加以下内容

{% load static %}
<aside class="card about mb-2">
    <div class="card-bg"></div>
    <div class="card-body text-center p-0">
        {% if request.user.is_authenticated %}
            <img class="xwcms" src="{{ MEDIA_URL }}{{ request.user.avatar }}"/>
            <h5 class="card-title text-dark mt-2">{% firstof request.user.nickname request.user.username %}</h5></a>
            <p class="card-text mb-2"
               style="font-size: 18px;font-family: STXingkai;">{{ request.user.introduction|default:'' }}</p>
            <p>
                <a href="#" class="text-reset "><i class="fa fa-user-circle"></i>修改资料</a>
                <a href="#" class="text-reset "><i class="fa fa-key"></i>修改密码</a>
                <a href="{% url 'sys:logout' %}" class="text-reset "><i class="fa fa-sign-out"></i>退出登录</a>
            </p>
        {% else %}
            <a href="#"><img class="xwcms"
                                                            src="{{ MEDIA_URL }}{{ superuser.avatar }}"></a>
            <a href="#" class=""><h5
                    class="card-title text-dark mt-2 superuser">{% firstof superuser.nickname superuser.username %}</h5>
            </a>
            <p class="card-text mb-2"
               style="font-size: 18px;font-family: STXingkai;">{{ superuser.introduction|default:'' }}</p>
            <div class="contact-me text-center">
                <a id="link_weixin" href="javascript:;" rel="nofollow" data-toggle="popover" data-trigger="hover click"
                   data-html="true" data-title="公众号"
                   data-content="<img src='{% static '/images/weixin_link.jpg' %}' width='150px' height='150px'>"><i
                        class="fa fa-weixin" aria-hidden="true"></i></a>
                <a target="_blank" href="https://github.com/tongyuebin" rel="nofollow"><i class="fa fa-github"
                                                                                          aria-hidden="true"></i></a>
                <a target="_blank" href="http://mail.qq.com/cgi-bin/qm_share?t=qm_mailme&email={{ superuser.email }}"
                   rel="nofollow"><i class="fa fa-envelope-o" aria-hidden="true"></i></a>
                <a id="link_qq" href="javascript:;" rel="nofollow" data-toggle="popover" data-title="博主QQ"
                   data-trigger="hover click" data-html="true"
                   data-content="<img src='{% static '/images/qq_link.jpg' %}' width='150px' height='150px'>"><i
                        class="fa fa-qq" aria-hidden="true"></i></a>
            </div>
            </div>
            <div class="text-center" style="border-top: 1px solid #ddd;">
                <a id="bookmark" class="bookmark button--primary button--animated" href="javascript:;" title="加入书签"
                   target="_self"><i class="fa fa-bookmark" aria-hidden="true"></i><span class="pl-2">加入书签</span></a>
            </div>
        {% endif %}
</aside>

未登录效果图

在这里插入图片描述

登陆后效果图

在这里插入图片描述

侧边栏文章推荐控件

编写\myblog\templates\layout\aside\recommendation-article.html

添加以下内容

{% load article_tags %}
{% get_new_article_list as newArticles %}
{% get_hot_article_list as hotArticles %}
{% if newArticles or hotArticles %}
    <aside class="mb-2 py-2 pl-3 recommendation tab-6">
        <ul class="nav nav-tabs" role="tablist">
            {% if newArticles %}
                <li class="nav-item">
                    <a class="nav-link border-0 text-center text-dark active" data-toggle="tab" href="#new" role="tab"
                       aria-controls="new">最新文章</a>
                </li>
            {% endif %}
            {% if hotArticles %}
                <li class="nav-item">
                    <a class="nav-link border-0 text-center text-dark" data-toggle="tab" href="#hot" role="tab"
                       aria-controls="hot">最热文章</a>
                </li>
            {% endif %}
        </ul>
        <div class="tab-content">
            <div class="tab-pane active" id="new" role="tabpanel">
                <ul class="list-group list-group-flush">
                    {% for newArticle in newArticles %}
                        <a class="pl-0 list-group-item text-truncate text-dark " title="{{ newArticle.title }}"
                           href="#">
                            <i class="fa  fa-book"></i>
                            {{ newArticle.title }}
                        </a>
                    {% endfor %}
                </ul>
            </div>
            <div class="tab-pane" id="hot" role="tabpanel">
                <ul class="list-group list-group-flush">
                    {% for hotArticle in hotArticles %}
                        <a class="pl-0 list-group-item text-truncate text-dark " title="{{ hotArticle.title }}"
                           href="#">
                            <i class="fa  fa-book"></i>
                            {{ hotArticle.title }}
                        </a>
                    {% endfor %}
                </ul>
            </div>
        </div>

    </aside>
{% endif %}

效果图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vusCzGWC-1656230280020)(assets/1656228326389.png)]

侧边栏文章标签控件

新增\myblog\templates\layout\aside\article-tag.html

内容如下

{% load  system_tags %}
{% load  article_tags %}
{% get_tag_list as tags %}
{% if tags %}
    <aside class="mb-2 py-2 pl-3 aside-article-tag tab-6">
        <ul class="nav nav-tabs" role="tablist">
            <li class="nav-item">
                <a class="nav-link border-0 text-center text-dark active" data-toggle="tab" href="#aside-article-tag"
                   role="tab" aria-controls="aside-article-tag">标签云</a>
            </li>
        </ul>
        <div class="tab-content pt-2">
            <div class="tab-pane active" id="aside-article-tag" role="tabpanel">
                <!--标签云-->
                <div class="tags btn-group-sm d-inline-block">
                    {% for tag in tags %}
                        <a class="btn btn-sm border rounded-pill" href="{% url 'article:tag' tag.slug %}"
                           data-toggle="tooltip" title="该标签下有{{ tag.article_nums }}篇文章">{{ tag.name }}</a>
                    {% endfor %}
                </div>
            </div>
        </div>

    </aside>
{% endif %}

效果图

在这里插入图片描述

首页模板

新增\myblog\templates\index.html

{% extends 'base.html' %}
{% load static %}
{% block title %}
    {{ title|default:'首页' }}
{% endblock %}
{% block main %}
    <div class="container">
        <div class="row">
            <section class="col-sm-12 col-md-12 col-lg-8 index" id="main">

                {% include 'article/article-query-list.html' %}

                {% include 'layout/pagination.html' %}

            </section>
            {% include 'layout/page_aside.html' %}

        </div>


    </div>
{% endblock %}

首页视图

编写\myblog\article\views.py

添加以下内容

from django.http import Http404
import logging

from django.views.generic import ListView

from article.models import Article, Tag, Category

logger = logging.getLogger('django')


class ArticleListView(ListView):
    template_name = 'index.html'
    context_object_name = 'articles'
    paginate_by = 10
    page_kwarg = 'page'
    http_method_names = ['get']
    model = Article

    queryset = Article.objects.only("id",
                                    "slug",
                                    "title",
                                    "cover",
                                    "summary",
                                    "category__name",
                                    "category_id",
                                    "place_top",
                                    "create_time",
                                    "views",
                                    "loves",
                                    "creator__username")

    def get_queryset(self):
        qs = super(ArticleListView, self).get_queryset()
        qs = qs.filter(**self.kwargs)
        if not self.request.user.is_superuser:
            qs = qs.filter(show=True)
        return qs

    def get_context_data(self, **kwargs):
        title = "首页"
        try:
            if 'tags__slug' in self.kwargs.keys():  # 按标签分类文章
                slug = self.kwargs.get('tags__slug')
                tag = Tag.objects.get(slug=slug)
                title = "文章标签归档:" + tag.name
            elif 'category__slug' in self.kwargs.keys():
                slug = self.kwargs.get('category__slug')
                category = Category.objects.get(slug=slug)
                title = "文章分类归档:" + category.name
            elif 'create_time__year' in self.kwargs.keys() and 'create_time__month' in self.kwargs.keys():  # 日期分类文章
                title = "文章日期归档:" + str(self.kwargs.get("create_time__year")) + "-" + str(
                    self.kwargs.get("create_time__month"))
            kwargs.update({"title": title})
        except Exception as e:
            logger.info(e)
            raise Http404()
        return super(ArticleListView, self).get_context_data(**kwargs)

路由绑定

编辑\myblog\article\urls.py

添加以下内容

from django.urls import path

from article import views

urlpatterns = [

    path('category/<slug:category__slug>', views.ArticleListView.as_view(), name='category'),
    path('tag/<slug:tags__slug>', views.ArticleListView.as_view(), name='tag'),
    path('date/<int:create_time__year>/<int:create_time__month>', views.ArticleListView.as_view(), name='date'),
]

编辑\myblog\myblog\urls.py

添加以下内容

from article.views import ArticleListView
path('', ArticleListView.as_view(), name="index"),

首页整体效果图

未登录效果图

在这里插入图片描述

登陆后效果图

在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值