【问题标题】:How to set ForeignKey in CreateView?如何在 CreateView 中设置 ForeignKey?
【发布时间】:2012-05-10 02:28:02
【问题描述】:

我有一个模型:

class Article(models.Model):
    text = models.CharField()
    author = models.ForeignKey(User)

如何编写基于类的视图来创建新模型实例并将author 外键设置为request.user

更新:

解决方案移至下方单独的answer

【问题讨论】:

  • 绝对是最好的解决方案!

标签: python django django-class-based-views


【解决方案1】:

您应该使用ModelForm 为该模型设置CreateView。在表单定义中,将ForeignKey 设置为具有HiddenInput 小部件,然后在视图上使用get_form 方法来设置用户的值:

forms.py:

from django import forms

class ArticleForm(forms.ModelForm):
    class Meta:
        model = Article
        widgets = {"user": forms.HiddenInput()}

views.py:

from django.views.generic import *
from myapp.forms import ArticleForm
from myapp.models import Article

class NewArticleView(CreateView):
    model = Article
    form_class = ArticleForm
    def get_form(self, form_class):
        initials = {
            "user": self.request.user
        }
        form = form_class(initial=initials)
        return form

【讨论】:

  • 我试过这段代码,但它没有通过用户查看,我无法保存新文章(因为需要user fiedl)。相反,我在 CBV 中使用了 form_valid 方法(请参阅有问题的更新。无论如何,感谢您有时间提供帮助!
  • 我不确定您所说的“不让用户查看”是什么意思?它传递用户的 ID 并放入隐藏字段“用户”中。如果您需要视图中其他位置的用户(即模板),它已经在 request 变量中。
  • 我再次尝试了您的代码:我在页面源中找不到任何隐藏的输入(csrf 令牌除外)。为什么你认为这个字段会是用户的 ID?为什么不用用户名?
  • 或者这只是 CBV 问题?见stackoverflow.com/questions/907858/…
  • 该死,我的代码错了。现在修好了。注意方法名称中的错误。
【解决方案2】:

berislav 在views.py 中的代码对我不起作用。表单按预期呈现,用户值在隐藏输入中,但表单没有保存(我不知道为什么)。我尝试了一种稍微不同的方法,它对我有用:

views.py

from django.views.generic import *
from myapp.forms import ArticleForm
from myapp.models import Article

class NewArticleView(CreateView):
    model = Article
    form_class = ArticleForm
    def get_initial(self):
        return {
            "user": self.request.user
        }

【讨论】:

  • 应该是'get_initial'吗?
  • 让我们看看,如果我修改 HTML 代码并在表单中注入任何用户 id 会怎样,您如何使用您的方法防止此类攻击?
  • @RichardCorden 是对的,它是get_initial(没有s
【解决方案3】:

我通过覆盖form_valid 方法解决了这个问题。这是澄清事情的详细风格:

class CreateArticle(CreateView):
    model = Article

    def form_valid(self, form):
        article = form.save(commit=False)
        article.author = self.request.user
        #article.save()  # This is redundant, see comments.
        return super(CreateArticle, self).form_valid(form)

但我们可以缩短(感谢 dowjones123),这个案例是 mentioned in docs.:

class CreateArticle(CreateView):
    model = Article

    def form_valid(self, form):
        form.instance.author = self.request.user
        return super(CreateArticle, self).form_valid(form)

【讨论】:

  • +1 表示做得正确。使用 HiddenInput 或设置首字母的所有其他方法都存在潜在的安全风险。
  • 这并不理想,因为它会导致表单的save 方法被调用两次。这是您最好的选择是重新实现 super 方法的其余部分而不是委托的情况之一。换句话说,将self.object 设置为新实例,然后重定向到self.get_success_url()
  • @orokusaki 你是完全正确的。我查看了源代码,发现form_valid 除了保存(已经验证的)表单实例之外什么也没做。所以我们可以安全地删除supersave 行。后者更好,因为我们不需要考虑success_url(否则我们需要明确设置它)。更正了答案。
  • @VladT。它在技术上仍然不正确,因为表单的保存方法仍然被调用两次(第一次使用commit=False),并且表单的保存方法中可能包含您可能不想运行两次的逻辑(甚至只是日志记录) .如果您添加回article.save() 并将super(...) 行替换为return redirect(self.get_success_url()),那将是最正确的解决方案。
  • 一种更有效的方法(观点可能不同)是将form_valid的前两行替换为form.instance.author = self.request.user
【解决方案4】:

我只是偶然发现了这个问题,这个帖子引导我朝着正确的方向前进(谢谢!)。基于this Django documentation page,我们完全可以避免调用表单的save()方法:

class CreateArticle(LoginRequiredMixin, CreateView):
    model = Article

    def form_valid(self, form):
        form.instance.author = self.request.user
        return super(CreateArticle, self).form_valid(form)

【讨论】:

    猜你喜欢
    • 2020-10-31
    • 2013-08-19
    • 2018-04-21
    • 2022-08-13
    • 2013-09-08
    • 2016-09-18
    • 1970-01-01
    • 1970-01-01
    • 2015-04-07
    相关资源
    最近更新 更多