社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Django

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

杜赛_dusai • 5 年前 • 548 次点击  
阅读 46

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

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

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

准备工作

与用户头像类似,标题图是属于每篇博文自己的“资产”,因此需要修改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>

...
复制代码

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

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

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

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

看起来似乎不错。

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

确实保存到想要的目录下,并且左下角显示图片的宽度全都为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以内)是花不了多少钱的。


Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/29322
 
548 次点击