Django搭建个人博客:设置文章的栏目

3248阅读 · 25评论 · 2019/01/29发布   前往评论

博客的文章类型通常不止一种:有时候你会写高深莫测的技术文章,有时候又纯粹只记录一下当天的心情。

因此对文章的分类就显得相当的重要了,既方便博主对文章进行分类归档,也方便用户有针对性的阅读。

而文章分类一个重要的途径就是设置栏目

栏目的模型

实现文章栏目功能的方法有多种。你可以只是简单的在文章的Model中增加CharField()字段,以字符串的形式将栏目名称保存起来(实际上这种实现更像是“标签”,以后会讲到)。这样做的优点是比较简单;缺点也很明显,就是时间长了你可能会记混栏目的名字,并且也不方便对栏目的其他属性进行扩展。

因此对文章栏目可以独立为一个Model,用外键与文章的Model关联起来。

修改article/modles.py文件:

article/models.py

...

class ArticleColumn(models.Model):
    """
    栏目的 Model
    """
    # 栏目标题
    title = models.CharField(max_length=100, blank=True)
    # 创建时间
    created = models.DateTimeField(default=timezone.now)

    def __str__(self):
        return self.title


class ArticlePost(models.Model):
    ...

    # 文章栏目的 “一对多” 外键
    column = models.ForeignKey(
        ArticleColumn,
        null=True,
        blank=True,
        on_delete=models.CASCADE,
        related_name='article'
    )

    ...

栏目的Model有两个字段,“名称”和“创建日期”。

一篇文章只有一个栏目,而一个栏目可以对应多篇文章,因此建立“一对多”的关系。

写好模型后记得用makemigrationsmigrate指令迁移数据

列表中显示栏目

添加测试数据

模型写好之后就需要几条栏目的数据来进行测试。由于还没有写视图,因此需要善加利用Django自带的后台。

首先把栏目模型注册到后台:

article/admin.py

...

from .models import ArticleColumn

# 注册文章栏目
admin.site.register(ArticleColumn)

然后就可以在后台中添加栏目的数据条目了:

先随便添加了“HTML”、“Java”、“Django”这3条。

在后台中随便打开一篇文章,“栏目”字段已经静静的在等你了:

随机找几篇文章设置不同的栏目用于测试。

重写文章列表

之前我们用卡片类型的UI界面展示文章列表。

卡片的好处是简洁大方,但是随着数据的增多,卡片小小的版面已经不堪重负了。

因此这里重写list.html模板的列表循环:

article/list.html

...

<!-- 列表循环 -->
<div class="row mt-2">
    {% for article in articles %}
        <!-- 文章内容 -->
        <div class="col-12">
            <!-- 栏目 -->
            {% if article.column %}
                <button type="button" 
                    class="btn btn-sm mb-2
                        {% if article.column.title == 'Django' %}
                            btn-success
                        {% elif article.column.title == 'Java' %}
                            btn-danger
                        {% elif article.column.title == 'HTML' %}
                            btn-warning
                        {% endif %}
                    "
                >
                    {{ article.column }}
                </button>
            {% endif %}
            <!-- 标题 -->
            <h4>
                <b>
                    <a href="{% url 'article:article_detail' article.id %}"
                       style="color: black;" 
                    >
                        {{ article.title }}
                    </a>
                </b>
            </h4>
            <!-- 摘要 -->
            <div>
                <p style="color: gray;">
                    {{ article.body|slice:'100' }}...
                </p>
            </div>
            <!-- 注脚 -->
            <p>
                <!-- 附加信息 -->
                <span style="color: green;">
                    {{ article.total_views }} 浏览&nbsp;&nbsp;&nbsp;
                </span>
                <span style="color: blue;">
                    {{ article.created|date:'Y-m-d' }} 发布&nbsp;&nbsp;&nbsp;
                </span>
                <span style="color: darkred;">
                    {{ article.updated|date:'Y-m-d' }} 更新
                </span>
            </p>
            <hr>
        </div>
    {% endfor %}
</div>

...

最主要的改动就是新增了展现“栏目”的按钮。我们甚至还为不同的栏目设置了不同的按钮颜色。

在附加信息中顺便还把之前没有用到的日期信息也添加上去了。

来看看效果:

感觉还不错!

修改写文章功能

展示已经没问题了,但是发表新文章时还不能选择栏目。

修改写文章的模板,在表单中新增下面的内容:

templates/article/create.html

...

<!-- 提交文章的表单 -->
<form method="post" action=".">
    {% csrf_token %}
    <!-- 文章标题 -->
    ...

    <!-- 文章栏目 -->
    <div class="form-group">
        <label for="column">栏目</label>
        <select class="form-control" 
                id="column" 
                name="column"
        >
                <option value="none">请选择栏目..</option>
            {% for column in columns %}
                <option value="{{ column.id }}">{{ column }}</option>
            {% endfor %}
        </select>
    </div>

    <!-- 文章正文 -->
    ...
    <!-- 提交按钮 -->
    ...
</form>

<select>是表单的下拉框选择组件。在这个组件中循环列出所有的栏目数据,并且设置value属性,指定表单提交栏目的id值。

刷新页面,效果像下面这样:

跟之前一样,能够展示了,但是还没有处理表单的视图逻辑。

修改已有的写文章视图article_create(),让其能够处理表单上传的栏目数据:

article/views.py

# 引入栏目Model
from .models import ArticleColumn

...

# 写文章的视图
...
def article_create(request):
    if request.method == "POST":
        ...
        if article_post_form.is_valid():
            ...

            # 新增的代码
            if request.POST['column'] != 'none':
                new_article.column = ArticleColumn.objects.get(id=request.POST['column'])

            # 已有代码
            new_article.save()
            ...
    else:
        ...

        # 新增及修改的代码
        columns = ArticleColumn.objects.all()
        context = { 'article_post_form': article_post_form, 'columns': columns }

        ...

新增代码涉及getpost两部分:

  • POST:主要考虑某些文章是可以没有栏目的。因此用if语句判断该文章是否有栏目,如果有,则根据表单提交的value值,关联对应的栏目。
  • GET:增加栏目的上下文,以便模板使用。

测试一下,写文章的栏目功能应该可以正常工作了。

修改更新视图

更新文章的视图同样也需要升级一下。

还是先更改模板:

templates/article/update.html

...

<!-- 提交文章的表单 -->
<form method="post" action=".">
    {% csrf_token %}

    <!-- 文章标题 -->
    ...

    <!-- 文章栏目 -->
    <div class="form-group">
        <label for="column">栏目</label>
        <select class="form-control" 
                id="column" 
                name="column"
        >
                <option value="none">请选择栏目..</option>
            {% for column in columns %}
                <option value="{{ column.id }}"
                    {% if column.id == article.column.id %}
                        selected
                    {% endif %}
                >
                    {{ column }}
                </option>
            {% endfor %}
        </select>
    </div>

    <!-- 文章正文 -->
    ...

    <!-- 提交按钮 -->
    ...
</form>

...

与前面稍有不同的是,表单中判断了column.idarticle.column.id是否相等,如果相等则将其设置为默认值。

然后修改视图函数:

article/views.py

# 更新文章
...
def article_update(request, id):
    ...

    # 判断用户是否为 POST 提交表单数据
    if request.method == "POST":
        ...
        if article_post_form.is_valid():
            ...

            # 新增的代码
            if request.POST['column'] != 'none':
                article.column = ArticleColumn.objects.get(id=request.POST['column'])
            else:
                article.column = None

            ...

    else:
        ...

        # 新增及修改的代码
        columns = ArticleColumn.objects.all()
        context = { 
            'article': article, 
            'article_post_form': article_post_form,
            'columns': columns,
        }

        ...

代码逻辑与前面很类似。修改文章的栏目功能,也就完成了。

总结

本章实现了简单的栏目功能,可以舒舒服服对文章进行分类了,强迫症福音啊。

还有些可以完善的工作,比如:

  • 单击栏目按钮显示所有相同栏目的文章。这个功能与之前学过的最热文章排序以及搜索文章非常的类似。还记得filter()方法吗?稍微麻烦点的地方是你需要考虑栏目、搜索、排序三者的联合查询,因此可能会对原有代码结构做适当的调整。

实在不知道如何去实现的同学,请查看教程代码文章列表的视图和模板,答案就在里面。

  • 栏目Model的增删改查。

如果你不想写增删改查,用后台管理栏目是完全可以的。

以上内容就不在这里赘述了。留给读者去尝试实现。




本文作者: 杜赛
发布时间: 2019年01月29日 - 23:38
最后更新: 2019年01月30日 - 21:57
知识共享许可协议   转载请保留原文链接及作者


登录 后回复

共有25条评论

avatar
流天 么么哒! 3

create.html代码对过没错,但是create.html栏目的下拉框没显示具体栏目。

update.html那边却可以显示。

网页查看源码,都是用{% for column im columns %}提取column,create.html那边却没有显示。

7个月前 回复


avatar
杜赛 [博主] 流天 么么哒! 3

你有拼写错误。是 im 还是 in 

在模板中把column打印出来看看。示例:

create.html

...

{% for column in columns %}
<p>{{ column }}</p>
{% endfor %}

...

或者在视图中把columns打印出来,在命令行中查看。示例:

views.py

...

def create(...):
    ...
    print(columns)
    ...

 

7个月前 回复


avatar
流天 杜赛 [博主] 么么哒! 3

没有拼写错误,只是在评论里打错了。

7个月前 回复


avatar
流天 杜赛 [博主] 么么哒! 3

谢谢博主,之前是在shell里面查看。忘了还有这种方法

7个月前 回复


avatar
杜赛 [博主] 流天 么么哒! 3

找到bug了吗?wink

7个月前 回复


avatar
流天 杜赛 [博主] 么么哒! 3

想问下博主,我的编辑器用的是ckeditor,所以侧边栏是用锚点的方式跟ckeditor中的大小标题还有,列表绑定吗?因为我看博主的博客前端好像有editor.md编辑。

7个月前 回复


avatar
流天 杜赛 [博主] 么么哒! 3

嗯,找到了。比我那个方法好用。

7个月前 回复


avatar
杜赛 [博主] 流天 么么哒! 3

我没用过ckeditor写博客,所以没研究过如何去实现它的目录。

不过我看ckeditor有提供生成目录的插件:官方下载页面

你可以花功夫研究一下~

我的博客以前确实用的editor.md,但是这个作者已经很久没更新了,很多小问题,所以现在逐渐由editor.md转向python内置md了。

7个月前 回复


avatar
流天 杜赛 [博主] 么么哒! 3

yes十分给力。

7个月前 回复


avatar
天青色 流天 么么哒! 3

请教楼主,我也遇到了相同问题,新手,没找到问题出哪了,望提点下smiley

5个月前 回复


avatar
pidada 流天 么么哒! 3

请问你是怎么解决的呢?我也是下拉栏中没有显示具体的栏目

5个月前 回复


avatar
sys0613 pidada 么么哒! 3

“改已有的写文章视图article_create(),让其能够处理表单上传的栏目数据:”

原因是这里,博主的博文是先说下拉的效果了,然后是这段,实际在这里才传入columns参数的。把这句话搜到,把里面的内容做完,就能看到下拉框里面有内容了。

3个月前 回复


avatar
pidada 么么哒! 3

博主,教程很详细!一路学习下来有个感觉:就是看博客中前端代码的时候,不是很清晰修改的位置,感觉好模糊

有个叫“追梦人物”的博主也写过django的博文,可以参看下他的写法!仅仅是个人看法smiley还是给博主大大赞yes

5个月前 回复


avatar
杜赛 [博主] pidada 么么哒! 3

追梦人物的教程我看过一部分,写得很好,我博客分页的代码就参考了这个博主。教程开篇也提到了。

我会尽量把模板部分写得简洁清楚。说实话不是很容易,因为“简洁”和“清楚”本身就是矛盾的,我再找找平衡吧。

感谢反馈!

5个月前 回复


avatar
pidada 杜赛 [博主] 么么哒! 3

anyway  给博主点赞!最近看博主的文章很有感触!yes

5个月前 回复


avatar
杜赛 [博主] pidada 么么哒! 3

5个月前 回复


avatar
ac1864 pidada 么么哒! 3

前端好难啊。

学不会。

5个月前 回复


avatar
sys0613 pidada 么么哒! 4

我也是从头跟过来的,其实细心点,从头跟过来,每个前端修改处其实都是特别清晰的,博主会给出前后的原内容,然后插入。可能有些地方博主认为无关紧要,就直接写要添加的内容了。这些地方可能需要有一点点点点,就那么一丢丢前端基础就能看明白了。例如博主写的修改已有的写文章视图article_create()

if article_post_form.is_valid():
            ...

            # 新增的代码
            if request.POST['column'] != 'none':
                new_article.column = ArticleColumn.objects.get(id=request.POST['column'])

            # 已有代码
            new_article.save()
            ...

其实这里的第一个新增应该是放到

if article_post_form.is_valid():
    #保存数据,但暂时不提交到数据库中
    new_article=article_post_form.save(commit=False)  我是一
    if request.POST['column']!='none':
        new_article.column=ArticleColumn.objects.get(id=request.POST['column'])
    #指定目前登录的用户为作者
    new_article.author=User.objects.get(id=request.user.id)
    #将新文章保存到数据库中
    new_article.save()  我是二

一和二 中间的 任何一段,不能早了,也不能靠后了。

这里需要有些基础才知道吧。我感觉可能因为这些,导致你说博主的前端描述不清楚了。

3个月前 回复


avatar
杜赛 [博主] sys0613 么么哒! 4

哈哈,谢谢帮忙解答。我以后还是再写详细一些吧。但太细又会很啰嗦,很难把握

3个月前 回复


avatar
ac1864 么么哒! 4

好不容易过关了。

但是,外观和老大的不太一样。

特别是栏目的高亮没有。

几乎把代码覆盖了一遍,还是一样的。

5个月前 回复


avatar
github653224 么么哒! 4

 else:
        ...

        # 新增及修改的代码
        columns = ArticleColumn.objects.all()
        context = { 
            'article': article, 
            'article_post_form': article_post_form,
            'columns': columns,
        }

博主这里有bug吧,article_post_form 变量作用域不对吧

4个月前 回复


avatar
杜赛 [博主] github653224 么么哒! 3

时间长了有点忘了。好像这些form实际上没有用到。

4个月前 回复


avatar
sys0613 杜赛 [博主] 么么哒! 3

是的,这个实际没有用到,在前端是单独写的form了。这个不传也没事。之前博主给我解答过了,我一直没传。效果一样。

3个月前 回复


avatar
wangycgood 么么哒! 3

python新手mark一下,selected下拉菜单无数据折腾了一天加一上午,最后发现在视图编写时误将columns写成了column,细心真的很重要。

学习之路还很漫长,看公司的大佬写devops平台都是前后端分离的,只有后端代码,基本很难看懂。

非常感谢博主的教程,受益匪浅,祝博主步步高升,事事顺利!

3个月前 回复


avatar
杜赛 [博主] wangycgood 么么哒! 3

谢谢。

前后端分离是趋势,但是对新手来说,还是从MTV学起比较要有成就感一些

3个月前 回复