Как сохранить пользовательское поле встроенного набора форм в Django с помощью представлений

Я использовал этот замечательный пост http://kevindias.com/writing/django-class-based-views-multiple-inline-formsets/ для настройки моего сайта. Мне было интересно, как автоматически сохранить пользовательское поле во встроенном наборе форм в представлениях (я использовал цитату для изменений в оригинале). RecipeForm в (см. Также контекст ниже)

self.object = form.save(commit=False)
self.object.owner = self.request.user
self.object.save()

сохраняет красиво автоматически, но не

ingredient_form.owner= self.request.user

Я знаю, что Django предлагает использовать BaseInlineFormSet, но большинство людей по разным причинам предлагают сохранять поле пользователя в views.py, а не в формах или моделях. Буду признателен за любые предложения или ответы. Вот полный код:

models.py

from django.db import models


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


class Ingredient(models.Model):
    owner = models.ForeignKey(User)
    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)
    InstructionFormSet = inlineformset_factory(Recipe, Instruction)

views.py

from django.http import HttpResponseRedirect
from django.views.generic import CreateView  
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 = 'success/'

    def get(self, request, *args, **kwargs):
        """
        Handles GET requests and instantiates blank versions of the form
        and its inline formsets.
        """
        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):
        """
        Handles POST requests, instantiating a form instance and its inline
        formsets with the passed POST variables and then checking them for
        validity.
        """
        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):
        """
        Called if all forms are valid. Creates a Recipe instance along with
        associated Ingredients and Instructions and then redirects to a
        success page.
        """
        self.object = form.save(commit=False)
        self.object.owner = self.request.user
        self.object.save()
        ingredient_form.instance = self.object
        ingredient_form.owner= self.request.user
        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):
        """
        Called if a form is invalid. Re-renders the context data with the
        data-filled forms and errors.
        """
        return self.render_to_response(
            self.get_context_data(form=form,
                                  ingredient_form=ingredient_form,
                                  instruction_form=instruction_form))

person mloch    schedule 08.11.2016    source источник


Ответы (1)


Я провел еще несколько исследований, и после этого руководства по добавлению пользовательского сохранения набора форм, но изменено для BaseInlineFormset, как упоминалось выше. Я понял, что будет проще просто создать ModelForms для каждой модели, а затем связать их в представлении, поскольку мне нужна только одна дочерняя форма за раз при добавлении нового представления рецепта и я могу повторно использовать код ModelForm.

вот новый код, который отлично работает! Не стесняйтесь обращаться, если вам нужна дополнительная информация.

forms.py

from django.forms import ModelForm
from .models import Recipe, Ingredient, Instruction


class RecipeForm(ModelForm):

    class Meta:
        model = Recipe
        exclude = ['owner',]

class IngredientForm(ModelForm):

    class Meta:
        model = Ingredient
        exclude = ['owner','recipe',]

class InstructionForm(ModelForm):

    class Meta:
        model = Instruction
        exclude = ['recipe',]

views.py

from .forms import IngredientForm, InstructionForm, RecipeForm


def add_new_value(request):
    rform = RecipeForm(request.POST or None)
    iform = IngredientForm(request.POST or None)
    cform = InstructionForm(request.POST or None)
    if rform.is_valid() and iform.is_valid() and cform.is_valid():
        rinstance = rform.save(commit=False)
        iinstance = iform.save(commit=False)
        cinstance = cform.save(commit=False)
        user = request.user
        rinstance.owner = user
        rinstance.save()
        iinstance.owner = user
        cinstance.owner = user
        iinstance.recipe_id = rinstance.id
        cinstance.recipe_id = rinstance.id
        iinstance.save()
        cinstance.save()
        return HttpResponseRedirect('/admin/')
    context = {
        'rform' : rform,
        'iform' : iform,
        'cform' : cform,
    }
    return render(request, "add_new_recipe.html", context)

шаблон: add_new_recipe.html

<!DOCTYPE html>
<html>
<head>
    <title>Add Recipe</title>
</head>

<body>
    <div>
        <h1>Add Recipe</h1>
        <form action="" method="post">
            {% csrf_token %}
            <div>
                {{ rform.as_p }}
                {{ iform.as_p }}
                {{ cform.as_p }}
            </div>
            <input type="submit" value="Add recipe" class="submit" />
        </form>
    </div>
</body>
</html>
person mloch    schedule 08.11.2016
comment
Так рада, что нашла этот пост. Я также пытался сделать это с помощью встроенных наборов форм и общего CreateView. Он работал, но я не мог понять, как установить владельца дочернего экземпляра для пользователя, делающего запрос. Это прекрасный пример случая, когда намного проще использовать функциональные представления и формы моделей, чем встроенный набор форм. Спасибо, что опубликовали свое решение, оно мне очень помогло. - person wjh18; 27.07.2019
comment
как сделать, чтобы добавить ингредиент или отредактировать ингредиент? - person atufa shireen; 10.01.2021