【问题标题】:Django class-based CreateView and UpdateView with multiple inline formsets具有多个内联表单集的基于 Django 类的 CreateView 和 UpdateView
【发布时间】:2015-03-08 17:21:32
【问题描述】:

我一直在尝试使用多个内联表单集来做基于 Django 类的 CreateView 和 UpdateView

CreateView 工作正常,但 UpdateView 无法正常工作,如果有人尝试使用多个内联表单集的 UpdateView,任何尝试过的人请分享 updateview 代码 sn-p。

# models.py
from django.db import models

class Recipe(models.Model):
    title = models.CharField(max_length=255)
    description = models.TextField()

class Ingredient(models.Model):
    recipe = models.ForeignKey(Recipe)
    description = models.CharField(max_length=255)

class Instruction(models.Model):
    recipe = models.ForeignKey(Recipe)
    number = models.PositiveSmallIntegerField()
    description = models.TextField()


# forms.py
from django.forms import ModelForm
from django.forms.models import inlineformset_factory
from .models import Recipe, Ingredient, Instruction

class RecipeForm(ModelForm):
    class Meta:
        model = Recipe

IngredientFormSet = inlineformset_factory(Recipe, Ingredient, extra=0)
InstructionFormSet = inlineformset_factory(Recipe, Instruction, extra=0)


# views.py
from django.http import HttpResponseRedirect
from django.views.generic.edit import CreateView, UpdateView
from django.shortcuts import get_object_or_404

from .forms import IngredientFormSet, InstructionFormSet, RecipeForm
from .models import Recipe

class RecipeCreateView(CreateView):
    template_name = 'recipe_add.html'
    model = Recipe
    form_class = RecipeForm
    success_url = '/account/dashboard/'

    def get(self, request, *args, **kwargs):
        self.object = None
        form_class = self.get_form_class()
        form = self.get_form(form_class)
        ingredient_form = IngredientFormSet()
        instruction_form = InstructionFormSet()
        return self.render_to_response(
            self.get_context_data(form=form,
                                  ingredient_form=ingredient_form,
                                  instruction_form=instruction_form))

    def post(self, request, *args, **kwargs):
        self.object = None
        form_class = self.get_form_class()
        form = self.get_form(form_class)
        ingredient_form = IngredientFormSet(self.request.POST)
        instruction_form = InstructionFormSet(self.request.POST)
        if (form.is_valid() and ingredient_form.is_valid() and
            instruction_form.is_valid()):
            return self.form_valid(form, ingredient_form, instruction_form)
        else:
            return self.form_invalid(form, ingredient_form, instruction_form)

    def form_valid(self, form, ingredient_form, instruction_form):
        self.object = form.save()
        ingredient_form.instance = self.object
        ingredient_form.save()
        instruction_form.instance = self.object
        instruction_form.save()
        return HttpResponseRedirect(self.get_success_url())

    def form_invalid(self, form, ingredient_form, instruction_form):
        return self.render_to_response(
            self.get_context_data(form=form,
                                  ingredient_form=ingredient_form,
                                  instruction_form=instruction_form))

class RecipeUpdateView(UpdateView):
    template_name = 'recipe_add.html'
    model = Recipe
    form_class = RecipeForm

    def get_success_url(self):
        self.success_url = '/account/dashboard/'
        return self.success_url

    def get_context_data(self, **kwargs):
        context = super(RecipeUpdateView, self).get_context_data(**kwargs)
        if self.request.POST:
            context['form'] = RecipeForm(self.request.POST, instance=self.object)
            context['ingredient_form'] = IngredientFormSet(self.request.POST, instance=self.object)
            context['instruction_form'] = InstructionFormSet(self.request.POST, instance=self.object)
        else:
            context['form'] = RecipeForm(instance=self.object)
            context['ingredient_form'] = IngredientFormSet(instance=self.object)
            context['instruction_form'] = InstructionFormSet(instance=self.object)
        return context

    def post(self, request, *args, **kwargs):
        self.object = None
        form_class = self.get_form_class()
        form = self.get_form(form_class)
        ingredient_form = IngredientFormSet(self.request.POST)
        instruction_form = InstructionFormSet(self.request.POST)
        if (form.is_valid() and ingredient_form.is_valid() and
            instruction_form.is_valid()):
            return self.form_valid(form, ingredient_form, instruction_form)
        else:
            return self.form_invalid(form, ingredient_form, instruction_form)

    def form_valid(self, form, ingredient_form, instruction_form):
        self.object = form.save()
        ingredient_form.instance = self.object
        ingredient_form.save()
        instruction_form.instance = self.object
        instruction_form.save()
        return HttpResponseRedirect(self.get_success_url())

    def form_invalid(self, form, ingredient_form, instruction_form):
        return self.render_to_response(
            self.get_context_data(form=form,
                                  ingredient_form=ingredient_form,
                                  instruction_form=instruction_form))

提前致谢。

【问题讨论】:

标签: django django-class-based-views


【解决方案1】:

我不认为更新视图的常规形式必须添加到上下文中,因为无论如何它都在那里。可以实现具有 inlineformsets 的工作 Updateview 不太复杂。我基于这个Question

class RecipeUpdateView(UpdateView):
    model = Recipe
    form_class = RecipeUpdateForm
    success_url = "/foo/"

    def get_success_url(self):
        self.success_url = '/account/dashboard/'
        return self.success_url

    def get_object(self):
        return #your object

    def get_context_data(self, **kwargs):
        context = super(RecipeUpdateView, self).get_context_data(**kwargs)
        if self.request.POST:
            context['ingredient_form'] = IngredientFormSet(self.request.POST, instance=self.object)
            context['instruction_form'] = InstructionFormSet(self.request.POST, instance=self.object)
        else:
            context['ingredient_form'] = IngredientFormSet(instance=self.object)
            context['instruction_form'] = InstructionFormSet(instance=self.object)
        return context

    def form_valid(self, form):
        context = self.get_context_data()
        ingredient_form = context['ingredient_form']
        instruction_form = context['instruction_form']
        if ingredient_form.is_valid() and instruction_form.is_valid():
            self.object = form.save()
            ingredient_form.instance = self.object
            ingredient_form.save()
            instruction_form.instance = self.object
            instruction_form.save()
        return self.render_to_response(self.get_context_data(form=form))

【讨论】:

    【解决方案2】:

    所以我认出了这个post 的模型。要让 UpdateView 正常工作,您至少需要做两件事,也许是三件事:

    1. 更新self.object = self.get_object() -- 之后,您的动态添加功能应该可以工作了。

    2. 要正确更新动态删除,您需要使用 form.DELETE 更改模板(在两个地方,成分和说明)。

      {{ form.description }}
      {% if form.instance.pk %}{{ form.DELETE }}{% endif %}
      
    3. 不确定是否有必要,但我也将 can_delete 添加到工厂。

      IngredientFormSet = inlineformset_factory(Recipe, Ingredient, fields=('description',), extra=3, can_delete=True)
      InstructionFormSet = inlineformset_factory(Recipe, Instruction, fields=('number', 'description',), extra=1, can_delete=True)
      

    【讨论】:

    • 此评论中的链接已失效。
    【解决方案3】:

    我不确定您是否找到了答案,但我在此处找到的答案中记录了 UpdateView 的工作版本:

    UpdateView with inline formsets trying to save duplicate records?

    【讨论】:

    • 此链接提供了方法。注意在get函数的formset定义中使用instance,而不是initial,以及self.object = self.get_object而不是None
    【解决方案4】:

    我猜你做不到

    self.object = None
    

    UpdateView 中覆盖post 方法。所以,试试

    self.object = self.get_object()
    

    相反,在这种情况下,一旦您已经拥有一个对象实例。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-05-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-15
      • 1970-01-01
      • 2013-08-09
      相关资源
      最近更新 更多