Django class-based CreateView and UpdateView with multiple inline formsets

谁都会走 提交于 2019-11-28 16:51:59

问题


I have been trying to do Django class-based CreateView and UpdateView with multiple inline formsets

CreateView works fine but UpdateView is not working properly, If anyone tried UpdateView with multiple inline formsets, anyone tried pls share updateview code snippet.

# 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))

Thanks in advance.


回答1:


My guess is that you can't do

self.object = None

on overwritten post method in a UpdateView. So, try

self.object = self.get_object()

instead, once you already have an object instance in this case.




回答2:


I'm not sure if you've found an answer, but I have a working version of UpdateView documented in my answer found here:

UpdateView with inline formsets trying to save duplicate records?




回答3:


I don't think that the regular form of the updateview has to be added to the context because it is there anyways. A working Updateview with inlineformsets could be achieved less complicated. I based this on this 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))



回答4:


So I recognize the models form this post. To get UpdateView working properly you are going to have to do at least two, maybe three things:

  1. Update the self.object = self.get_object() -- after that, your ability to dynamically add should work.

  2. To get the dynamic deletes updating properly, you will need to alter the template with form.DELETE (in two places, the ingredients and the instructions).

    {{ form.description }}
    {% if form.instance.pk %}{{ form.DELETE }}{% endif %}
    
  3. Not sure it was necessary but I added can_delete to the factory too.

    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)
    


来源:https://stackoverflow.com/questions/27876644/django-class-based-createview-and-updateview-with-multiple-inline-formsets

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!