【问题标题】:Django: how to re-create my function-based view as a (Generic Editing) Class Based ViewDjango:如何将我的基于函数的视图重新创建为(通用编辑)基于类的视图
【发布时间】:2021-02-04 23:08:30
【问题描述】:

我有一个基于函数的视图,目前正在成功运行。但是,我想学习如何使用通用的 UpdateView 类创建此函数的等效基于类的视图版本——尽管我认为无论是什么解决方案,对于 CreateView 来说都是完全相同的。

我一般都知道如何创建和使用基于类的视图,但是我的基于函数的视图中有一行我无法在相应的 UpdateView 中工作——像往常一样使用通用编辑基于类的视图,目前尚不清楚我需要重写哪种方法来插入所需的功能。

可以说,我无法移植到 CBV 的特定任务是覆盖将用于显示特定字段的查询集的一行,该字段被定义为另一个模型的 ForeignKey在我的数据库中。

首先,基于工作函数的视图,在 CVB 版本中无法工作的特定代码位突出显示:

@login_required
def update_details(request, pk):
    """update details of an existing record"""
    umd_object = UserMovieDetail.objects.select_related('movie').get(pk=pk)

    movie = umd_object.movie

    if umd_object.user != request.user:
        raise Http404

    if request.method != 'POST':
        form = UserMovieDetailForm(instance=umd_object)

        # this is the single line of code I can't get working in UpdateView version:

        form.fields['user_guess'].queryset = User.objects.filter(related_game_rounds=movie.game_round)

    else:
        form = UserMovieDetailForm(instance=umd_object, data=request.POST)
        if form.is_valid():
            form.save()

            return redirect(movie)

    context = {'form': form, 'object': umd_object }
    return render(request, 'movies/update_details.html', context)

我可以在 UpdateView 中成功地重新创建这个基于函数的视图的每个部分除了这行(为了清楚起见,从上面复制):

    form.fields['user_guess'].queryset = User.objects.filter(related_game_rounds=movie.game_round)

这一行的作用:ForeignKey 的默认表单字段是 ModelChoiceField,默认显示相关模型的 所有 对象。我上面的代码覆盖了这种行为,并说:我只希望表单显示这组过滤的对象。只要我使用这个基于函数的视图,它就可以正常工作。

旁注: 我知道可以通过在我的 forms.py 文件中修改 ModelForm 本身来实现此结果。这个问题的目的是更好地理解如何使用内置的基于通用类的视图,使它们能够重新创建我已经可以使用基于函数的视图实现的功能。因此,请不要用“为什么不直接在表单本身中执行此操作”来回答我的问题——我已经知道这个选项,具体而言,这不是我要解决的问题。

现在是 UpdateView(我认为 CreateView 也是如此)。首先,它基本上看起来像这样:

class UpdateDetailsView(LoginRequiredMixin, UpdateView):
    model = UserMovieDetail
    template_name = 'movies/update_details.html'
    form_class = UserMovieDetailForm

    login_url = 'login' # used by LoginRequiredMixin

    # what method do I override here, to include that specific line of code, which needs
    # to occur in the GET portion of the view?    

    def get_success_url(self):
        return reverse('movies:movie', kwargs={'pk': self.object.movie.pk, 'slug': self.object.movie.slug })

以上是我基于函数的视图的重新创建,复制了所有行为除了过滤特定字段的 ModelChoiceField 显示在表单中的结果的重要行。 p>

如何让该行代码在此 UpdateView 中运行?我在 classy 基于类的视图 网站上查看了 UpdateView 内置的方法,然后尝试(通过纯粹的猜测)覆盖 get_form_class方法,但我没有用,我一开始基本上是在黑暗中拍摄。

请注意,由于我要重新创建的功能是关于在表单的 ModelChoiceField 中显示项目,因此所需的行为适用于视图的 GET 部分,而不是 POST。所以我需要能够在第一次呈现表单之前覆盖表单字段,就像我在基于函数的视图中所做的那样。在 UpdateView 中的何处以及如何执行此操作?

【问题讨论】:

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


    【解决方案1】:

    首先,与表单无关的注释 - 来自raise Http404 在功能视图中我了解您希望允许用户仅访问他自己的电影。对于基于类的视图,您可以覆盖 get_queryset 方法:

    class UpdateDetailsView(LoginRequiredMixin, UpdateView):
        def get_queryset(self):
            return UserMovieDetail.objects \
                .filter(user=request.user) \
                .select_related('movie')
    

    现在让我们开始自定义表单。

    选项 1 - .get_form()

    您可以覆盖 UpdateView 的 get_form 方法:

    class UpdateDetailsView(LoginRequiredMixin, UpdateView):
        form_class = UserMovieDetailForm
    
        def get_form(self, form_class=None):
            form = super().get_form(form_class)
            # add your customizations here
            round = self.object.movie.game_round
            form.fields['user_guess'].queryset = \
                User.objects.filter(related_game_rounds=round)
            return form
    

    选项 2 - 将自定义项移动到表单类和 .get_form_kwargs()

    您可能更喜欢将自定义逻辑从视图移动到表单。为此,您可以覆盖表单的 __init__ 方法。如果自定义逻辑需要额外的信息(例如,queryset 依赖于当前用户),那么您也可以重写get_form_kwargs 方法以将额外的参数传递给表单:

    # views.py
    class UpdateDetailsView(LoginRequiredMixin, UpdateView):
        form_class = UserMovieDetailForm
    
        def get_form_kwargs(self):
            kwargs = super().get_form_kwargs()
            kwargs.update({'current_user': self.request.user})
            return kwargs
    
    
    # forms.py
    class UserMovieDetailForm(forms.ModelForm):
        def __init__(self, *args, **kwargs):
            self.current_user = kwargs.pop('current_user')
            super().__init__(*args, **kwargs)
            # add your customizations here
            self.fields['user_guess'].queryset = ...
    

    附:一般来说,了解基于 django 类的视图的重要资源是 https://ccbv.co.uk/

    【讨论】:

    • @m.arthur - 我更新了答案,请看一下
    • 谢谢你,很棒的东西。在 CBV 中覆盖 get_form 正是我所缺少的,但其余部分也很有用。我可以问你,为什么 get_form 方法中的参数 form_class=None ?而在覆盖这个方法的时候,还需要把form赋给form_class属性吗?
    • 不确定为什么需要 form_class 参数,正如我在 django 代码中看到的那样,get_form 总是在没有参数的情况下调用。 RE 表单类 - 如果您调用 super().get_form(),则需要传递它。如果您不调用 super() 而是显式实例化表单,则不需要 form_class。
    猜你喜欢
    • 2021-05-30
    • 2014-02-16
    • 1970-01-01
    • 2021-01-24
    • 2012-10-23
    • 2014-08-07
    • 1970-01-01
    • 2023-03-06
    相关资源
    最近更新 更多