利用Django徒手写个静态页面生成工具

每个Geek对折腾自己的博客都有着一份执念

背景介绍

曾经多次在不同的平台写博客,但全部都以失败而告终。去年七月选择微信公众号做为平台开始了又一次的技术分享,庆幸一直坚持到现在,但随着文章发表的越来越多,发现公众号对于PC端很不友好,文章列表没有PC端入口,查看分享很不方便,所以就利用github pages搭建了一个【运维咖啡吧】的网站,分类展示公众号内发表的所有文章以及一些未在公众号发表的琐碎内容

为了追求极速的浏览体验,整个网站采用纯静态的方式构建,这里的静态并不是像Jekyll或者Hexo之类的静态博客框架,而是手写HTML,页面少的时候还能应对,但随着页面越来越多,维护这些内容就成了灾难,好在对Django比较熟悉,于是便动手写了这么一个静态博客页面生成工具

主要功能

网站非常简单,只有三类页面,主页、文章列表页和文章详情页

  • 主页用来分类展示公众号内的文章列表
  • 文章列表页用来展示网站内文章(一些琐碎的未在公众号发表的文章)的列表
  • 文章详情页用来展示具体文章的内容

基于以上的内容分析,其实只需要做两个后台页面,包含几个小功能,画个思维导图

首页为什么要去读取JSON文件呢?主要是因为运维咖啡吧的小程序也同时依赖这个JSON文件,修改一个地方避免维护多份数据

最终实现的效果如下图

接下来介绍下实现这些功能用到的技术或组件

所用技术

读取及写入文件

from django.conf import settings


class FileRun:
    def __init__(self):
        self.file = settings.BASE_DIR + '/ops_coffee/backends/blog.json'

    def read(self):
        try:
            with open(self.file, 'r', encoding='utf8') as f:
                return True, f.read()

        except Exception as e:
            return False, str(e)

    def write(self, content):
        try:
            with open(self.file, 'w', encoding='utf8') as f:
                return True, f.write(content)

        except Exception as e:
            return False, str(e)

读取及写入文件的操作与Django的View没有太大的关系,所以这里我用了一个单独的类来处理,解释下其中的四个用法

1. 本地文件路径不要硬编码到代码中,尽量采用settings.BASE_DIR相对路径,或者直接将路径以变量的形式写入到settings文件,例如我们后边要说的生成本地文件的目录就直接在settings中添加了一个变量OPS_COFFEE_GIT_DIR

2. 每个方法返回两个参数状态和数据return True,data,这样在调用这个方法的时候就可以很方便的判断出来这个方法是执行成功还是失败,例如如下代码

state, data = FileRun().read()
if state:
    return(data)

3. 读取文件使用with方法可以在你读取结束后自动执行f.close()关闭文件,避免因打开文件过多造成的资源消耗

4. 使用try来避免程序直接抛错,有错误处理机制

JSON格式化

为了展示好看且能实现语法错误提示,采用了jsoneditor插件,这是一个前端的插件,使用非常简单

<div class="col-sm-12" id="jsoneditor" style="height:620px"></div>
<script src="/static/js/jsoneditor.min.js"></script>
<script>
  // create the editor
  var container = document.getElementById("jsoneditor");
  var editor = new JSONEditor(container, {
    mode: 'code'
  });

  // set json
  editor.set({{ data|safe }});
</script>

JSON Editor 可以用来查看、编辑、格式化和验证JSON,支持多种模式,例如tree、code、text,当为tree模式时显示树状结构,当为text时显示纯文本,我们这里采用了code模式有行号和颜色,看起来更美观

safe django从view向template传递HTML数据的时候,为了防止html中包含恶意攻击的代码django默认不会渲染HTML,所以需要在template接收到html数据后添加|safe进行渲染

生成HTML

观察会发现整个网站里所有的页面除了中间的内容区域之外,其他的地方都一样,所以我们只需要考虑替换中间的内容就可以了,实际上为了SEO等我们还需要替换title等数据

替换内容生成html文件这里使用了jinja2,我有尝试直接用django的template来渲染,但最终有一些编码问题没有解决,还是采用了jinja2,代码如下

from jinja2 import Template
from django.conf import settings

tmpl = """<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta name="theme-color" content="#2879d0" />
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <link rel="stylesheet" href="/css/style.min.css" media="screen" type="text/css" />

  <title>{{ title }}</title>
  <meta property="og:title" content="{{ title }}" />
  <meta property="og:description" content="{{ description }}" />
</head>

<body>
  <header>
    <div class="inner">
      <a href="https://ops-coffee.cn/">
        <h1>运维咖啡吧</h1>
      </a>
      <h2>追求技术的道路上,我从不曾停下脚步</h2>
    </div>
  </header>

  <div id="content-wrapper">
    <div class="inner clearfix">
      <section id="main-content">
      {% if havet %}
        <h1 id="art-title">{{ title }}</h1>
      {% endif %}

      {{ content }}
      </section>

      <aside id="sidebar">
        <blockquote class="route">微信公众号</blockquote>
        <img border="0" src="/images/z-qrcode.jpg" width="100%" height="100%" alt="ops-coffee" />

        <blockquote class="route">归档列表</blockquote>
        <div class="sidebar-list"><a href="/"> 精选文章列表</a></div>
        <div class="sidebar-list"><a href="/s/"> 日常运维记录</a></div>
      </aside>
    </div>
  </div>

</body>

</html>
"""

kwargs = {
    "havet": 0,
    "title": "运维咖啡吧",
    "description": "追求技术的道路上,我从不曾停下脚步",
    "content": content
}

_content = Template(tmpl).render(kwargs)
with open(self.blogDir + '/index.html', 'w', encoding='utf8') as f:
    f.write(_content)

tmpl 定义了一个模版,模版内可以使用诸如{{ title }}这样的变量或是{% if havet %}这样的语法

kwargs 定义了一个字典,字典的内容用来替换模版中的变量,字典的key值与模版里边的变量做匹配,匹配到了就用字典的value填充模版

_content 就是最终html的内容,Template(tmpl).render(kwargs)会将kwargs的每个key值与模版中的变量做替换

最后会将html内容写入到html文件

上传GitHub

网站使用github pages搭建,最后需要将生成的html文件上传到github,这里我们使用了gitpython库,gitpython库的用法跟原生git的命令非常像,只是命令中间以.连接

最佳的自动上传步骤应该是:

1. 本地生成ssh密钥,并将公钥上传至github,实现本机与ssh之间的无密码上传下载

2. 本地创建网站目录,这个目录需要跟settings里边的OPS_COFFEE_GIT_DIR变量一致,方便直接将html文件生成在这个目录下

3. 进入网站目录并使用git clone拉取github上的代码,注意这里应选择ssh协议的url,例如:git clone git@github.com:ops-coffee/demo.git .,且确定无需输入账号密码即可拉取

4. 然后就可以使用以下程序实现自动上传更新到github了,也就是在跑本文所讲的这个生成工具之前需要先做好以上三步

from git import Repo
from django.conf import settings


class GitRun:
    def __init__(self):
        self.repo = Repo(settings.OPS_COFFEE_GIT_DIR)

    def push(self):
        try:
            self.repo.git.add(A=True)
            self.repo.index.commit('ops-coffee')
            self.repo.remote(name='origin').push()

            return True, True
        except Exception as e:
            return False, str(e)

Repo() 选择已有的git仓库

git.add 添加本地修改到暂存区,A=True添加到暂存区时包含删除文件的修改

index.commit 提交修改到本地仓库,我这里比较粗糙,统一使用ops-coffee做为log

repo.remote().push() 选择远程分支并提交,name参数表示远程分支的名字

登陆登出

虽然是个简单的个人系统,但最基本的用户认证还是要有的,没有用Django默认的admin页面,但还想使用django提供的auth系统实现登陆登出的话,可以采用下边这种方式

from django.urls import path
from django.contrib.auth.views import LoginView, LogoutView

urlpatterns = [
    path('login', LoginView.as_view(template_name='login.html'), name='login-url'),
    path('logout', LogoutView.as_view(template_name='login.html'), name='logout-url'),
]

django.contrib.auth.views下导入LoginViewLogoutView,然后写两条url并指定自己的模版位置就可以使用django的登陆登出功能了,这在一些需要简单认证的系统中非常方便

写在最后

不断折腾的过程才是成长最快的过程,用技术来解决实际的问题是对技术最好的应用

本文分享自微信公众号 - 运维咖啡吧(ops-coffee)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-05-27

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏程序员果果

Spring Boot Security OAuth2 实现支持JWT令牌的授权服务器

之前的两篇文章,讲述了Spring Security 结合 OAuth2 、JWT 的使用,这一节要求对 OAuth2、JWT 有了解,若不清楚,先移步到下面两...

17040
来自专栏程序员果果

Spring Boot Security 整合 JWT 实现 无状态的分布式API接口

JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案。JSON Web Token 入门教程 - 阮一峰,这篇文章可以帮你了解JWT的概念...

14120
来自专栏腾讯技术工程官方号的专栏

【Go API 开发实战 7】基础 3:记录和管理 API 日志

apiserver 所采用的日志包 lexkong/log 是笔者根据开发经验,并调研 GitHub 上的 开源log 包后封装的一个日志包,也是笔者所在项目使...

22220
来自专栏Linyb极客之路

使用lombok编写优雅的Bean对象

使用java编写代码,十之八九都是在写java类,从而构建java对象。lombok之前也说了不少,但使用了这么多年,感觉还是有很多技巧可以使用的。

20550
来自专栏杨建荣的学习笔记

MySQL优化案例(二)

近期收到慢日志监控报警,在慢日志平台查看,主要瓶颈在于几条创建临时表的SQL语句,占用了大量的临时空间,需要优化。

15320
来自专栏Android机动车

Android开发神兵利器

在Google的广大支持下,便捷开发Android程序的Native工具层出不穷。其实Android开发涉及到的范围也不小,一些Web工具有时候也会带来事半功倍...

13810
来自专栏新智元

【GitHub热门开源】构建NLP深度学习模型其实就是搭积木

在构建自然语言理解深度学习模型过程中,研究人员或者工程师们经常需要在编程细节和代码调试上花费大量精力,而不是专注于模型架构设计与参数调整。

12620
来自专栏aoho求索

gRPC 使用 protobuf 构建微服务

以前使用 Laravel 做 web 项目时,是根据 MVC 去划分目录结构的,即 Controller 层处理业务逻辑,Model 层处理数据库的 CURD,...

16020
来自专栏DApp开发入门系列

【EOS DApp开发入门系列】搭建EOS主网节点

"initial_timestamp": "2018-06-08T08:08:08.888",

33850
来自专栏银河系资讯

Java创建Annotation

注解是Java很强大的部分,但大多数时候我们倾向于使用而不是去创建注解。例如,在Java源代码里不难找到Java编译器处理的@Override注解,Spring...

16520

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励