django快速实现个人博客(附源码)

简介: Django作为一款成熟的Python Web开发框架提供了丰富的内置功能,如ORM(对象关系映射)、Admin管理界面、URL分发、模板系统、表单处理等,使得开发者能够快速搭建Web应用,大幅提高了开发效率。下面介绍如何快速的通过django模板系统快速实现个人博客。

Django作为一款成熟的Python Web开发框架提供了丰富的内置功能,如ORM(对象关系映射)、Admin管理界面、URL分发、模板系统、表单处理等,使得开发者能够快速搭建Web应用,大幅提高了开发效率。以前写过一篇博文《Django+Vue快速实现博客网站》介绍了通过Djang+Vue快速实现博客网站,django+vue作为个人博客来说稍显复杂,部署起来也比较麻烦,Vue的单页面架构也不利于SEO,更简单的解决方案其实还是用django的模板系统快速构建web应用,对于个人博客来说部署和运维更加简单也利于SEO。下面介绍如何快速的通过django模板系统快速实现个人博客。

一、工程目录组织结构

在这里插入图片描述

二、模型及管理实现

模型及管理端的实现沿用《Django+Vue快速实现博客网站》文章中的实现,用Django搭建很快很简单。
模型很简单,根据博客要显示的内容包括有‘文章分类’、‘文章标签’、‘博客文章’、‘站点信息’、‘社交信息’、‘聚焦’,模型定义分别如下: 这里要说明的是因为博客文章内容准备用markdown编写,所以引入了mdeditor from mdeditor.fields import MDTextField 内容字段content=MDTextField(verbose_name='内容')
模型代码示例如下:

1、模型

from django.db import models
from common.basemodel import BaseModel
from mdeditor.fields import MDTextField
# Create your models here.
'''文章分类'''
class BlogCategory(BaseModel):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=50,verbose_name='分类名称',default='')
    href = models.CharField(max_length=100,verbose_name='分类路径',default='')

    def __str__(self):
        return self.title

    class Meta:
        verbose_name = '文章分类'
        verbose_name_plural = '文章分类'


'''文章标签'''


class Tag(BaseModel):
    id=models.AutoField(primary_key=True)
    tag=models.CharField(max_length=20, verbose_name='标签')

    def __str__(self):
        return self.tag

    class Meta:
        verbose_name='标签'
        verbose_name_plural='标签'


'''博客文章'''
class BlogPost(BaseModel):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=200, verbose_name='文章标题', unique = True)
    category = models.ForeignKey(BlogCategory, blank=True,null=True, verbose_name='文章分类', on_delete=models.DO_NOTHING)
    isTop = models.BooleanField(default=False, verbose_name='是否置顶')
    isHot = models.BooleanField(default=False, verbose_name='是否热门')
    isShow = models.BooleanField(default=False, verbose_name='是否显示')
    summary = models.TextField(max_length=500, verbose_name='内容摘要', default='')
    content = MDTextField(verbose_name='内容')
    viewsCount = models.IntegerField(default=0, verbose_name="查看数")
    commentsCount = models.IntegerField(default=0, verbose_name="评论数")
    tags = models.ManyToManyField(to=Tag, related_name="tag_post", blank=True, default=None, verbose_name="标签")
    blogSource = models.CharField(max_length=200, blank=True, null=True, default='',verbose_name='文章来源')
    pubTime = models.DateTimeField(blank=True, null=True, verbose_name='发布日期')

    @property
    def tag_list(self):
        return ','.join([i.tag for i in self.tags.all()])

    def __str__(self):
        return self.title

    class Meta:
        verbose_name = '博客文章'
        verbose_name_plural = '博客文章'


'''站点信息'''


class Site(BaseModel):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=50, verbose_name='站点名称', unique = True)
    avatar = models.CharField(max_length=200, verbose_name='站点图标')
    slogan = models.CharField(max_length=200, verbose_name='站点标语')
    domain = models.CharField(max_length=200, verbose_name='站点域名')
    notice = models.CharField(max_length=200, verbose_name='站点备注')
    desc = models.CharField(max_length=200, verbose_name='站点描述')

    def __str__(self):
        return self.name

    class Meta:
        verbose_name = '站点信息'
        verbose_name_plural = '站点信息'


'''社交信息'''


class Social(BaseModel):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=20, verbose_name='标题')
    icon = models.CharField(max_length=200, verbose_name='图标')
    color = models.CharField(max_length=20, verbose_name='颜色')
    href = models.CharField(max_length=100, verbose_name='路径')

    def __str__(self):
        return self.title

    class Meta:
        verbose_name = '社交信息'
        verbose_name_plural = '社交信息'


'''聚焦'''


class Focus(BaseModel):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=20, verbose_name='标题')
    img = models.CharField(max_length=100, verbose_name='路径')

    def __str__(self):
        return self.title

    class Meta:
        verbose_name = '聚焦'
        verbose_name_plural = '聚焦'


'''友链'''


class Friend(BaseModel):
    id = models.AutoField(primary_key=True)
    siteName = models.CharField(max_length=20, verbose_name='友链站点名称')
    path = models.CharField(max_length=100, verbose_name='地址路径')
    desc = models.CharField(max_length=200, verbose_name='描述')

    def __str__(self):
        return self.siteName

    class Meta:
        verbose_name = '友链'
        verbose_name_plural = '友链'

2、admin管理

实际上只要把模型注册到admin就可以了

from django.contrib import admin
from blog.models import *
# Register your models here.
@admin.register(BlogCategory)
class BlogCategoryAdmin(admin.ModelAdmin):
    admin.site.site_title="ishareblog后台"
    admin.site.site_header="ishareblog后台"
    admin.site.index_title="ishareblog管理"

    list_display = ['id', 'title', 'href']

@admin.register(BlogPost)
class BlogPostAdmin(admin.ModelAdmin):
    list_display = ['title','category','isTop','isHot']
    search_fields = ('title',)

@admin.register(Site)
class SiteAdmin(admin.ModelAdmin):
    list_display = ['name','slogan','domain','desc']

@admin.register(Social)
class SocialAdmin(admin.ModelAdmin):
    list_display = ['title','href']

@admin.register(Focus)
class FoucusAdmin(admin.ModelAdmin):
    list_display = ['title','img']

@admin.register(Friend)
class FoucusAdmin(admin.ModelAdmin):
    list_display = ['siteName','path','desc']

@admin.register(Tag)
class TagAdmin(admin.ModelAdmin):
    list_display = ['id','tag']

三、博客展现实现

博客前端展现用django的模板技术实现。在网上找了一个基于Bootstrap v4.3.1的小清新风格HTML博客模板,https://gitee.com/yinqi/Light-Year-Blog 这个博客模只有三个页面,首页,详细页和About页面,样式和js都不多,比较简单。将html模板放入到templates的blog目录,为了便于维护将一些公共部分抽到了base.html,index.html和post.html 通过{ % extends 'blog/base.html' % }进行应用

1、视图实现

from django.http import HttpResponse, Http404
from django.template import loader
from django.core.paginator import Paginator
from blog.models import BlogPost, Tag, BlogCategory
from django.shortcuts import render
from django.db.models import Count
from django.db.models.functions import TruncYear

import markdown2


# 首页/列表页视图实现.
def index(request):
    category_id = request.GET.get('category')
    tag_id = int(request.GET.get('tag',0))
    year = request.GET.get('year')
    search = request.GET.get('search')
    if category_id:
        blogpost_list = BlogPost.objects.filter(category=category_id, isShow=True).order_by('-isTop', '-pubTime')
    elif tag_id:
        blogpost_list = BlogPost.objects.filter(tags__id=tag_id, isShow=True).order_by('-isTop', '-pubTime')
    elif year:
        blogpost_list = BlogPost.objects.filter(pubTime__year=year, isShow=True).order_by('-isTop', '-pubTime')
    elif search:
        blogpost_list = BlogPost.objects.filter(content__icontains=search, isShow=True).order_by('-isTop', '-pubTime')
    else:
        # 筛选出需要显示的博客文章
        blogpost_list = BlogPost.objects.filter(isShow=True).order_by('-isTop', '-pubTime', '-update_time')
    # 每页显示的数量
    per_page = 10

    # 创建分页器实例
    paginator = Paginator(blogpost_list, per_page)

    # 获取当前页码,如果没有提供,则默认为第一页
    page_number = request.GET.get('page') or 1

    # 获取当前页的数据
    page_obj = paginator.get_page(page_number)

    # 计算显示的页码范围
    current_page = int(page_number)
    pages_to_show = 11  # 当前页前后各5页加上当前页共11页
    start_page = max(current_page - 5, 1)
    end_page = min(start_page + pages_to_show - 1, paginator.num_pages)

    template = loader.get_template("blog/index.html")
    context = {
   
        "page_obj": page_obj,
        'start_page': start_page,
        'end_page': end_page,
        'hot_posts': get_hot_posts(),
        'tags': get_all_tags(),
        'post_grouped_by_year':get_post_groped_by_year(),
        'categories': get_categories(),
        'category_id': category_id,
        'tag_id': tag_id,
        'year': year,
        'search': search,
    }
    return HttpResponse(template.render(context, request))


# 详情页视图实现.
def post_detail(request, id):
    try:
        post_obj = BlogPost.objects.get(id=id)
        html_content = markdown2.markdown(post_obj.content,
                                          extras=["code-color", "fenced-code-blocks", "cuddled-lists", "tables",
                                                  "with-toc", "highlightjs-lang"])
        html_content = html_content.replace('< table >', '< table class="table table-bordered" >')
        html_content = html_content.replace('< img src=', '< img style="max-width:100%;height:auto;" src= ')
        context = {
   "post_obj": post_obj, "html_content": html_content, "hot_posts": get_hot_posts(),"tags": get_all_tags(),"post_grouped_by_year":get_post_groped_by_year(),'categories': get_categories()}
    except BlogPost.DoesNotExist:
        raise Http404("Post does not exist")
    return render(request, "blog/post.html", context)


def get_hot_posts():
    # 获取点赞数最高的前5篇文章
    hot_posts = BlogPost.objects.filter(isShow=True).order_by('-viewsCount', '-pubTime')[:5]
    return hot_posts


def get_all_tags():
    # 获取所有的标签
    tags = Tag.objects.all()  # 获取所有的标签
    return tags

def get_post_groped_by_year():
    # 将发布日期截断为年份,并计算每年的文章数量。
    post_grouped_by_year = (
        BlogPost.objects
        .annotate(year=TruncYear('pubTime'))
        .values('year')  # 返回的字典包含'year'键
        .annotate(publication_count=Count('id'))  # 计算每年的文章数量
        .order_by('-year')  # 按年排序
    )
    return post_grouped_by_year

def get_categories():
    # 获取所有分类
    categories = BlogCategory.objects.all()
    return categories

2、模板实现

静态文件如css、js等放到static的blog目录,html模板文件放到templates的blog目录
在setting.py文件中配置 STATIC_URL = 'static/',在html模板文件中通过{% raw %}{ % load static % }{% endraw %} 将静态文件的地址引用进来
将公共部分抽取出来形成base.html

{ % load static % }
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>XieJava的博客</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="" />
<meta name="keywords" content="" />
<meta name="author" content="xiejava" />
<link rel="shortcut icon" type="image/x-icon" href="favicon.ico" />
<link rel="stylesheet" type="text/css" href="{ % static 'blog/css/bootstrap.min.css' % }" />
<link rel="stylesheet" type="text/css" href="{ % static 'blog/css/materialdesignicons.min.css' % }" />
<link rel="stylesheet" type="text/css" href="{ % static 'blog/css/style.min.css' % }" />
</head>
<body>
<header class="lyear-header text-center" style="background-image:url(images/left-bg.jpg);">
  <div class="lyear-header-container">
    <div class="lyear-mask"></div>
    <h1 class="lyear-blogger pt-lg-4 mb-0"><a href="{ % url 'index' % }">XieJava的博客</a></h1>
    <nav class="navbar navbar-expand-lg">
    <a class="navbar-toggler" data-toggle="collapse" data-target="#navigation" aria-controls="navigation" aria-expanded="false" aria-label="Toggle navigation">
      <div class="lyear-hamburger">
        <div class="hamburger-inner"></div>
      </div>
    </a>

    <div id="navigation" class="collapse navbar-collapse flex-column">
     <div class="profile-section pt-3 pt-lg-0">
       <img class="profile-image mb-3 rounded-circle mx-auto" src="https://img9.doubanio.com/icon/ul70489051-4.jpg" width="120" height="120" alt="xiejava" >
       <div class="lyear-sentence mb-3">
            记录最好的自己<br>
            写是为了更好的思考,坚持写作,力争更好的思考。
        </div>
        <hr>
     </div>

     <ul class="navbar-nav flex-column text-center">
       <li class="nav-item active">
          <a class="nav-link" href="{ % url 'index' % }">首页</a>
       </li>
          { % for category in categories % }
       <li class="nav-item">
         <a class="nav-link" href="{ % url 'index' % }?category={  { category.id  }  }">{  { category.title  }  }</a>
       </li>
          { % endfor % }
       <li class="nav-item">
         <a class="nav-link" href="{ % url 'index' % }">关于我</a>
       </li>
     </ul>

     <div class="my-2 my-md-3">
        <form class="lyear-search-form form-inline justify-content-center pt-3">
          <input type="text" id="search" name="search" class="form-control mr-md-1" placeholder="搜索关键词" />
        </form>
     </div>
   </div>
  </nav>
  </div>
</header>
<div class="lyear-wrapper">
  <section class="mt-5 pb-5">
    <div class="container">

      <div class="row">
        <!-- 文章列表 -->
        <div class="col-xl-8">
          <!-- 内容 -->
           { % block content % }
            <!-- 默认内容 -->
           { % endblock % }
        </div>
        <!-- 内容 end -->

        <!-- 侧边栏 -->
        <div class="col-xl-4">
          <div class="lyear-sidebar">
            <!-- 热门文章 -->
            <aside class="widget widget-hot-posts">
              <div class="widget-title">热门文章</div>
              <ul>
                  { % for post in hot_posts % }
                <li>
                  <a href="{ % url 'post_detail' id=post.id % }">{  { post.title  }  }</a> <span>{  { post.pubTime  }  }</span>
                </li>
                  { % endfor % }
              </ul>
            </aside>

            <!-- 归档 -->
            <aside class="widget">
              <div class="widget-title">归档</div>
              <ul>
                  { % for post in post_grouped_by_year % }
                      <li><a href="{ % url 'index' % }?year={  { post.year|date:'Y'  }  }" >{ % if year == post.year|date:'Y' % }<b>{  { post.year|date:'Y'  }  } 年 </b>{ % else % }{  { post.year|date:'Y'  }  } 年{ % endif % }</a> ({  { post.publication_count  }  })</li>
                  { % endfor % }
              </ul>
            </aside>

            <!-- 标签 -->
            <aside class="widget widget-tag-cloud">
              <div class="widget-title">标签 </div>
              <div class="tag-cloud">
                  { % for tag in tags % }
                <a href="{ % url 'index' % }?tag={  { tag.id  }  }" { % if tag_id == tag.id % }class="badge badge-primary"{ % else % }class="badge badge-light"{ % endif % }>{  { tag.tag  }  }</a>
                  { % endfor % }
              </div>
            </aside>
          </div>
        </div>
        <!-- 侧边栏 end -->
      </div>

    </div>
    <!-- end container -->
  </section>
</div>
<script type="text/javascript" src="{ % static 'blog/js/jquery.min.js' % }"></script>
<script type="text/javascript" src="{ % static 'blog/js/jquery.nicescroll.min.js' % }"></script>
<script type="text/javascript" src="{ % static 'blog/js/bootstrap.min.js' % }"></script>
<script type="text/javascript" src="{ % static 'blog/js/main.min.js' % }"></script>
</body>
</html>

博客首页/列表页
通过{ % extends 'blog/base.html' % } 将公共部门引入进来后index.html的内容就简洁了很多
index.html


{ % extends 'blog/base.html' % }

<!-- 内容 -->
{ % block content % }
 { % if page_obj.object_list.count > 0 % }
  { % for blogpost in page_obj.object_list % }
  <article class="lyear-arc">
    <div class="arc-header">
      <h2 class="arc-title"><a href="article/{  { blogpost.id  }  }">{  { blogpost.title  }  }</a></h2>
      <ul class="arc-meta">
        <li><i class="mdi mdi-calendar"></i> {  { blogpost.pubTime  }  }</li>
        <li><i class="mdi mdi-tag-text-outline">
        </i> { % for tag in blogpost.tags.all % }<a href="{ % url 'index' % }?tag={  { tag.id  }  }">{  { tag.tag  }  }</a>&nbsp{ % endfor % }</li>
        <!--<li><i class="mdi mdi-comment-multiple-outline"></i> <a href="#">3 评论</a></li>-->
        <li><i class="mdi mdi-heart-outline"></i> <a href="#">{  { blogpost.viewsCount  }  } 喜欢</a></li>
      </ul>
    </div>

    <div class="arc-synopsis">
      <p>{  { blogpost.summary  }  }</p>
    </div>
  </article>
 { % endfor % }


  <!-- 分页 -->
  <div class="row">
    <div class="col-lg-12">
      <ul class="pagination">
       { % if page_obj.has_previous % }
        <li class="page-item"><a class="page-link" href="?page={  { page_obj.previous_page_number  }  }"><i class="mdi mdi-chevron-left"></i></a></li>
       { % endif % }

        { % for page_no in page_obj.paginator.page_range % }
            { % if page_no >= start_page and page_no <= end_page % }
                { % if page_no == page_obj.number % }
                    <li class="page-item active"><a class="page-link" href="#">{  { page_no  }  }</a></li>
                { % else % }
                    <li class="page-item"><a class="page-link" href="?page={  { page_no  }  }{ % if tag_id % }&tag={  { tag_id  }  }{ % endif % }{ % if year % }&year={  { year  }  }{ % endif % }{ % if search % }&search={  { search  }  }{ % endif % }">{  { page_no  }  }</a></li>
                { % endif % }
            { % endif % }
        { % endfor % }

      { % if page_obj.has_next % }
        <li class="page-item"><a class="page-link" href="?page={  { page_obj.next_page_number  }  }{ % if tag_id % }&tag={  { tag_id  }  }{ % endif % }{ % if year % }&year={  { year  }  }{ % endif % }{ % if search % }&search={  { search  }  }{ % endif % }"><i class="mdi mdi-chevron-right"></i></a></li>
      { % endif % }

      <p>总页数: {  { page_obj.paginator.num_pages }  }</p>
      </ul>
    </div>
  </div>
    { % else % }
    <p> 没有找到文章 </p>
    { % endif % }
  <!-- 分页 end -->
{ % endblock % }
<!-- 内容 end -->

博客详情页post.html

{ % extends 'blog/base.html' % }
<!-- 文章阅读 -->

{ % block content % }

          <article class="lyear-arc">
            <div class="arc-header">
              <h2 class="arc-title"><a href="#">{  { post_obj.title  }  }</a></h2>
              <ul class="arc-meta">
                <li><i class="mdi mdi-calendar"></i> {  { post_obj.pubTime  }  }</li>
                <li> { % for tag in post_obj.tags.all % }<a href="{ % url 'index' % }?tag={  { tag.id  }  }">{  { tag.tag  }  }</a>{ % endfor % }</li>
                <!--<li><i class="mdi mdi-comment-multiple-outline"></i> <a href="#">3 评论</a></li>-->
                <li><i class="mdi mdi-heart-outline"></i> <a href="#">{  { post_obj.viewsCount  }  } 喜欢</a></li>
              </ul>
            </div>

            <div class="arc-preview">
              <img src="images/blog/post-1.png" alt="" class="img-fluid rounded" />
            </div>

            <div class="lyear-arc-detail">
               {  { html_content|safe  }  }
            </div>

          </article>

{ % endblock % }
<!-- 内容 end -->

四、部署及效果

在部署之前执行python manage.py collectstatic 将admin等其他模块用到的静态文件统一输出到static的目录。
通过 python manage.py runserver 启动应用就可以看到效果。
实际效果见 http://iblog.ishareread.com/
博客首页
在这里插入图片描述

博客详情页
在这里插入图片描述

五、源代码

所有源代码及说明见 https://gitee.com/xiejava/ishareblog


博客地址:http://xiejava.ishareread.com/

目录
相关文章
|
移动开发 搜索推荐 算法
Python基于Django的电影推荐系统和论坛项目完整源码
Python基于Django的电影推荐系统和论坛项目完整源码
1033 0
Python基于Django的电影推荐系统和论坛项目完整源码
|
2月前
|
数据库 数据库管理 Python
#736421#基于django的个人博客系统
#736421#基于django的个人博客系统
46 4
#736421#基于django的个人博客系统
|
4月前
|
JSON API 网络架构
Django 后端架构开发:DRF 高可用API设计与核心源码剖析
Django 后端架构开发:DRF 高可用API设计与核心源码剖析
95 0
|
7月前
|
安全 Python
102-Django开发学校教务管理系统源码+ER图
**Django学校教务管理系统**是使用Python的Django框架开发的,专注于学生和教师的互动。系统功能包括班级管理(教师添加和管理学生,学生查看信息)、分数管理(教师打分和更新,学生查看详情)、作业管理(上传、提交和批改)、师生通讯、发布公告以及用户配置。系统支持管理员、教师和学生的不同角色和权限,特点是互动性强、操作便捷且安全性高。通过这个系统,教学过程变得更加高效,有助于提升教学质量和学习效果。
91 4
|
关系型数据库 MySQL 数据库
Python 基于 Django 的公务员考试信息管理系统+数据库(附源码,教程)
Python 基于 Django 的公务员考试信息管理系统+数据库(附源码,教程)
|
数据库 Python
Django开发个人博客基本示例
以下是一个简单的 Django 个人博客开发示例。在这里只概述基本步骤和代码。请确保你已经安装了 Python 和 Django。1. 创建一个新的 Django 项目```bashdjango-admin startproject myblog```2. 进入项目目录并创建一个新的应用```bashcd myblogpython manage.py startapp blog```3. 在...
110 0
|
7月前
|
API 数据安全/隐私保护 Python
101-Django开发毕业设计学院ERM系统源码
使用Django框架开发的学校ERP系统旨在提升现代教育的管理效率。该系统针对学生、老师和管理员三类用户设计,提供登录、考勤管理、分数查看、课程表、教学管理等功能。技术栈包括Python 3.11和Django 5,以及djangorestframework用于API交互。系统实现了智能化的操作,优化了学校的管理平台,提高了服务水平和教学质量。
71 0
|
数据可视化 数据库 数据安全/隐私保护
Python 基于 Django 的学生成绩管理系统,可视化界面+数据库(附源码,教程)
Python 基于 Django 的学生成绩管理系统,可视化界面+数据库(附源码,教程)
|
关系型数据库 MySQL 测试技术
django基于python智能在线考试阅卷系统(源码+系统+mysql数据库+Lw文档)
随着计算机多媒体技术的发展和网络的普及。采用当前流行的B/S模式以及3层架构的设计思想通过Python技术来开发此系统的目的是建立一个配合网络环境的基于python的学校对在线考试阅卷系统的平台,这样可以有效地解决基于python的在线考试阅卷系统混乱的局面。本文首先介绍了基于python的在线考试系统的发展背景与发展现状,然后遵循软件常规开发流程,首先针对系统选取适用的语言和开发平台,根据需求分析制定模块并设计数据库结构,再根据系统总体功能模块的设计绘制系统的功能模块图,流程图以及E-R图。然后,设计框架并根据设计的框架编写代码以实现系统的各个功能模块。最后,对初步完成的系统进行测试,主要是
660 0
|
安全 关系型数据库 MySQL
基于“python3.6.5“和“Django2.1“开发的的个人博客系统
基于“python3.6.5“和“Django2.1“开发的的个人博客系统
136 0