Django:从CBV表单视图传递数据以形成CBV

时间:2017-04-05 17:22:04

标签: django

我有Form ModelChoiceFieldform_class用作FormView

必须使用绑定到request对象的信息填充选择字段。

总结一下:

class MyFormView(FormView):
    # I need to pass `request.user` and a value 
    # derived from `request.GET['pk']` to the form
    form_class = MyForm

class MyForm(Form):
    choices = ModelChoiceField(queryset=MyChoice.objects.none())

    def __init__(self, user, number, *args, **kwargs):
        super(MyForm, self).__init__(*args, **kwargs)
        self.fields['choices'] = MyChoice.objects(number=number, owner=user)

在创建实例时,我需要做什么才能将数据传递给表单?

我尝试覆盖get_form,但我不确定这是否是正确的方法:

 def get_form(self, form_class):
        user = self.request.user
        number = SomeModel.objects.get(self.GET['pk']).number
        return form_class(user, number, **self.get_form_kwargs())

2 个答案:

答案 0 :(得分:2)

覆盖get_form会有效,但更好的方法是覆盖get_form_kwargs,这样您就不必重复get_form方法中的代码。

class MyFormView(FormView):
    form_class = MyForm

    def get_form_kwargs(self):
        kwargs = super(MyFormView, self).get_form_kwargs()
        kwargs['user'] = self.request.user
        kwargs['number'] = SomeModel.objects.get(self.GET['pk']).number
        return kwargs

答案 1 :(得分:0)

我知道这是一个很长的答案; 相信我,最终结果是 很棒

首先,我想传达的是,不需要具有辅助文件,例如 forms.py,tables.py和filters.py 正如大多数django教程所建议的。

Django随附了包括的电池;当我们运行 django-admin startapp 时,它将为我们创建所有必需的文件( admin,应用程序,模型,测试,视图)。

第二,我想说不需要可以创建单独的表单类。 我清楚地了解,必须使用Form类来自定义我们的表单;我只是说我们不需要明确定义它们

Django强调 DRY(不要重复自己)原则。
如果您有机会浏览Django的源代码,您将发现重复使用了python内置的 Type function (Link to Official Documentation) 的特殊变体。

最后,在分享解决方案之前,我希望您观看以下视频,因为它可以帮助您理解此解决方案中使用的概念。

  1. First Class Functions
  2. Closures

现在是 8行解决方案的时间(自从我进行了广泛的评论以来,时间似乎更长了)

class MyFormView(FormView):
    # I need to pass `request.user` and a value 
    # derived from `request.GET['pk']` to the form
    # I am using the same class names (MyChoice, SomeModel) that were defined in previous responses
    def get_form_class(self):
        """
        Built-in Class Based View method to set the form class.
        Takes current object (self) as argument
        Returns a Class
        """
        # Fetch the record object of interest
        obj = SomeModel.objects.get(id=self.GET['pk'])

        # Define the class attributes of the Form Class
        cho = ModelChoiceField(queryset=MyChoice.objects.none())

        # Define the class methods of the Form Class
        def ini(s, *args, **kwargs): # s has been used instead of self to avoid ambiguity
            # invoking the super class's init
            super(type(s),s).__init__(*args,**kwargs) 

            # It is worth noting this function has access to:
            # 1. self (Present view's instance)
            # 2. obj (the variables defined within the parent function)
            # 3. global variables and imports made in this file

            # write your customizations below: 
            s.fields['choices'] = MyChoice.objects(number=obj.number, owner=self.request.user)

        # Let's now return the Form Class variable using the "all powerful" type function
        return type( \
                    type(self).__name__ + '_Form', # generate the Form Class Name \
                    (Form,), # Define the tuple of parent classes \
                    {'choices': cho, '__init__': ini} # value for the __dict__ attribute \
                    )

采用这种编码风格,我们所有的逻辑都停留在一个“ views.py ”位置。 同样的方法也可以扩展到表和过滤器(如果使用DjangoTables2)

希望您喜欢此解决方案。