0

My goal is to edit/update existing data. I am able to display existing data and add new data but not edit data. My model are Recipe and Ingredient. I am using modelformset_factory to add ingredients to recipe.

Here is the forms.py:

class RecipeModelForm(forms.ModelForm):

  class Meta:
    model = Recipe
    fields = ('name', )
    labels = {
        'name': 'Recipe Name'
    }
    widgets = {
        'name': forms.TextInput(attrs={
            'class': 'form-control',
            'placeholder': 'Enter Recipe Name here'
        }
        )
    }


IngredientFormset = modelformset_factory(
    Ingredient,
    fields=('name', 'amount', 'unit'),
    labels={
        'name': 'Ingredient Name',
        'amount': 'Amount',
        'unit': 'Unit'
    },
    extra=1,
    widgets={
        'name': forms.TextInput(
            attrs={
                'class': 'form-control',
                'placeholder': 'Enter Ingredient Name here'
            }
        ),
        'amount': forms.TextInput(
            attrs={
                'class': 'form-control',
                'placeholder': 'Enter Amount here'
            }
        ),
        'unit': forms.Select(
            attrs={
                'class': 'form-control',
                'placeholder': 'Enter Unit here'
            }
        )
    },
    can_delete=True
)

views.py:

@login_required
def update_recipe(request, pk):
  template_name = 'recipe/create_with_ingredients.html'
  # template_name = 'recipe/update.html'
  recipe = Recipe.objects.get(id=pk, author=request.user)
  ingredients = Ingredient.objects.filter(recipe=recipe)

  if request.method == 'GET':
    recipeform = RecipeModelForm(instance=recipe)
    formset = IngredientFormset(queryset=ingredients)

  elif request.method == 'POST':
    recipeform = RecipeModelForm(request.POST, instance=recipe)
    formset = IngredientFormset(request.POST, queryset=ingredients)

    if recipeform.is_valid():  # and formset.is_valid():
      recipe = recipeform.save(commit=False)
      recipe.author = request.user
      recipe.save()

      for form in formset.initial_forms:
        print(form.is_valid())
        if form.is_valid():
          data = form.cleaned_data

      for form in formset:
        if form.is_valid():
          data = form.cleaned_data

          if data:
            ingredient = form.save(commit=False)
            ingredient.recipe = recipe
            ingredient.author = request.user
            ingredient.save()

      return redirect('recipe:list-recipe')

  context = {
      'recipeform': recipeform,
      'formset': formset,
  }
  return render(request, template_name, context)

My html template:

{% extends "recipe/base.html" %}

{% block container %}
{% if heading %}
<h3>{{heading}}</h3>
{% endif %}
<form class="form-horizontal" method="POST" action="">
  {% csrf_token %}
  <div class="row spacer">
  <div class="col-6">
    <label>{{recipeform.name.label}}</label>
  </div>
  <div class="col-8">
    <div class="input-group">
      {{recipeform.name}}
    </div>
  </div>
  </div>
  {{ formset.management_form }}
  {% for form in formset %}
    {% if form.subject.errors %}
      <ol>
      {% for error in form.subject.errors %}
          <li><strong>{{ error|escape }}</strong></li>
      {% endfor %}
      </ol>
    {% endif %}
    <!-- {% if form.errors %}
      {% for field in form %}
          {% for error in field.errors %}
              <div class="alert alert-danger">
                  <strong>{{ error|escape }}</strong>
              </div>
          {% endfor %}
      {% endfor %}
      {% for error in form.non_field_errors %}
          <div class="alert alert-danger">
              <strong>{{ error|escape }}</strong>
          </div>
      {% endfor %}
    {% endif %} -->
    <div class="row form-row spacer">
      <table>
        <div class="col-2">
          <tr>
            <th><label>{{form.name.label}}</label></th>
            <th><label>{{form.amount.label}}</label></th>
            <th><label>{{form.unit.label}}</label></th>
          </tr>
        </div>
        <div class="col-4">
          <tr>
            <div class="input-group">
              <td>{{form.name}}</td>
              <td>{{form.amount}}</td>
              <td>{{form.unit}}</td>
            
              <div class="input-group-append">
                <td><button class="btn btn-success add-form-row">+</button></td>
              </div>
            </tr>
          </div>
        </div>
      </table>
    </div>
  {% endfor %}
  <div class="row spacer">
    <div class="col-4 offset-0">
      <button type="submit" class="btn btn-block btn-primary">Create</button>
    </div>
  </div>
</form>
{% endblock %}

First I had if recipeForm.is_valid() and formset.is_valid() but this never result to True (formset fails). So, I removed second check and instead validate each form in formset. With this approach I found out that initial_forms (existing data) are not in formset. Therefore, to get initial forms I get it from formset.initial_forms this gives me forms with edited data and existing data but when I call form.is_valid() it fails. I am not sure why does initial data fail to valid when they are already in database. How can I update edited forms to existing data in database?

soul
  • 29
  • 9

1 Answers1

0

Try checking form validation errors first.

Give us an additional feedback with error messages in case my solution wasn't enough.

mszan
  • 517
  • 5
  • 18
  • thank you, I have implemented your solution but I do not get any errors. I have edited my post so you can see my html template. – soul Oct 30 '20 at 22:33