Django - несколько форм на одном представлении - передача несозданного объекта второй форме

Интересно, можно ли сделать две формы на основе модели django на одном представлении, одна из которых требует объект, который будет создан второй (Foreign Key). Я покажу пример, чтобы сделать его более понятным

У меня есть эти модели:

class Recipe(models.Model):
    name = models.CharField(max_length=200)


    def __str__(self):
        return self.name

class Ingredient(models.Model):
    name = models.CharField(max_length=200)
    quantity = models.CharField(max_length=200)
    recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE)

    def __str__(self):
        return self.name

Как вы можете видеть, модель Indgredient принимает Recipe в качестве параметра. Я хочу сделать форму, которая позволит пользователю создать новый рецепт и ингредиент (или ингредиенты), поле которого будет заполнено рецептом, созданным в том же представлении

Я пытался сделать это таким образом

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

class IngredientForm(ModelForm):
    class Meta:
        model = Ingredient
        fields = ['name', 'quantity']

class RecipeForm(ModelForm):
    class Meta:
        model = Recipe
        fields = ['name']
def recipeAndIngredientsCreationView(request):
    form1 = RecipeForm()
    form2 = IngredientForm()
    if request.method == "POST":


        data1 = {
                'csrfmiddlewaretoken': request.POST['csrfmiddlewaretoken'],
                'name': request.POST['name'],
        }
        form1 = RecipeForm(data1)
    
        if form1.is_valid():
            form1.save()
            print("recipe created")


        data2 = {
                'csrfmiddlewaretoken': request.POST['csrfmiddlewaretoken'],
                'name': request.POST['name'],
                'quantity' : request.POST['quantity'],
                'recipe_id': Recipe.objects.get(name=request.POST['name']).id,
        }
        form1 = IngredientForm(data2)

        
        if form2.is_valid():
            form2.save()
            return redirect('home')


    context = {'form1':form1, 'form2':form2}
    return render(request, 'recipes/create_recipe.html', context)

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

Рецепт создается без проблем, но есть некоторые проблемы с ингредиентом, что я должен изменить, чтобы он работал, или может быть изменить мой подход и сделать форму совершенно другим способом?

редактирование: Я не уверен, что 'recipe_id' является правильным способом передачи id, но я получал ошибку, что поле с таким именем должно быть заполнено, поэтому я сделал его таким образом

Добавьте ввод формы имени во все ваши формы и элементы управления в def post вашего представления с

if request.POST['name_form] == 'form1':
    #
elif request.POST['name_form'] == 'form2':
    #

Сначала необходимо подготовить формы:

from django import forms
from django.forms.models import inlineformset_factory
from .models import Ingredient, Recipe


class RecipeForm(ModelForm):
    class Meta:
        model = Recipe
        fields = ['name']


IngredientInlineFormset = inlineformset_factory(Recipe, Ingredient, fields='__all__', extra=1)

inlineformset_factory - функция формы модели Django docs

extra - сколько наборов форм должно отображаться в шаблоне

Тогда в ваших представлениях:

def new_recipe(request):
   if request.method == 'POST':
      form_inline = IngredientInlineFormset(request.POST)
      form = RecipeForm(request.POST)
      if form.is_valid() and form_inline.is_valid():
          instance = form.save()

          form_inline.instance = instance
          form_inline.save()
          return redirect('index')
       else:
          print(form.errors, form_inline.errors)
   else:
       form_inline = IngredientInlineFormset()
       form = RecipeForm()

   context = {
       'form': form,
       'form_inline': form_inline,
   }

   return render(request, 'new_recipe.html', context)

Здесь вы проверяете, действительны ли обе формы, затем сначала сохраняете форму родительской модели и присваиваете ее inlineformset (нам нужен id нового объекта Recipe).

И, наконец, ваша форма в шаблоне:

<form method="post">
   {% csrf_token %}
   {{ form }}
   {{ form_inline.management_form }}
   {{ form_inline.non_form_errors }}
   {% for form in form_inline %}
       <p>{{ form }}</p>
   {% endfor %}
   <button type="submit">Add Recipe with Ingredients</button>
</form>

Это базовый пример. Не забудьте включить management_form из inlineformset. Не спешите, у меня ушло довольно много времени, чтобы понять это.

Вернуться на верх