Django搭建个人博客:完成修改文章功能

3623阅读 · 38评论 · 2018/10/20发布   前往评论

目前为止我们已经完成了文章的新建、删除以及查看,还剩最后一项,即对已经完成的文章进行修改。

实际上修改文章与新建文章有点类似,不同的地方有两点:

  • 修改是在原有文章的基础上,因此需要传递 id 指明具体需要修改的文章
  • 加载页面时需要将旧的内容作为默认值填写到表单中,因此需要将文章对象传递到html

按照这个思路,接下来先写视图函数。

视图函数

ariticle/views.py中增加修改文章的视图函数article_update()

article/views.py

...

# 更新文章
def article_update(request, id):
    """
    更新文章的视图函数
    通过POST方法提交表单,更新titile、body字段
    GET方法进入初始表单页面
    id: 文章的 id
    """

    # 获取需要修改的具体文章对象
    article = ArticlePost.objects.get(id=id)
    # 判断用户是否为 POST 提交表单数据
    if request.method == "POST":
        # 将提交的数据赋值到表单实例中
        article_post_form = ArticlePostForm(data=request.POST)
        # 判断提交的数据是否满足模型的要求
        if article_post_form.is_valid():
            # 保存新写入的 title、body 数据并保存
            article.title = request.POST['title']
            article.body = request.POST['body']
            article.save()
            # 完成后返回到修改后的文章中。需传入文章的 id 值
            return redirect("article:article_detail", id=id)
        # 如果数据不合法,返回错误信息
        else:
            return HttpResponse("表单内容有误,请重新填写。")

    # 如果用户 GET 请求获取数据
    else:
        # 创建表单类实例
        article_post_form = ArticlePostForm()
        # 赋值上下文,将 article 文章对象也传递进去,以便提取旧的内容
        context = { 'article': article, 'article_post_form': article_post_form }
        # 将响应返回到模板中
        return render(request, 'article/update.html', context)

更新的视图与创建文章非常相似,但又有点小区别:

  • 文章的 id 作为参数传递进来了
  • 用户POST提交表单时没有创建新的文章,而是在之前的文章中修改
  • redirect函数没有返回文章列表,而是返回到修改后的文章页面去了,因此需要同时把文章的id也打包传递进去,这是url所规定的
  • GET获取页面时将article对象也传递到模板中去,以便后续的调用

编写模板

模板文件就与创建文章的更像了,不过我们这里还是重新写一遍。

新建templates/article/update.html并写入:

templates/article/update.html

{% extends "base.html" %} {% load staticfiles %}
{% block title %} 更新文章 {% endblock title %}
{% block content %}
<div class="container">
    <div class="row">
        <div class="col-12">
            <br>
            <form method="post" action=".">
                {% csrf_token %}
                <div class="form-group">
                    <label for="title">文章标题</label>
                    <!-- 在 value 属性中指定文本框的初始值为旧的内容,即 article 对象中的 title 字段 -->
                    <input type="text" class="form-control" id="title" name="title" value="{{ article.title }}">
                </div>
                <div class="form-group">
                    <label for="body">文章正文</label>
                    <!-- 文本域不需要 value 属性,直接在标签体中嵌入数据即可 -->
                    <textarea type="text" class="form-control" id="body" name="body" rows="12">{{ article.body }}</textarea>
                </div>
                <button type="submit" class="btn btn-primary">完成</button>
            </form>
        </div>
    </div>
</div>
{% endblock content %}

在模板中,分别将文章旧的标题和正文作为初始值,传递了进去,其他就与新建文章的模板完全没区别了。

有读者可能就会问了,既然这两个函数、模板都很相似,能不能合并成一个函数、模板呢?当然是可以的,合并相同功能的函数可以让代码更加简洁漂亮,也便于后期的维护。有兴趣的读者可以自己尝试一下。

URL 和入口

接下来的套路都懂的,配置路由article/urls.py

article/urls.py

...

urlpatterns = [
    ...

    # 更新文章
    path('article-update/<int:id>/', views.article_update, name='article_update'),
]

在文章详情页面tempaltes/article/detail.html中添加修改文章的入口:

tempaltes/article/detail.html

...
<div class="col-12 alert alert-success">作者:{{ article.author }}
    · <a href="#" onclick="confirm_delete()">删除文章</a>
    · <a href="{% url "article:article_update" article.id %}">编辑文章</a>
</div>

启动服务器,可以看到修改文章的功能就实现了。同样的,如有故障也不要着急,在Debug页面寻找出错的线索,求助网络帮忙解决吧。

总结

至此我们就实现了一篇文章的增、删、改、查四个基础功能,也算小有成就。

当然还有很多进阶的功能可以去做,不过我们在这里先休息休息,来罐快乐水庆祝一下。

下一章开始解决更加燃眉之急的内容:用户管理。




本文作者: 杜赛
发布时间: 2018年10月20日 - 22:19
最后更新: 2019年01月27日 - 19:39
知识共享许可协议   转载请保留原文链接及作者


登录 后回复

共有38条评论

avatar
Superb来了 么么哒! 3

关于初始化表单的旧内容,form类提供了一个instance方法哦,让instance=blog就能让表单基于这篇文章进行操作,像下面这样:

 def edit_page(request,artical_id):
    artical = BlogPost.objects.get(id=artical_id) 
    if request.method == 'POST': 
        form = BlogPostForm(request.POST,instance=artical) 
        if form.is_valid(): form.save() 
        return HttpResponseRedirect('/blog/') 
    else: 
        return HttpResponse('表单内容有误,请重新填写') 
    form = BlogPostForm(instance=artical) 
    return render(
        request, 'blog/edit_page.html', 
        {'form_title':form['title'].value(), 
        'form_body':form['body'].value(), 
        'artical_id':artical_id})

 

10个月前 回复


avatar
杜赛 [博主] Superb来了 么么哒! 4

感谢指出。 这个方法我是知道的。相比直接在模板中取值,它有什么优点吗?求教

10个月前 回复


avatar
Superb来了 杜赛 [博主] 么么哒! 4

效果上是一样的,只不过代码看起来更简洁吧,我也是新手哈哈哈

10个月前 回复


avatar
杜赛 [博主] Superb来了 么么哒! 3

哈哈~互相学习

10个月前 回复


avatar
github653224 Superb来了 么么哒! 3

大家好我彻底好清楚了大家为什么报错的原因了,原因是因为,我们没有按照博主的写法对 app的url做了其他的名字命名,重定向那里是重定向到article_detail命名空间了,后面的传递的id参数是个键值对的,由于我的url改了和博主不一样,改成article_id了,所以重定向时,传入的key也应该是article_id, 另外对view中的id也是改成article_id了,但是我们把博主的代码复制或者照着打印过去后,就会报错

正确配置如下:

app_name = 'article'

urlpatterns = [
    path('article-list/', views.article_list, name='article_list'),

    path('article-detail/<int:article_id>/', views.article_detail, name='article_detail'),

    path('article-create/', views.article_create, name='article_create'),

    path('article-delete/<int:article_id>/', views.article_delete, name='article_delete'),

    path('article-update/<int:article_id>/', views.article_update, name='article_update'),

views.py

# 更新文章
def article_update(request, article_id):
    """
    更新文章的视图函数
    通过POST方法提交表单,更新titile、body字段
    GET方法进入初始表单页面
    id: 文章的 id
    """

    # 获取需要修改的具体文章对象
    article = ArticlePost.objects.get(id=article_id)
    # 判断用户是否为 POST 提交表单数据
    if request.method == "POST":
        # 将提交的数据赋值到表单实例中
        article_post_form = ArticlePostForm(data=request.POST)
        # 判断提交的数据是否满足模型的要求
        if article_post_form.is_valid():
            # 保存新写入的 title、body 数据并保存
            article.title = request.POST['title']
            article.body = request.POST['body']
            article.save()
            # 完成后返回到修改后的文章中。需传入文章的 id 值
            print('走到这一步了')
            print("article_id==>", article_id)
            return redirect("article:article_detail", article_id=article_id)
            # return redirect("article:article_list")
        # 如果数据不合法,返回错误信息
        else:
            return HttpResponse("表单内容有误,请重新填写。")

    # 如果用户 GET 请求获取数据
    else:
        print("进入表单实例")
        # 创建表单类实例
        article_post_form = ArticlePostForm()
        # 赋值上下文,将 article 文章对象也传递进去,以便提取旧的内容
        context = { 'article': article, 'article_post_form': article_post_form }
        # 将响应返回到模板中
        print('表单实例马上结束')
        return render(request, 'article/update.html', context)

 

4个月前 回复


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

yes解决了问题就好

4个月前 回复


avatar
Jenychen1996 么么哒! 3

兄弟,我又有问题了。

修改文章之后点击完成不会跳转到正常的页面。但是我按照步骤检查了,没有发现什么问题。下面是代码:

# 更新文章
def article_update(request, id):
    """
    更新文章的视图函数
    通过POST方法提交表单,更新titile、body字段
    GET方法进入初始表单页面
    id: 文章的 id
    """

    # 获取需要修改的具体文章对象
    article = ArticlePost.objects.get(id=id)
    # 判断用户是否为 POST 提交表单数据
    if request.method == "POST":
        # 将提交的数据赋值到表单实例中
        article_post_form = ArticlePostForm(data=request.POST)
        # 判断提交的数据是否满足模型的要求
        if article_post_form.is_valid():
            # 保存新写入的 title、body 数据并保存
            article.title = request.POST['title']
            article.body = request.POST['body']
            article.save()
            # 完成后返回到修改后的文章中。需传入文章的 id 值
            return redirect("article:article_detail", id=id)
        # 如果数据不合法,返回错误信息
        else:
            return HttpResponse("表单内容有误,请重新填写。")

    # 如果用户 GET 请求获取数据
    else:
        # 创建表单类实例
        article_post_form = ArticlePostForm()
        # 赋值上下文,将 article 文章对象也传递进去,以便提取旧的内容
        context = { 'article': article, 'article_post_form': article_post_form }
        # 将响应返回到模板中
        return render(request, 'article/update.html', context)

urls.py

# 更新文章
    path('article-update/<int:id>', views.article_update, name='article_update'),

update.html

{% extends "base.html" %} {% load staticfiles %}
{% block title %} 更新文章 {% endblock title %}
{% block content %}
<div class="container">
    <div class="row">
        <div class="col-12">
            <br>
            <form method="post" action=".">
                {% csrf_token %}
                <div class="form-group">
                    <label for="title">文章标题</label>
                    <!-- 在 value 属性中指定文本框的初始值为旧的内容,即 article 对象中的 title 字段 -->
                    <input type="text" class="form-control" id="title" name="title" value="{{ article.title }}">
                </div>
                <div class="form-group">
                    <label for="body">文章正文</label>
                    <!-- 文本域不需要 value 属性,直接在标签体中嵌入数据即可 -->
                    <textarea type="text" class="form-control" id="body" name="body" rows="12">{{ article.body }}</textarea>
                </div>
                <button type="submit" class="btn btn-primary">完成</button>
            </form>
        </div>
    </div>
</div>
{% endblock content %}

page not found

Page not found (404)
Request Method:	POST
Request URL:	http://127.0.0.1:8000/article/article-update/
Using the URLconf defined in my_blog.urls, Django tried these URL patterns, in this order:

admin/
article/ article-list/ [name='article_list']
article/ article-detail/<int:id>/ [name='article_detail']
article/ article-create/ [name='article_create']
article/ article-delete/<int:id> [name='article_delete']
article/ article-update/<int:id> [name='article_update']
userprofile/
password-reset/
^media/(?P<path>.*)$
The current path, article/article-update/, didn't match any of these.

You're seeing this error because you have DEBUG = True in your Django settings file. Change that to False, and Django will display a standard 404 page.

 

7个月前 回复


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

你的post提交到article/article-update/了,但是这个url实际不存在。

只是重定位有问题吗,文章修改是成功的吧?

7个月前 回复


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

看不到文章修改是否成功,每次修改完成点击完成就是跳转不过去,我只能回退,然后就造成无法保存

7个月前 回复


avatar
杜赛 [博主] Jenychen1996 么么哒! 2

暂时没看出来你这个问题在哪儿。

把redirect替换成别的页面,比如返回博客首页,问题是否还存在?

如果还存在,说明逻辑分支有问题;如果不存在了,说明确实重定向相关代码有错误,就仔细检查所有相关的地方。

7个月前 回复


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

我也遇到了这个问题,问题出在update.html的form标签的action属性里,根据官方文档,form的action需要指向一个url,

<form method="post" action="{% url 'article:article_update' article.id %}">

改成如此就解决问题了,我仔细看了一下create和update的view和html的区别,虽然我没明白action属性用‘.’表示什么(大概就是当前的url吧),但是从update的问题可以确定action的‘.’就是代表提交到当前的url,但是这个url是不存在于我们配置的urlpatterns中,所以出现404的错误

6个月前 回复


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

form表单的action属性指明数据提交的路径,可以是绝对路径也可以是相对路径。

相对路径的几种表示法:

  • .. 代表父目录
  • . 代表当前目录
  • / 以它开头代表根目录
  • / 不以它开头代表子目录

我测试的时候写action="."是没有问题的,因为当前路径的post方法在视图中是存在的。

6个月前 回复


avatar
pidada Jenychen1996 么么哒! 3

我今天也遇到了这个404,后来试了下面有个大神的方法,在form的action中加上url就OK;

然后我改成了博主的点,当前路径又可以了

很奇妙

6个月前 回复


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

我也遇到404错误了,找不到action-update这个路径,action加上这个url就好使了。博主,我想问下,只用“点”,修改以后点完成,为什么会好使呢??我感觉没有将id的值post回到action-update这个路径啊。匹配不上下面这个路径啊。

'article-update/<int:id>'

4个月前 回复


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

我在上面也回复了,“.”的意思是提交到当前页面,而当前页面的url中是含有id值的,所以按道理来说没有问题

刚才我又试了一下,我的代码中用“.”确实能够正常工作,就不太清楚为啥这里有问题的人比较多了..

4个月前 回复


avatar
sys0613 杜赛 [博主] 么么哒! 3
本回复已被 sys0613 删除

4个月前 回复


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

有可能是bootstrap版本的问题导致的?

我用4.3.1的版本,也存在这样的问题,修改提交时,并没有把 id 提交过去.

<form method="post" action="./{{ article.id }}">

这样就可以了. 

4个月前 回复


avatar
cnhotfire 杜赛 [博主] 么么哒! 5

我有来了。刚灵光一闪,知道这个为什么你可以,我们不行了。

问题在于 urls.py里面的配置

我原先是

path('serverupdate/<int:id>', views.server_update, name='server_update'),

url最后未加 / 

form 提交时action="." 取到的是 serverupdate/

后来改成 (您的文章里面是有加的)

path('serverupdate/<int:id>/', views.server_update, name='server_update'),

form 提交时action="." 取到的是 serverupdate/(id)

看来这个 / 很重要啊!

4个月前 回复


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

yes给你点赞,给后来者提供一个解决方案了

4个月前 回复


avatar
seekdoor 杜赛 [博主] 么么哒! 1

他的url配置的时候 article-update/<int:id> 这个结束的时候没有加/

我刚刚也是遇到这个问题,然后把/加上就好了。。。laugh

2个月前 回复


avatar
chenxiang567 么么哒! 3

Reverse for 'article_update' not found. 'article_update' is not a valid view function or pattern name.

你好,我按你的步骤以后出现上面的报错,这个函数article_update是不是写得有问题?

6个月前 回复


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

代码我跑通了再写的文章,应该没问题吧。

点击链接的时候报错吗?

跟函数没关系,报错提示没找到逆向解析地址。检查一下是不是命名空间写错了,或者有拼写错误。

6个月前 回复


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

# 你好,我也遇到了更新文章点击完成时找不到网页报404的错误,之前其他功能都是对的

# 更新文章
def article_update(request, article_id):
    """
    更新文章的视图函数
    通过POST方法提交表单,更新titile、body字段
    GET方法进入初始表单页面
    id: 文章的 id
    """

    # 获取需要修改的具体文章对象
    article = ArticlePost.objects.get(id=article_id)
    # 判断用户是否为 POST 提交表单数据
    if request.method == "POST":
        # 将提交的数据赋值到表单实例中
        article_post_form = ArticlePostForm(data=request.POST)
        # 判断提交的数据是否满足模型的要求
        if article_post_form.is_valid():
            # 保存新写入的 title、body 数据并保存
            article.title = request.POST['title']
            article.body = request.POST['body']
            article.save()
            # 完成后返回到修改后的文章中。需传入文章的 id 值
            print('走到这一步了')
            return redirect("article:article_detail", id=article_id)
        # 如果数据不合法,返回错误信息
        else:
            return HttpResponse("表单内容有误,请重新填写。")

    # 如果用户 GET 请求获取数据
    else:
        print("进入表单实例")
        # 创建表单类实例
        article_post_form = ArticlePostForm()
        # 赋值上下文,将 article 文章对象也传递进去,以便提取旧的内容
        context = { 'article': article, 'article_post_form': article_post_form }
        # 将响应返回到模板中
        print('表单实例马上结束')
        return render(request, 'article/update.html', context)

# 报错如下:

System check identified no issues (0 silenced).                                                        
July 06, 2019 - 16:16:33                                                                               
Django version 2.2.2, using settings 'my_blog.settings'                                                
Starting development server at http://0.0.0.0:9090/                                                    
Quit the server with CTRL-BREAK.                                                                       
走到这一步了                                                                                                 
Internal Server Error: /article/article-update/8/                                                      
Traceback (most recent call last):                                                                     
  File "D:\Python\Envs\django2.1_blog_env\lib\site-packages\django\core\handlers\exception.py", line 34
ner                                                                                                    
    response = get_response(request)                                                                   
  File "D:\Python\Envs\django2.1_blog_env\lib\site-packages\django\core\handlers\base.py", line 115, in
esponse                                                                                                
 。。。。。。
  File "D:\Python\Envs\django2.1_blog_env\lib\site-packages\django\urls\base.py", line 90, in reverse  
    return iri_to_uri(resolver._reverse_with_prefix(view, prefix, *args, **kwargs))                    
  File "D:\Python\Envs\django2.1_blog_env\lib\site-packages\django\urls\resolvers.py", line 668, in _re
ith_prefix                                                                                             
    raise NoReverseMatch(msg)                                                                          
django.urls.exceptions.NoReverseMatch: Reverse for 'article_detail' with keyword arguments '{'id': 8}' 
nd. 1 pattern(s) tried: ['article/article\\-detail/(?P<article_id>[0-9]+)/$']                          
[06/Jul/2019 16:17:16] "POST /article/article-update/8/ HTTP/1.1" 500 110955

 

4个月前 回复


avatar
台风利齐马 chenxiang567 么么哒! 2

我也遇到过这样的

不过我都是再仔细核对我的代码和博主有什么区别

最总都是我的代码 敲得 有点问题,不是单词拼错了,就是少些标点,

仔细点 都能查到原因

3个月前 回复


avatar
1581779395@qq.com chenxiang567 么么哒! 1

这位朋友的问题解决的了吗?

2个月前 回复


avatar
ac1864 么么哒! 2

灌水,灌水。

删减都可以搞定了。

谢谢作者。

5个月前 回复


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

学得很快啊。加油

5个月前 回复


avatar
sys0613 么么哒! 4

博主,我想问下,每次用get方法,将article_post_form传递到了模板,但是没看到模板使用这个article_post_form呢,这个回传到模板以后,怎么用的article_post_form呢?谢谢

# 赋值上下文,将 article 文章对象也传递进去,以便提取旧的内容
        context = { 'article': article, 'article_post_form': article_post_form }

4个月前 回复


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

这个确实是我疏忽了,get方法传递的表单实际上是没有使用的(某章节已经勘误了)。

传递这个表单的作用是可以用模板语言快速渲染。使用方法像这样:

<form action="..." method="post">
    {% csrf_token %}
    {{ form }}
    <input type="submit" value="Submit" />
</form>

除此之外还有更多的渲染样式:

  • {{ form.as_table }} 将表单渲染成表格元素
  • {{ form.as_p }} 将表单的输入框包裹在<p>标签内
  • {{ form.as_ul }} 将表单渲染成一个列表元素

教程中没用这种自动渲染的原因是它的样式不好看(你可以自己试一下),我们要自己引用Bootstrap的好看的表单,所以就手动渲染的所有表单字段。

教程中传递了这个纯属写顺手了..

4个月前 回复


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

收到,非常感谢。

4个月前 回复


avatar
MFullStack 么么哒! 2

已学习,感谢。

3个月前 回复


avatar
1581779395@qq.com 么么哒! 1

博主你好

    我的学习方法是看文章、复制代码。走流程。

    可是昨天我手痒痒手动撸了几行代码。然后报了一些错误,都是在我手写代码的地方掉坑的

    不知道我这种方法是否可行。反正这个教程我没准备一遍学会。在web0基础的情况下,我觉得还是先淌水。

    谢谢博主的细心。以后我也会细心学习。成为博主一样优秀的人。

2个月前 回复


avatar
杜赛 [博主] 1581779395@qq.com 么么哒! 2

怎么学都是可以的,关键在坚持。

写代码也是熟能生巧的过程。第一次自己写10行代码,9行有bug很正常的。在反复错误中进化为高级程序员。

2个月前 回复


avatar
1581779395@qq.com 杜赛 [博主] 么么哒! 2

好的,我会继续努力的

2个月前 回复


avatar
杜赛 [博主] 1581779395@qq.com 么么哒! 1

哈哈,感谢打赏。

加油学吧!

2个月前 回复


avatar
sqc 么么哒! 2

500错误是因为啥呢,检查了好久- -,再检查不出来要从头开始了

21天前 回复


avatar
杜赛 [博主] sqc 么么哒! 1

这里怎么会报 500 错误?

21天前 回复


avatar
sqc 杜赛 [博主] 么么哒! 1

大佬你竟然回复了!!不知道,很难受,自己再看看吧,这个错误找起来头都肿了。

21天前 回复