Django 博客开发笔记

Report 笔记

0x01:前言,关于新Blog

过去我曾多次提起想抛弃Wordpress,Typecho自己写博客,而现在如君所见,在将近两个月的努力后终于实现了这个愿望。我并非完完全全的开发者,这个项目也是一时兴起,因此无论前端后端,还是Django都存在结构不规范,考虑功能不周全的问题。如果不出意外这个博客会是我第一个长期维护的项目,我计划在今年七月更新整个博客的结构层级,尝试用Boostrap代替现在的前端样式。

而这篇文章则用于记录我Django开发遇到的问题,如果你在浏览该博客的过程中遇到任何适配问题或者发现安全漏洞,欢迎通过侧边导航栏的邮箱联系我。

     

0x02:用Xadmin替代默认后台

Xadmin是第三方后台管理系统,我个人认为它在绝大多数时候都比默认后台更优秀。想使用它需要先安装依赖包和库,安装完成后在项目的Settting.py里添加:

INSTALLED_APPS = [
    'xadmin',
    'crispy_forms',
    'reversion',
]

修改Urls.py:

import xadmin
from django.conf.urls import url
from xadmin.plugins import xversion

urlpatterns = [
    url(r'^admin/', xadmin.site.urls),
]

最后在admin.py里注册的时候同样要修改,以GlobalSettings为例:

from xadmin import views
import xadmin

class GlobalSettings(object):
    site_title = "TalkSelf Project"
    site_footer = "Designed and copyright by Lion Ei'Jonson ©2018·All rights reserved."

xadmin.site.register(views.CommAdminView, GlobalSettings)

     

0x03:上传文件随机文件名

上传文件我经常这样写,models.py:

from django.db import models


class Image(models.Model):
    Img = models.FileField(upload_to='./static/image')

    def __unicode__(self):
        return self.Img

    class Meta:
        verbose_name_plural = '图片'
        verbose_name = '图片'

服务器保存的文件名与上传时的相同。虽然Django只会解析路由表里配置好的文件,上传的Shell很难被执行,但我依旧认为重命名是个好习惯。因为这个功能在一个工程里经常用,因此可以在工程根路径创建system目录,包含storage.py及__init__.py,storage.py:

# -*- coding: UTF-8 -*-
from django.core.files.storage import FileSystemStorage
import os


class ImageStorage(FileSystemStorage):
    from django.conf import settings

    def __init__(self, location=settings.MEDIA_ROOT, base_url=settings.MEDIA_URL):
        super(ImageStorage, self).__init__(location, base_url)

    def _save(self, name, content):
        import os
        import time
        import random
        ext = '.jpg'
        d = os.path.dirname(name)
        fn = time.strftime('%Y%m%d%H%M%S')
        fn += '_%d' % random.randint(0, 100)
        name = os.path.join(d, fn + ext)
        return super(ImageStorage, self)._save(name, content)

再在models.py引用该方法:

from system.storage import ImageStorage
from django.db import models


class Image(models.Model):
    Img = models.FileField(upload_to='./static/image', storage=ImageStorage())

    def __unicode__(self):
        return self.Img

    class Meta:
        verbose_name_plural = '图片'
        verbose_name = '图片'

通过重写ImageStorage类上传的文件名将会根据时间随机生成,只要对重命名有需求的图片上传模块就可以直接引用storage。

     

0x04:后台删除数据的同时,删除原文件

通过上一节的代码实现了文件上传功能,但后台删除上传图片数据的时候,图片是不会跟着数据被一起删除的,需要自己写删除方法,models.py:

from django.core.files.storage import FileSystemStorage
import os


class Image(models.Model):
    Img = models.FileField(upload_to='./static/image', storage=ImageStorage())

    def __unicode__(self):
        return self.Img

    class Meta:
        verbose_name_plural = '图片'
        verbose_name = '图片'

def delete_file(sender, **kwargs):
    patch = kwargs['instance']
    os.remove(patch.Img.path)

post_delete.connect(delete_file, sender=Image)

     

0x05:通过Markdown渲染页面

Markdown在文章编写方面非常友好,如果只是简单的编辑工作Markdown能完全替代Web Editor,以简单的模型举例,models.py:

from django.db import models


class Archives(models.Model):
    Title = models.CharField(max_length=64)
    Catalog = models.CharField(max_length=16)

    def __unicode__(self):
        return self.Title

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

在Views.py层进行渲染:

from django.shortcuts import render
from blog.models import Archives
import markdown

def article(request, title):
    articles = Archives.objects.filter(Title=title).values()
    for item in articles:
        item['Content'] = markdown.markdown(item['Content'], extensions=['markdown.extensions.extra',
                                                                         'markdown.extensions.codehilite',
                                                                         'markdown.extensions.toc'])
    return render(request, "article.html", {'article': articles})

通过添加新的依赖库Pygments,并在前端引入样式表就能实现语法高亮。

     

0x06:上传图片制作缩略图

最初设计的时候我没打算做缩略图,但是用Javascript裁剪原图效果不理想,models.py:

def clipping_thumb(path, maxheight=270):
    pixbuf = PIL.Image.open(path)
    width, height = pixbuf.size
    if height > maxheight:
        width = int(maxheight * (width / height))
        pixbuf.thumbnail((width, maxheight), PIL.Image.ANTIALIAS)
    return pixbuf


class Travel(models.Model):
    Img = models.FileField(upload_to='./static/image')

    def save(self):
        super(Travel, self).save()
        base, ext = os.path.split(os.path.basename(self.Img.path))
        thumb_pixbuf = clipping_thumb(os.path.join(MEDIA_ROOT, self.Img.path))
        relate_thumb_path = os.path.join(THUMB_ROOT, base + ext)
        thumb_path = os.path.join(MEDIA_ROOT, relate_thumb_path)
        thumb_pixbuf.save(thumb_path)
        origin = os.path.join(MEDIA_ROOT, self.thumb.path)
        os.remove(origin)

    def __unicode__(self):
        return self.Img

    class Meta:
        verbose_name_plural = '旅途照片'
        verbose_name = '旅途照片'