9

フォームとフォームセットを一度にレンダリングしようとしています。フォームセットは正常に動作していますが (私が思うに)、フォームは検証されていません (あたかもデータが投稿されていないかのように)

ボタンで遊んでみましたが、その主な送信機能はjsを介して行われます。

フォームはすべて独立して機能しますが、一緒に送信された場合は機能しないため、問題はビューにあるようです。コードは次のとおりです。

views.py

from django.shortcuts import render, render_to_response
from django.http import HttpResponseRedirect
from forms import LessonForm, AddMaterialForm
from models import Lesson, SUBJECT_OPTIONS, Materials, MATERIAL_TYPES
from django.forms.formsets import formset_factory

def Create_Lesson(request):
    AddMaterials=formset_factory(AddMaterialForm, extra=9)
    if request.method == "POST": # If the form has been submitted...
        lesson = LessonForm(request.POST, prefix="lesson") # A form bound to the POST data
        formset = AddMaterials(request.POST, request.FILES) # A form bound to the POST data
        if lesson.is_valid() and formset.is_valid(): # All validation rules pass
            lesson = lesson.save(commit=False)
            lesson.creator = request.user
            lesson.save()
            for form in formset:
                form = form.save(commit=False)
                form.lesson = lesson.pk
                form.save()
            return render(request, 'index.html',)
    else:
        lesson= LessonForm(prefix='lesson') # An unbound form
        formset = AddMaterials()
    return render(request, 'create_lesson/create.html', {
    'form': lesson,'formset':formset
})

.html

    <form id="create_lesson_form" method="post" action="">
    <h2>1: Create Your Lesson</h2>

        {{ form.non_field_errors }}
        <label for="subject"><span>Subject</span></label>
        {{form.subject}}
        {{ form.subject.errors }}
        <label for="title"><span>Title</span></label>
        <input type="text" id="title" name="name" placeholder="Give it a name"/>
        {{ form.name.errors }}
        <label class="error" for="title" id="title_error">You must choose a title!</label>            
        <label for="subtitle"><span>Subtitle</span></label>
        <input type="text" id="subtitle" name="subtitle" placeholder="Type your subtitle here"/>
        {{ form.subtitle.errors }}
        <label class="error" for="subtitle" id="subtitle_error">are you sure you want to leave subtititle blank?</label>
        <label for="description"><span>Description</span></label>
        <textarea id="description" name= "description" cols="42" rows="5" placeholder="why is it important? this can be a longer description"'></textarea>
        {{ form.description.errors }}
        <label class="error" for="description" id="description_error">are you sure you want to leave the description blank?</label>
        <label for="success" id="Goals_title"><span>Goals</span></label>
        <textarea id="success" name="success" cols="42" rows="5" placeholder="explain what sucess might look like for someone doing this lesson...what does mastery look like?" '></textarea>
        {{ form.success.errors }}
        <label class="error" for="success" id="success_error">are you sure you want to leave the goals blank?</label>
    {{ form.directions.errors }}
        <label class="error" for="directions" id="directions_error">are you sure you do not want to include dierections?</label>
    <label for="directions" id="Directions_title"><span>Directions</span></label>
        <textarea id="directions" name="directions" cols="42" rows="5" placeholder="you can add simple directions here" '></textarea><br>
    </form>


    <form id="add_elements_form" method="post" action="">
    {% csrf_token %}
    {{ formset.as_p}}
    <button type='submit' id='finish'>Finish Editing Lesson</button>
    </form>
4

3 に答える 3

10

これにより、フォームとフォームセットが同時に送信されます。

//When your uploading files or images don't forget to put "multipart/form-data" 
//   in your form. 
//To connect formset in your form, don't forget to put the model in the formset 
//   for instance.
//In this you can add many lines as you want or delete it.

フォーム.py

class LessonForm(forms.ModelForm):
    class Meta:
        model = Lesson


MaterialsFormset = inlineformset_factory(Lesson, Materials, 
    fields=('field_name', 'field_name'), can_delete=True)

ビュー.py

def view_name(request):
    form = LessonForm()
    formset = MaterialsFormset(instance=Lesson())
    if request.method == 'POST':
        form = LessonForm(request.POST)
        if form.is_valid():
            lesson = form.save()
            formset = MaterialsFormset(request.POST, request.FILES,
                instance=lesson)
            if formset.is_valid():
                formset.save()
                return render(request, 'index.html',)
    return render(request, "page.html", {
        'form': form, 'formset': formset
    })

html

<form method="post" enctype="multipart/form-data">
    {% csrf_token %}
    {{ form.as_p }}
    {{ formset.as_p }}
    <input type="submit" value="Save"/>
</form>
于 2013-03-02T01:06:05.963 に答える
0

django 4 がリリースされたので、Reusable templatesを使用してフォーム自体で同じことを行うことができます。ビューをいじらずに複雑なフォームを再利用する方が簡単なので、私はこのソリューションを好みます。

記録のために、ここに私がそれを行う方法があります

2 つの関連モデル:

# models.py
from django.db import models

class Recipe(models.Model):
    name = models.CharField(max_length=100)
    def __str__(self):
        return self.name

class Ingredient(models.Model):
    name = models.CharField(max_length=100)
    quantity = models.FloatField()
    recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE, related_name="ingredients")
    def __str__(self):
        return self.name

結合されたフォーム:

# forms.py
from django import forms
from .models import Recipe, Ingredient

class IngredientForm(forms.ModelForm):
    class Meta:
        model = Ingredient
        exclude = ('recipe',)

IngredientFormSet = forms.inlineformset_factory(Recipe, Ingredient, form=IngredientForm)

class RecipeForm(forms.ModelForm):
    class Meta:
        model = Recipe
        fields = '__all__'

    template_name = 'recipe_form.html'

    def __init__(self, *args, **kwargs):
        """Initialize the formset with its fields."""
        self.formset = IngredientFormSet(*args, **kwargs)
        super().__init__(*args, **kwargs)

    def get_context(self):
        """Add the formset to the context for rendering."""
        context = super().get_context()
        context['formset'] = self.formset
        return context

    def save(self, commit=True):
        """Bind both models together."""
        instance = super().save(commit=False)
        self.formset.instance = instance
        if self.formset.is_valid():
            instance.save()
            self.formset.save()
        return instance

フォームのテンプレート:

<!-- templates/recipe_form.html -->
<p>Recipe: {{ form.name }}</p> <!-- calling "form" creates a rendering recursion -->
<p>Ingredients:</p>
{{ formset.management_form }}
<ul>
{% for elt in formset %}
  <li>{{ elt }}</li>
{% endfor %}
</ul>

そしてそれを使用したビュー:

# views.py
from django.views.generic import TemplateView
from .forms import RecipeForm

class RecipeView(TemplateView):
    template_name = 'recipe.html'
    def get_context_data(self):
        context = super().get_context_data()
        context['form'] = RecipeForm()
        return context

    def post(self, *args, **kwargs):
        form = RecipeForm(self.request.POST)
        if form.is_valid():
            form.save()
        else:
            raise Exception('Something bad happened!')
        return self.get(*args, **kwargs)

非常に基本的なテンプレートの場合:

<!-- templates/recipe.html -->
<form action="." method="post">
    {% csrf_token %}
    {{ form }}
    <button type="submit">Submit</button>
</form>

そして最後に、あなたは行く準備ができています:

# tests.py
from django.test import TestCase
from django.urls import reverse
from .models import Recipe

class TestFormSet(TestCase):
    def test_new_recipe(self):
        data = {
            "name": "quiche",
            "ingredients-TOTAL_FORMS": 3,
            "ingredients-INITIAL_FORMS": 0,
            "ingredients-0-name": 'bacon bits',
            "ingredients-0-quantity": 200,
            "ingredients-1-name": 'eggs',
            "ingredients-1-quantity": 4,
            "ingredients-2-name": 'cream',
            "ingredients-2-quantity": 150,
        }
        r = self.client.post(reverse('recipe'), data=data)
        self.assertEqual(Recipe.objects.first().ingredients.count(),3)
$ python manage.py test
OK

うまくいけば、それは誰かに役立つでしょう。

于 2022-02-02T15:27:00.160 に答える