Django搭建个人博客:给文章加个漂亮的标题图

栏目: Python · 发布时间: 6年前

内容简介:现在虽然博客的功能大都实现了,但是界面还是比较朴素,特别是首页的文章列表几乎全是文字,看多了难免疲劳。因此,给每个文章标题配一张在与用户头像类似,标题图是属于每篇博文自己的“资产”,因此需要

现在虽然博客的功能大都实现了,但是界面还是比较朴素,特别是首页的文章列表几乎全是文字,看多了难免疲劳。因此,给每个文章标题配一张 标题图 ,不仅美观,用户也能通过图片快速了解文章内容。实际上大部分社交网站也都是这么干的,毕竟人的天性就是懒,能看图就坚决不看字。

上传用户头像 章节中,我们已经接触过上传、展示图片了。标题图的实现也差不多,不同的是本章会更近一步, 对图片进行缩放 等处理,使页面整洁美观、并且高效。

准备工作

与用户头像类似,标题图是属于每篇博文自己的“资产”,因此需要 修改model ,新建一个字段:

article/models.py

class ArticlePost(models.Model):
    ...
    
    # 文章标题图
    avatar = models.ImageField(upload_to='article/%Y%m%d/', blank=True)
    
    ...

注意上传地址中的 %Y%m%d 是日期格式化的写法。比如上传时间是2019年2月26日,则标题图会上传到 media/article/20190226 这个目录中。

记得数据迁移。

标题图通常在创建新文章的时候就设置好了,而新文章是通过表单上传到数据库中的。因此接下来就是 修改发表文章的表单类

article/forms.py

...
class ArticlePostForm(forms.ModelForm):
    class Meta:
        ...
        fields = ('title', 'body', 'tags', 'avatar')

增加了 avatar 字段而已,没有新内容。

下一步就是 修改视图 。因为POST的表单中包含了图片文件,所以要将 request.FILES 也一并绑定到表单类中,否则图片无法正确保存:

article/views.py

...
def article_create(request):
    if request.method == "POST":
        # 增加 request.FILES
        article_post_form = ArticlePostForm(request.POST, request.FILES)
        
        ...

很好,功能差不多已经通了,接下来就是对图片进行处理。

处理图片

写代码之前先构思一下需要进行怎样的处理:

  • 标题图对画质没有太高的要求,因此需要 缩小图片的体积 ,以便提高网页的加载速度。
  • 其次还需要对图片的长宽进行规范化。我比较喜欢将图片的 宽度设置得相同 ,这样标题可以比较整齐。

下一个问题是,代码应该写到什么地方呢?似乎在 modelform 或者 view 里处理图片都可以。在这里我打算把代码写到 model 中去,这样不管你在任何地方上传图片(包括后台中!),图片都会得到处理。

想好之后,就要行动了。还记得Pillow这个库吗,我们很早就把它安装好了,现在是使用它的时候了:

article/models.py

...

# 记得导入!
from PIL import Image

class ArticlePost(models.Model):
    ...
    # 前面写好的代码
    avatar = models.ImageField(upload_to='article/%Y%m%d/', blank=True)
    
    # 保存时处理图片
    def save(self, *args, **kwargs):
        # 调用原有的 save() 的功能
        article = super(ArticlePost, self).save(*args, **kwargs)

        # 固定宽度缩放图片大小
        if self.avatar and not kwargs.get('update_fields'):
            image = Image.open(self.avatar)
            (x, y) = image.size
            new_x = 400
            new_y = int(new_x * (y / x))
            resized_image = image.resize((new_x, new_y), Image.ANTIALIAS)
            resized_image.save(self.avatar.path)

        return article
    
...

代码不多,但是有很多细节,值得仔细推敲。不急,一行一行来:

  • save() 是model内置的方法,它会在model实例每次保存时调用。这里改写它,将处理图片的逻辑“塞进去”。
  • super(ArticlePost, self).save(*args, **kwargs) 的作用是调用父类中原有的 save() 方法,即将model中的字段数据保存到数据库中。因为图片处理是基于已经保存的图片的,所以 这句一定要在处理图片之前执行 ,否则会得到找不到原始图片的错误。
  • if 判断语句的条件有两个:

    • 博文的标题图不是必须的, self.avatar 剔除掉没有标题图的文章,这些文章不需要处理图片。
    • 不太好理解的是这个 not kwargs.get('update_fields') 。还记得 article_detail() 视图中为了统计浏览量而调用了 save(update_fields=['total_views']) 吗?没错,就是为了排除掉统计浏览量调用的 save() ,免得每次用户进入文章详情页面都要处理标题图,太影响性能了。

      这种判断方法虽然简单,但会造成模型和视图的紧耦合。读者在实践中可探索更优雅的方法,比如专门设置一个参数,用来判断是哪类视图调用了save()。

  • 接下来都是Pillow处理图片的流程了:打开原始图片,取得分辨率,将新图片的宽度设置为400并根据比例缩小高度,最后用新图片将原始图片覆盖掉。 Image.ANTIALIAS 表示缩放采用平滑滤波。
  • 最后一步,将父类 save() 返回的结果原封不动的返回去。

完美!

模板与测试

剩下的工作就比较简单了。

修改 发表文章的模板 ,让表单能够上传图片:

templates/article/create.html

...

<!-- 记得增加 enctype ! -->
<form ... enctype="multipart/form-data">
    ...

    <!-- 文章标题图 -->
    <div class="form-group">
        <label for="avatar">标题图</label>
        <input type="file" class="form-control-file" name="avatar" id="avatar">
    </div>
    
    ...
</form>
...

然后 修改文章列表模板 ,让其能够展现标题图。

为了美观,这里稍微改动了列表循环的整体结构:

templates/article/list.html

...

<!-- 列表循环 -->
<div class="row mt-2">
    {% for article in articles %}
        <!-- 标题图 -->
        {% if article.avatar %}
            <div class="col-3">
                <img src="{{ article.avatar.url }}" 
                     alt="avatar" 
                     style="max-width:100%; border-radius: 20px"
                >
            </div>
        {% endif %}

        <div class="col">
            <!-- 栏目 -->
            ...

            <!-- 标签 -->
            ...

            ...
            
        <hr style="width: 100%;"/>
    {% endfor %}
</div>

...

接下来又是喜闻乐见的测试环节。

启动服务器,打开发表文章页面:

Django搭建个人博客:给文章加个漂亮的标题图

选择几张分辨率各不相同的图片作为标题图,

发表几篇文章并回到文章列表页面:

Django搭建个人博客:给文章加个漂亮的标题图

看起来似乎不错。

查看一下media目录下实际保存的图片:

Django搭建个人博客:给文章加个漂亮的标题图

确实保存到想要的目录下,并且左下角显示图片的宽度全都为400了。

扫尾工作

功能已经实现了,但还有扫尾工作需要去做:

  • 需要对上传的图片做更多的验证工作,比如上传的文件是否为图片、分辨率是否满足要求。虽然在个人博客项目中这些验证并不是特别重要,但在其他项目中就说不好了:谁知道用户会上传些什么奇奇怪怪的东西?
  • 编辑文章、删除文章也同样需要处理上传的图片。你还可以将缩放分辨率的技术应用到用户头像上,比如裁剪成方形。

    注意:删除数据库中的avatar条目只是断开了数据表和图片的链接而已,实际上图片还保存在原来的位置。要彻底删除图片,你还得写操作系统文件的代码才行。

怎么实现这些功能就不赘述了,留给读者自己去折腾吧。

轮子

虽然本文是自己动手写的代码(严格说来Pillow也是轮子),但想必你也猜到了,还有更加智能的轮子: django-imagekit ,这个库可以直接继承到model字段里面,比如这样:

article/models.py

# 引入imagekit
from imagekit.models import ProcessedImageField
from imagekit.processors import ResizeToFit

class ArticlePost(models.Model):
    ...
    
    avatar = ProcessedImageField(
        upload_to='article/%Y%m%d',
        processors=[ResizeToFit(width=400)],
        format='JPEG',
        options={'quality': 100},
    )

字段中定义好了上传位置、处理规则、存储格式以及图片质量,你不需要写任何处理图片的代码了。

更多的用法见 官方介绍

总结

本章学习了如何上传并处理文章的标题图,从此博客首页就有了漂亮的外观。

需要指出的是,个人博客所采用的服务器通常性能不佳,用来保存文章缩略图等小尺寸的图片倒还好,但是 千万不要存储大尺寸的图片文件 ,否则用户等待几分钟都刷不开你的图片,那是很悲剧的。

因此建议你将大尺寸的图片、视频等放到专业的云对象存储服务商中,比如 七牛云又拍云 等,在你存储量很小时(10G以内)是花不了多少钱的。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Domain-Driven Design

Domain-Driven Design

Eric Evans / Addison-Wesley Professional / 2003-8-30 / USD 74.99

"Eric Evans has written a fantastic book on how you can make the design of your software match your mental model of the problem domain you are addressing. "His book is very compatible with XP. It is n......一起来看看 《Domain-Driven Design》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具