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?