Django looping formsets cause ValidationError in management form - django

I am trying to output two same forms and save them to database with different prefix. I used this post https://collingrady.wordpress.com/2008/02/18/editing-multiple-objects-in-django-with-newforms/ as an example. However I get validation error that management form is being tampered with. Could you please kindly advise how to solve it? Thank you.
Also is it possible to filter database by the prefix in this case if i want to retrieve the data later for analysis.
VIEWS.PY
from django.shortcuts import render
from .forms import modelformset_factory, AssumptionsForm
from .models import Assumptions
model_names = ['Form1', 'Form2']
def get_assumptions(request):
AssumptionsFormset = modelformset_factory(
Assumptions, form=AssumptionsForm, extra=5)
if request.method == 'POST':
formsets = [AssumptionsFormset(request.POST, prefix=thing) for thing in model_names]
if all([formset.is_valid() for formset in formsets]):
for formset in formsets:
for form in formset:
form.save()
else:
formsets = [AssumptionsFormset(request.POST, prefix=thing) for thing in model_names]
return render(request, 'assumptions.html', {'formsets': formsets})
ASSUMPTIONS.HTML
<div class="form">
<form action="" method="post">
{% csrf_token %}
{% for formset in formsets %}
{{ formset.management_form }}
{{ formset.non_form_errors.as_ul }}
<h1>{{formset.prefix}}</h1>
<table id="formset" class="form">
{% for form in formset.forms %}
{% if forloop.first %}
<thead><tr>
{% for field in form.visible_fields %}
<th>{{ field.label|capfirst }}</th>
{% endfor %}
</tr></thead>
{% endif %}
<tr class="{% cycle 'row1' 'row2' %}">
{% for field in form.visible_fields %}
<td>
{# Include the hidden fields in the form #}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field.errors.as_ul }}
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<input type="submit" value="Submit">
{% endfor %}
</form>
</div>
MODELS.PY
from django.db import models
from django.forms import ModelForm
class Assumptions(models.Model):
Worst = models.FloatField(null=True, blank=True, default=None)
Base = models.FloatField(null=True, blank=True, default=None)
Best = models.FloatField(null=True, blank=True, default=None)
FORMS.PY
from django import forms
from django.forms import modelformset_factory, ModelForm
from .models import Assumptions
class AssumptionsForm(ModelForm):
class Meta:
model = Assumptions
fields = '__all__'

You are trying to initialize the formsets with request.POST on GET requests, which of course can't work.
Replace the second
formsets = [AssumptionsFormset(request.POST, prefix=thing) for thing in model_names]
with
formsets = [AssumptionsFormset(prefix=thing) for thing in model_names]

Related

How to combine ListView and UpdateView

In my web application, I have a template with ListView that shows list of table records, that works fine.
Now I want to allow user to update fields for each row of the table.
One "easy" solution is to create an update page using UpdateView and put a link to it in each row. User can then click update, open update page ... But I am wondering if there is a way to update fields in the same table, without opening a new page.
Hope my question is clear.
thanks for your help
UPDATE (and "my" solution)
I managed to do it using formset:
in forms.py
class IssueForm(forms.ModelForm):
class Meta:
model=Issue
fields=('__all__')
IssueFormset=inlineformset_factory(IssuesList, Issue, form=IssueForm,extra=0)
in views.py
class IssuesListUpdateView(UpdateView):
model=IssuesList
fields='__all__'
# ovewrite get_context_data to add formset as an additionnal parameter
def get_context_data(self, **kwargs):
context = super(IssuesListUpdateView, self).get_context_data(**kwargs)
if self.request.POST:
# if Save All issues button submitted
if 'save_all' in self.request.POST.keys():
formset = IssueFormset(self.request.POST, instance=self.object)
# sending formset to the template
context['issues_formset'] = formset
# save in the DB (saves only what has changed)
#https://docs.djangoproject.com/en/2.1/topics/forms/modelforms/#saving-objects-in-the-formset
# if formset.is_valid():
formset.save()
# re-update context with new formset, this is need to refresh table in case of deleting an object
# without this, issue is deleted but still visible in the table
context['issues_formset'] = IssueFormset(instance=self.object)
else:
# sending formset to the template
context['issues_formset'] = IssueFormset(instance=self.object)
return context
In template:
<form method="post">{% csrf_token %}
<!-- Below line is important to avoid django exception:
[django 'ManagementForm data is missing or has been tampered with] -->
{{ issues_formset.management_form }}
<input name="save_all" type="submit" value="Save All Issues">
<div class="">
<table id="formset" class="form" border="1">
{% for form in issues_formset.forms %}
{% if forloop.first %}
<thead><tr>
{% for field in form.visible_fields %}
<th>{{ field.label|capfirst }}</th>
{% endfor %}
</tr></thead>
{% endif %}
<tr>
{% for field in form.visible_fields %}
<td>
{# Include the hidden fields in the form #}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field.errors.as_ul }}
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
</div>
</form>
hope it will help someone, please let me know if you have any comments

Django request.POST.get does not save to model field

I am trying to save data from request.POST dictionary. I would like to get the the 'name' value from request.POST which should correspond to the prefix. However, it does not happen and nothing gets saved to Name model. Also, I get no error. Could you please kindly advise how to solve this case? Thank you.
views.py
from django.shortcuts import render
from .forms import modelformset_factory, AssumptionsForm
from .models import Assumptions
from django.core.exceptions import ValidationError
import pdb
model_names = ['A', 'B']
def get_assumptions(request):
AssumptionFormset = modelformset_factory(
Assumptions, form=AssumptionsForm, extra=5)
if request.method == 'POST':
formsets = [AssumptionFormset(request.POST, prefix=thing) for thing in model_names]
if all([formset.is_valid() for formset in formsets]):
for formset in formsets:
for form in formset:
form.save(commit=False)
form.Name = request.POST.get('name')
form.save()
else:
formsets = [AssumptionFormset(prefix=thing) for thing in model_names]
return render(request, 'assumptions.html', {'formsets': formsets})
assumptions.html
<div class="form">
<form action="" method="post">
{% for formset in formsets %}
{% csrf_token %}
{{ formset.management_form }}
{{ formset.non_form_errors.as_ul }}
<h1>{{formset.prefix}}</h1>
<table id="formset" class="form">
{% for form in formset.forms %}
{% if forloop.first %}
<thead><tr>
{% for field in form.visible_fields %}
<th>{{ field.label|capfirst }}</th>
{% endfor %}
</tr></thead>
{% endif %}
<tr class="{% cycle 'row1' 'row2' %}">
{% for field in form.visible_fields %}
<td>
{# Include the hidden fields in the form #}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field.errors.as_ul }}
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<input type="hidden" id={{formset.prefix}} name={{formset.prefix}} />
{% endfor %}
<input type="submit" value="Submit">
</form>
</div>
forms.py
from django import forms
from django.forms import modelformset_factory, ModelForm
from .models import Assumptions
class AssumptionsForm(ModelForm):
class Meta:
model = Assumptions
fields = '__all__'
exclude = ['Name']
models.py
from django.db import models
from django.forms import ModelForm
class Assumptions(models.Model):
Worst = models.FloatField(null=True, blank=True, default=None)
Base = models.FloatField(null=True, blank=True, default=None)
Best = models.FloatField(null=True, blank=True, default=None)
Name = models.TextField(null=True, blank=True, default=None)
You should also pass "name=name" and "value={{formset.prefix}}" in your HTML form.
<input type="hidden" id={{formset.prefix}} name="name" value="{{formset.prefix}}" />
I figured out. The reason is that instance of the model was not created. Code should be as follows:
for form in formset:
assumption_data = form.save(commit=False)
assumption_data.Name = request.POST['name']
assumption_data.save()
where assumptions_data is an instance of Assumptions model. Thank you everyone for insights provided.

Django template does not output multiple forms and save to modelform

the issue is that template does not output multiple forms and save to Assumptions modelform. I am trying to save input from multiple forms by adding different names from template to Assumptions.Name field in Assumptions model. However, this approach does not work for some reason. Advise how to solve it would be highly appreciated. Thank you in advance.
views.py
from django.shortcuts import render
from .forms import modelformset_factory, AssumptionsForm
from .models import Assumptions
def get_assumptions(request):
if request.method == 'POST':
if 'name' in request.POST:
formset = modelformset_factory(Assumptions, form = AssumptionsForm, extra = 5)
if formset.is_valid():
print('valid form')
for form in formset:
print('Looping forms')
assumptions = form.save(commit='False')
assumptions.Name = 'name'
assumptions.save()
formset = modelformset_factory(Assumptions, form = AssumptionsForm, extra = 5)
return render(request, 'assumptions.html', {'formset': formset})
assumptions.html
{% for name in "AB" %}
<div class="form">
<form action="" method="post">
{% csrf_token %}
{{ formset.management_form }}
{{ formset.non_form_errors.as_ul }}
<table id="formset" class="form">
{% for form in formset.forms %}
{% if forloop.first %}
<thead><tr>
{% for field in form.visible_fields %}
<th>{{ field.label|capfirst }}</th>
{% endfor %}
</tr></thead>
{% endif %}
<tr class="{% cycle 'row1' 'row2' %}">
{% for field in form.visible_fields %}
<td>
{# Include the hidden fields in the form #}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field.errors.as_ul }}
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<input type="submit" name="{{name}}" value="save" />
</form>
</div>
{% endfor %}
models.py
from django.db import models
from django.forms import ModelForm
class Assumptions(models.Model):
Worst = models.FloatField(null=True, blank=True, default=None)
Base = models.FloatField(null=True, blank=True, default=None)
Best = models.FloatField(null=True, blank=True, default=None)
Name = models.TextField(null=True, blank=True, default=None)
forms.py
from django import forms
from django.forms import modelformset_factory, ModelForm
from .models import Assumptions
class AssumptionsForm(ModelForm):
class Meta:
model = Assumptions
fields = ['Worst', 'Base', 'Best']
exclude = ['Name']

Django formset for models without foreign key

I'm trying to use formset to create connections between my Neo4j nodes. These models have been constructed using the django-neomodel package. They aren't related using foreign keys, but since this isn't an inline formset that shouldn't matter, right?
models.py
class Person(DjangoNode):
uid = UniqueIdProperty()
name = StringProperty(max_length=50)
created_at = DateTimeProperty(default=datetime.now)
friends = RelationshipTo('Friend', 'FRIENDED', model=FriendRel)
# DjangoNode class must have a Meta class and documentation specifies 'django_node'
class Meta:
app_label = 'django_node'
class FriendRel(DjangoRel):
created_at = DateTimeProperty(default=datetime.now)
class Meta:
app_label = 'django_rel'
forms.py
class PersonForm(forms.ModelForm):
class Meta:
model = Person
# don't need fields for automatically assigned keys like `uid` and `created_at`
fields = ['name']
class FriendRelForm(forms.ModelForm):
class Meta:
model = FriendRel
exclude = () #"creating ModelForm without either 'fields' || 'exclude' attribute is prohibited"
FriendRelFormSet = formset_factory(FriendRelForm, extra=1)
form.html
<div>
<form action="" method="post">{% csrf_token %}
{{ form.as_p }}
<table class="table">
{{ friends.management_form }}
{% for form in friends.forms %}
{% if forloop.first %}
<thead>
<tr>
{% for field in form.visible_fields %}
<th>{{ field.label|capfirst }}</th>
{% endfor %}
</tr>
</thead>
{% endif %}
<tr class="{% cycle "row1" "row2" %} formset_row">
{% for field in form.visible_fields %}
<td>
<!-- Include the hidden fields in the form -->
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field.errors.as_ul }}
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<input type="submit" value="Save"/>
</form>
</div>
I'm expecting a "friend" formset to appear in the rendered form, but am not quite sure how to get it there. If I add the following I get an error:
class FriendRelForm(forms.ModelForm):
class Meta:
model = FriendRel
exclude = ()
fields = ['friends']
***ERROR*** django.core.exceptions.FieldError: Unknown field(s) (friends) specified for FriendRel

Saving from a django-leaflet widget

I am having trouble saving the point coordinates from a django-leaflet form. I can display the leaflet widget and add point markers but when I submit the form no geometry is sent. I know this from looking at what is posted back to the server in FireBug.
e.g.
csrfmiddlewaretoken 3fOhKMkrlMqIvQfqsq6Myx9agpsif2aQ
geom
name test
submit Save
Here is the code:
forms.py
from leaflet.forms.fields import MultiPointField
class LocationForm(forms.ModelForm):
geom = MultiPointField()
class Meta:
model = Location
fields = ['name', 'geom']
models.py
from leaflet.forms.fields import MultiPointField
class Location(models.Model):
locationid = models.IntegerField(primary_key=True)
name = models.CharField(max_length=256)
geom = MultiPointField()
def __unicode__(self):
return self.name
template file
{% load leaflet_tags %}
{% leaflet_js plugins="forms" %}
{% leaflet_css plugins="forms" %}
<form id="location_form" method="post" action="/addlocation/">
{% csrf_token %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in form.visible_fields %}
{{ field.errors }}
{{ field.help_text}}
{{ field }}
{% endfor %}
<div class="login-actions">
<button type="submit" name="submit" value="Save">Save</button>
</div> <!-- .actions -->
</form>
May be you missed, in settings:
'ENGINE': 'django.contrib.gis.db.backends.mysql', # if use mysql
in models:
from django.contrib.gis.db import models as gismodels
class Location(gismodels.Model):