在Django中删除对象

23

在一个迷你博客应用中,我想创建一个删除功能,以便博客的所有者可以删除他的文章(仅限他自己的文章)。 我猜唯一的方法是使用一个表单。 虽然我的删除代码似乎很清晰和正确,但它并不能工作。 我的代码:

def delete_new(request,id):
   u = New.objects.get(pk=id).delete()
   if request.method == 'POST':
       form = DeleteNewForm(request.POST)    
       form.u.delete()             
       form.save()   
   return render_to_response('news/deleteNew.html', {
           'form': form,
           }, 
        context_instance=RequestContext(request)) 

并在模板中:

<a href='/news/delete_new/{{object.id}}/'> Delete</a> <br /> 

这种方式正确吗?我的意思是,为此创建一个表单? 而且,获取与删除链接相关联的博客文章的唯一方法就是将id作为参数。这样做合适吗?我的意思是,也许任何用户都可以在url中键入另一个id并删除另一个条目(最终可能不是他自己的)

2个回答

35

如果不使用表单,您的应用程序将容易受到CSRF攻击。在检查请求是GET还是POST之前删除模型也会造成漏洞。

创建一个简单的ModelForm

from django import forms

from .models import New

class DeleteNewForm(forms.ModelForm):
    class Meta:
        model = New
        fields = []
在同一Django应用程序中的views.py文件中:
from django.shortcuts import render, get_object_or_404

from .forms import DeleteNewForm
from .models import New

def delete_new(request, new_id):
    new_to_delete = get_object_or_404(New, id=new_id)
    #+some code to check if this object belongs to the logged in user

    if request.method == 'POST':
        form = DeleteNewForm(request.POST, instance=new_to_delete)

        if form.is_valid(): # checks CSRF
            new_to_delete.delete()
            return HttpResponseRedirect("/") # wherever to go after deleting

    else:
        form = DeleteNewForm(instance=new_to_delete)

    template_vars = {'form': form}
    return render(request, 'news/deleteNew.html', template_vars)

8
为了解释为什么这是最佳选择:在任何情况下,提交、更新和删除都必须使用POST(或PUT)请求。GET请求可以伪装成看起来无害的链接。比如说,如果一个大型网站在example.com/account/delete/上有一个简单的“删除我的所有内容”,我可以将这样的链接隐藏在博客文章或类似TinyURL的服务中。当你访问时,会看到一个漂亮的“您的个人资料已被删除。”页面。像Wilfred Hughes的混合方法一样,在GET请求上显示带有CSRF token的确认页面。 POST请求会验证CSRF,然后最终删除所请求的资源。 - sleblanc

21

一般情况下,删除对象应使用POST(或DELETE)HTTP方法

如果您真的想在示例中使用HTTP GET,那么这就是您需要修复的内容:

如果您有指向像这样的URL的URL:<a href='/news/delete_new/{{object.id}}/'> Delete</a>,则可以编写视图来检查对象是否属于已登录的用户,并在是时删除此条目,就像您已经编写的代码一样。

def delete_new(request,id):
   #+some code to check if New belongs to logged in user
   u = New.objects.get(pk=id).delete()
要检查新对象是否属于某个用户,您需要在New模型中创建一个与User相关的关系(例如created_by = models.ForeignKey(User))。
您可以通过以下方式获取已登录的用户:request.user 我希望我正确理解了您的问题,并且我的答案对您有所帮助。
PS:您也可以考虑使用{% url %}标签,而不是直接在模板中编写URL。

1
它可以顺畅地运行,只需要一行代码: u = New.objects.filter(created_by=request.user).get(pk=id).delete() 谢谢! :) - dana
没错,如果给定用户没有这样的新对象,您可以添加一些错误处理并显示漂亮的错误消息(使用您的一行代码会大声失败并显示500错误)。但基本上这就是您在此处需要做的全部 :) 很高兴我的答案对您有帮助。 - dzida
24
我强烈建议使用表单和检查POST方式,因为GET方式访问页面不应更改服务器上的状态。(尽管在实践中这里发生的事情相对“安全”) - Steve Jalim
14
如果这样做,您的网站将面临CSRF攻击,因为没有传输/需要令牌。 - Gelatin
4
这是不安全的,会导致 CSRF 攻击,使用户能够删除属于其他用户的内容。 - Natan Yellin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接