【问题标题】:Django: pass data from CBV form view to form CBVDjango:将数据从 CBV 表单视图传递给 CBV
【发布时间】:2017-08-31 10:54:52
【问题描述】:

我有一个Form 和一个ModelChoiceField,它被用作FormView 中的form_class

必须使用绑定到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())

【问题讨论】:

    标签: django


    【解决方案1】:

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

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

    Django 自带电池;在我们运行 django-admin startapp 的那一刻,它会为我们创建所有必要的文件(管理、应用程序、模型、测试、视图)。

    其次,我想说不需要创建一个单独的表单类。 我清楚地明白,自定义我们的表单必须有一个表单类;我只是说我们不需要明确定义它们

    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)

    希望您喜欢这个解决方案。

    【讨论】:

      【解决方案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
      

      【讨论】:

      • 那么我将不得不从 kwargs 中弹出 'user''number' 而不是将它们作为表单构造函数中的参数,对吧?
      • 不,这不正确。既然你有def __init__(self, user, number, *args, **kwargs),就不需要从kwargs弹出usernumber
      • 哦,对了,因为我明确声明了这些参数,所以我根本不需要在表单构造函数中检查kwargs。我需要进行哪些更改才能将该数据包含在 kwargs 而不是命名参数中?
      • 如果将方法签名更改为def __init__(self, *args, **kwargs),则usernumber 将位于kwargs 中的__init__ 方法内。在这种情况下,你必须弹出它们,否则调用super时会出错。
      猜你喜欢
      • 2013-12-05
      • 2015-01-16
      • 1970-01-01
      • 2015-11-27
      • 1970-01-01
      • 2017-04-10
      • 1970-01-01
      • 2013-06-29
      • 2016-03-01
      相关资源
      最近更新 更多