django内联表单与自定义表单

时间:2011-03-02 17:49:59

标签: django django-forms

您好 我有一个域模型,在Django应用程序中使用,我想在一个表单上呈现。我用自定义的ModelForms创建了我的应用程序(没有太多变化,一些字段被排除在外等)。模型的依赖关系如下:

Complaint
   \
    .--- CarInfo
    .--- Customer

我的视图功能如下:

def make(request):
  if request.method == 'POST':
    parameters = copy.copy(request.POST)
    complaint = Complaint()
    carInfo = CarInfo()
    customer = Customer()

    customer_form = CustomerForm(parameters, instance=customer)
    carInfo_form = CarInfoForm(parameters, instance=carInfo)
    parameters['complaint_date'] = get_current_date()
    parameters['customer'] = 1 # dummy value to allow validation success
    parameters['car_info'] = 1 # dummy value to allow validation success
    form = ComplaintForm(parameters, instance=complaint)
    if form.is_valid() and customer_form.is_valid() and carInfo_form.is_valid():
      carInfo_form.save()
      customer_form.save()
      parameters['customer'] = customer.id
      parameters['car_info'] = carInfo.id
      form = ComplaintForm(parameters, instance=complaint)
      form.save()
      return index(request)
  else:
    form = ComplaintForm()
    carInfo_form = CarInfoForm()
    customer_form = CustomerForm()
  return render_to_response('complaints/make_complaint.html', {'complaint_form' : form, 'customer_form' : customer_form, 'carInfo' : carInfo_form})

我不太喜欢这种方法,而且它并不适用于所有环境 - 你还没有找到它不起作用的原因。我一直在研究修改这段代码,发现像内联formset (http://docs.djangoproject.com/en/dev/topics/forms/modelforms/#inline-formsets) 。这个解决方案似乎没问题,但由于我的表单是定制的,我可以使用它。

也许有人可以就如何妥善解决此类案件提供一些建议。清洁解决方案非常受欢迎。

EDITED 我有一个案例,这个解决方案不起作用。尽管在外键上设置了虚拟值,但当我调用is_valid()时,我得到FALSE,并显示错误消息,指出这些字段未设置。我用django 1.2.5观察这个问题 - 它发生在服务器上我打算运行这个应用程序,但是我的笔记本电脑(也是django 1.2.5)没有这个问题。

3 个答案:

答案 0 :(得分:5)

您可以将投诉模型的complaint_date更改为此类

complaint_date = models.DateField(default=datetime.date.today())

这样你可以摆脱

parameters['complaint_date'] = get_current_date()

对于您的多形式视图,您可以使用未绑定的表单来实现所需的行为。

通过在投诉表格中将fk排除给汽车和客户,表格应该验证。同时检查所有3个表单的.is_valid(),然后保存投诉对象所依赖的2个表单,创建投诉对象,不提交数据库(commit = False),添加客户的id和开车到那个物体,然后保存。

在你看来。

def make(request):
    if request.method == 'POST':
        customer_form = CustomerForm(request.POST)
        carInfo_form = CarInfoForm(request.POST)
        form = ComplaintForm(request.POST)

        if form.is_valid() and customer_form.is_valid() and carInfo_form.is_valid():
            car_instance = carInfo_form.save()
            customer_instance = customer_form.save()

            complaint_instance = form.save(commit=False)
            complaint_instance.car_info = car_instance
            complaint_instance.customer_info = customer_instance          
            complaint_instance.save()

            return index(request)
    else:
        form = ComplaintForm()
        carInfo_form = CarInfoForm()
        customer_form = CustomerForm()

    context = { 'complaint_form' : form,
                'customer_form' : customer_form, 
                'carInfo' : carInfo_form,
              }
    return render_to_response('complaints/make_complaint.html', context, context_instance=RequestContext(request))

编辑:

模型看起来像这样:

class CarInfo(models.Model):
    some_car_info = models.CharField()

class Customer(models.Model):
    some_customer_info = models.CharField()

class Complaint(models.Model):
    car_info = models.ForeignKey(CarInfo)
    customer_info = models.ForeignKey(Customer)
    some_complaint_info = models.CharField()

forms.py应如下所示:

class CarInfoForm(forms.ModelForm):
    class Meta:
        model = CarInfo

class CustomerForm(forms.ModelForm):
    class Meta:
        model = Customer

class ComplaintForm(forms.ModelForm):
    class Meta:
        model = Complaint
        exclude = ('car_info', 'customer_info',) # or include = ('some_complaint_info',)

让我们一起浏览上面写的视图: form in view docs

  • 首次通过时,没有request.method,因此我们创建了3个unbound表单。

    else:
        form = ComplaintForm()
        carInfo_form = CarInfoForm()
        customer_form = CustomerForm()
    
  • 将这些表单传递给模板并进行渲染。

  • 当使用request.method ==“POST”评估为true再次调用视图时,我们使用来自request.POST的数据创建3个绑定的表单实例。

    if request.method == 'POST':
        customer_form = CustomerForm(request.POST)
        carInfo_form = CarInfoForm(request.POST)
        form = ComplaintForm(request.POST)
    
  • 接下来,我们在每个表单上调用.is_valid()方法。在我们的示例中,因为我们在投诉模型中排除了'customer_info'和'car_info'外键字段,每个表单只检查char输入字段是否有效。

  • 如果验证全部通过,那么我们可以开始将表格保存到模型中,这样我们需要小心填写我们的投诉所需的fk:

        if form.is_valid() and customer_form.is_valid() and carInfo_form.is_valid():
            car_instance = carInfo_form.save()
            customer_instance = customer_form.save()
    
  • 使用这两种形式,我们可以像往常一样调用.save()。但是,我们会将返回值分配给car_instance和customer_instance。这些将包含我们刚刚在表单上使用.save()方法创建的CarInfo和Customer模型的实例。

  • 接下来,使用.save()方法中的commit=False参数,我们可以从绑定表单(包含request.POST数据)创建一个对象,而不是将其保存到数据库

            complaint_instance = form.save(commit=False)
            complaint_instance.car_info = car_instance
            complaint_instance.customer_info = customer_instance          
            complaint_instance.save()
    
  • 为了更清楚,您还可以创建一个新的Complaint对象:

    complaint_info = form.cleaned_data.get('some_complaint_info')
    complaint_instance = Complaint(car_info=car_instance, customer_info=customer_instance, some_complaint_info=some_complaint_info)
    complaint_instance.save()
    
  • 渲染

答案 1 :(得分:3)

我认为您已经拥有最干净简单的方法,但如果您想使用formsets,请尝试以下链接:

EDIT。我猜你可以通过虚拟值(和修改请求.POST,我可以继续猜测:)来体验问题,但@kriegar展示了如何避免这种情况。无论如何,在一个视图中保存多个表单并不困难; Django Forms足以支持这种情况。我的观点是,明确地这样做是最最干净和最简单的方式,形式集不会改善情况。

答案 2 :(得分:1)

可能formset工厂和内联formset应解决您的问题...您可以修改或覆盖从模型创建的表单字段,对于子模型,您可以使用内联表单集...

Formsets and inline formsets...

可能的解决方案:

表格定义中的

class CarInfoFrm(forms.ModelForm):
    class Meta:
        model = CarInfo
        fields = (....)
carInfoForm = inlineformset_factory(Complaint, CarInfo, form=carInfoFrm,)
CustomerForm = inlineformset_factory(Complaint, Customer, form=carInfoFrm,)

在您看来:

complaint = Complaint()
carInfo = CarInfo()
customer = Customer()

cus_form = CustomerForm(parameters, instance=complaint)
car_form = CarInfoForm(parameters, instance=complaint)
comp_form = ComplaintForm(parameters, instance=complaint)

if cus_form.is_valid() and ...... :
    comp = comp_form.save(commit=False)#do not save it yet
    comp.<attr> = "some_value" #you can edit your data before save...
    comp.save()
    car = car_form(commit=False)
    # do as complaint form... edit and save... 

编辑:我在保存内联形式时定义实例参数时制作了一个misteke。所以我更正了......

更新现有记录时,您不会遇到问题,但最好像使用它一样:

if comp_form.is_valid():
    comp = comp_form.save(commit=False)
    comp.<attr> = "some_value"
    comp.save()
if car_form.is_valid():
    # edit if neccessary then save...

if cust_form.is_Valid():
    # edit if neccessary then save...

更简单的是,在定义表单时,可以通过外键设置父表单

carInfoForm = inlineformset_factory(Complaint, CarInfo, form=carInfoFrm,)

使用内联表单进行更新时,可以使用父Compalaint记录

对其进行初始化
car_form = CarInfoForm(parameters, instance=complaint)

所以,car_form不接受carInfo实例,而是一个投诉实例(这是我第一次回答的错误,所以我更正了)。如果它创建了新记录,它会自动将其绑定到相关的投诉记录。如果是更新,则只更新您想要的字段。

对我而言,最好使用框架的moehods而不是编写自己的所有者。通过这样做,您将保证由django进行所有验证检查。