Django - formset with crispy forms - missing management form data - django

I'm trying to render this form set:
ProductFormSet = modelformset_factory(
model=Product,
fields='__all__',
extra=5,
)
class ProductFormSetHelper(FormHelper):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.form_tag = False
self.layout = Layout(
Row(
'productCategory',
'name',
'measurement_unit',
)
)
self.render_required_fields = True
with this view:
def product_create(request):
helper = ProductFormSetHelper()
context = {
'helper': helper,
'title': 'Nuovi Prodotti',
}
if request.method == 'GET':
formset = ProductFormSet(queryset=Product.objects.none())
context['formset'] = formset
elif request.method == 'POST':
formset = ProductFormSet(request.POST)
context['formset'] = formset
if formset.is_valid():
formset.save()
messages.success(request, 'Prodotti aggiunti correttamente', fail_silently=True)
return HttpResponseRedirect(reverse('warehouse:products_list'))
else:
return render(request, 'warehouse/product_create.html', context)
return render(request, 'warehouse/product_create.html', context)
and this template:
{% extends "masterpage.html" %}
{% load static %}
{% block headTitle %}
<title>Nuovo Prodotto</title>
{% endblock %}
{% block contentHead %}
{% endblock %}
{% block contentBody %}
{% load document_tags %}
{% load custom_tags %}
{% load crispy_forms_tags %}
<FORM method="POST" autocomplete="off">
<div class="alert alert-info">
{{ title }}
</div>
{{ formset.management_form }}
{% crispy formset helper %}
<input type="submit" class="btn btn-primary margin-left" value="SALVA">
</FORM>
{% endblock %}
Problem is that when I submit the form I get the: ValidationError: management form data are missing! First of all, using crispy forms the management data should be included, second even if I explicitly call with the tag, I still get the ValidationError.
in Every forum I searched online, everyone was missing the management form tag, so I have no clue on what could be wrong....
Any idea?
thank you very much
C

in your Post method you didnt include the "queryset"
formset = ProductFormSet(queryset=Product.objects.WHATEVER_YOU_CHOOSE, request.POST)
and for crispy forms
{% load crispy_forms_tags %}
<form method="post">
{% csrf_token %}
{{ formset|crispy }}
{{ formset.management_form }}
<input type="submit" class="btn btn-primary margin-left" value="SALVA">
</form>
and because you are not working in get_context_data fucntion, just keep it simple
context = {'formset':formset}
Exemple from the docs
from django.forms import modelformset_factory
from django.shortcuts import render
from myapp.models import Author
def manage_authors(request):
AuthorFormSet = modelformset_factory(Author, fields=('name', 'title'))
if request.method == "POST":
formset = AuthorFormSet(
request.POST, request.FILES,
queryset=Author.objects.filter(name__startswith='O'),
)
if formset.is_valid():
formset.save()
# Do something.
else:
formset = AuthorFormSet(queryset=Author.objects.filter(name__startswith='O'))
return render(request, 'manage_authors.html', {'formset': formset})

Hope it can help others. Since I'm having troubles configuring the thousand separator, I tried to change the input.html widget in /templates/django/forms/widgets by adding the |intcomma filter.
This altered the management form data ( one field is set to 1000 by default ), thus corrupting the whole form.

Related

Django crispy forms - bootstrap4 table_inline_formset template rendering extra row on top

I am using the bootstrap4/table_inline_formset.html template in a FormHelper from django-crispy-forms. The table is rendered correctly in the template, but an extra form always appears at the beginning of the table, which is not visible when submitting the form.
forms.py:
class MetricForm(forms.ModelForm):
class Meta:
model = Metric
exclude = ['auto_value','occurrence']
class MetricFormSetHelper(FormHelper):
def __init__(self, *args, **kwargs):
super(MetricFormSetHelper, self).__init__(*args, **kwargs)
self.add_input(Submit('submit', 'Submit', css_class="btn btn-success"))
self.template = 'bootstrap4/table_inline_formset.html'
views.py:
#login_required
def create_occurrence(request, pk):
try:
site = Site.objects.get(id=pk)
except Site.DoesNotExist:
raise Http404("Site does not exist")
form = OccurrenceForm(request.POST or None, initial={'site':site})
MetricFormset = modelformset_factory(Metric, form=MetricForm, extra=3)
formset = MetricFormset(queryset=Metric.objects.none())
helper = MetricFormSetHelper()
if form.is_valid():
occurrence = form.save(commit=False)
occurrence.added_by = request.user
occurrence.site = site
occurrence.save()
form.save_m2m()
metric_formset = MetricFormset(request.POST)
if metric_formset.is_valid():
for metric_form in metric_formset.forms:
if all([metric_form.is_valid(), metric_form.cleaned_data != {}]):
metric = metric_form.save(commit=False)
metric.occurrence = occurrence
metric.save()
messages.success(request, "Occurrence created successfully.")
execute_from_command_line(["../manage_dev.sh", "updatelayers", "-s", "archaeology"])
return redirect(occurrence.get_absolute_url())
context = {
'form': form,
'site':site,
'formset':formset,
'helper': helper,
}
return render(request, "archaeology/occurrence_form.html", context=context)
template:
...
<form action="" method="post">
{% csrf_token %}
{{ form|crispy }}
<h4>Metrics</h4>
{{ formset.management_form }}
{% crispy formset helper %}
{% if form.instance.pk != None %}
<a class="btn btn-danger" href="{% url 'delete_occurrence' occurrence.id %}">{% trans "Delete" %}</a>
{% endif %}
</form>
...
Any idea how to remove the extra row?
I had to change the template and remove the lines that printed an empty form at the beginning.
table_inline_formset.html:
<tr class="d-none empty-form">
{% for field in formset.empty_form %}
{% include 'bootstrap4/field.html' with tag="td" form_show_labels=False %}
{% endfor %}
</tr>

Django form and formset are not valid

I'm trying to make a view containing one form and one formset but something does not work.
both form and formset after checking if .is_valid returns false. I do not really undersand why it is like that
def ProductCreateView(request):
context = {}
created_product = None
form = ProductForm()
if request.method == 'POST':
form = ProductForm(request.POST)
if form.is_valid():
created_product = form.save()
print("Successfully created new product: {}".format(created_product))
else:
print("form is not valid")
#print(request.POST) returns csrfmiddlewaretoken ...
#print(request.FILES) returns file : inmemoryuploadedfile ...
#print(list(request.POST.items()))
context['form'] = form
formset = ProductPhotoInlineFormset()
if request.method=='POST':
formset = ProductPhotoInlineFormset(request.POST or None, request.FILES or None, instance=created_product)
if formset.is_valid():
created_images = formset.save()
print("Successfully created new imagest: {}".format(created_images))
else:
print("formset is not valid")
context['formset'] = formset
return render(request, "Ecommerce/create_product_test.html", context)
my template - create_product_test.html
{% extends 'base.html' %}
{% block content %}
<div id="alert-box">
</div>
<div id="image-box" class="mb-3">
</div>
<div id="image-box"></div>
<div class="form-container">
<button class="btn btn-primary mt-3 not-visible" id="confirm-btn">confirm</button>
<form method="POST" enctype="multipart/form-data" action="" id="image-form">
{% csrf_token %}
<div>
{{form}}
{{formset.management_form}}
{{formset.as_p}}
</div>
</form>
</div>
{% endblock content %}
forms.py file
ProductPhotoInlineFormset = inlineformset_factory(
Product,
Photo,
fields=('file',),
form=PhotoForm,
extra=1,
)
where is the problem ?
You can find out what's wrong with the form with:
print(form.errors)
print(form.non_field_errors())
and for a formset:
print(formset.errors)
print(formset.non_form_errors())
This way, you can easily find out why the form is not valid.

Trying to save to database via ModelForm Django

My form is not saving to the database or at least i know the form is not valid i just dont know why? because it will always skip to the else in the if form.is_valid() (print("didnt work!"))
the view.py:
def index(request):
component = Component.objects.all()
form = ComponentModelForm()
if request.method == 'POST':
form = ComponentModelForm(request.POST)
if form.is_valid():
form.save()
return redirect('/maintenance')
else:
form = ComponentModelForm()
print("didnt work!")
context = {
'components': component,
'form': form,
}
return render(request, 'maintenance/index.html', context)
forms.py:
class ComponentModelForm(forms.ModelForm):
note = forms.CharField(widget=forms.Textarea)
image = forms.ImageField(error_messages = {'invalid':("Image files only")}, widget=forms.FileInput)
class Meta:
model = Component
fields = ("name",
"manufacturer",
"model",
"serial_number",
"price",
"note",
"image",
"parent",)
the template form:
{% load widget_tweaks %}
<form class="component-update-grid" enctype="multipart/form-data" method='POST' action=''>
{% csrf_token %}
<div class="component-form-data">
<span class="component-label-text">Name</span>
{% render_field form.name class="component-form-data-inputs" %}
<span class="component-label-text">Manufacturer</span>
{% render_field form.manufacturer class="component-form-data-inputs" %}
<span class="component-label-text">Model</span>
{% render_field form.model class="component-form-data-inputs" %}
<span class="component-label-text">Serial Number</span>
{% render_field form.serial_number class="component-form-data-inputs" %}
<span class="component-label-text">Price</span>
{% render_field form.price class="component-form-data-inputs" %}
<span class="component-label-text">Note</span>
{% render_field form.note class="component-form-data-inputs" %}
{% render_field form.parent class="component-form-data-inputs " %}
<input type="submit" class="button1" value='Create Component' />
</div>
<div class="component-form-img">
<img class="maintenance-component-img" src='{%static 'imgs/sidebar/logo.png'%} ' />
{% render_field form.image %}
</div>
</form>
You should not construct a new form when the form fails: a failed form will render the errors, such that the user knows what is going wrong, so:
def index(request):
component = Component.objects.all()
form = ComponentModelForm()
if request.method == 'POST':
form = ComponentModelForm(request.POST)
if form.is_valid():
form.save()
return redirect('/maintenance')
else:
# Don't create a new form!
print("didnt work!")
context = {
'components': component,
'form': form,
}
return render(request, 'maintenance/index.html', context)

Django booleanfield modelform

So I'm making a to-do list and I made a booleanfield modelform which has attribute "complete". I want that user to check it when it's complete and I tried wriring if task.complete == True cross out the item and it didn't work(it only worked when I checked it from the admin panel). Then I tried form.complete instead of task.complete and it doesn't do anything.
models:
class Task(models.Model):
title = models.CharField(max_length=200)
complete = models.BooleanField(default=False)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
forms:
from .models import *
class TaskForm(forms.ModelForm):
title = forms.CharField(widget= forms.TextInput(attrs={'placeholder':'Add new task...'}))
class Meta:
model = Task
fields = '__all__'
html:
<div class="main-container">
<form method="POST" action="/">
{% csrf_token %}
<input type="submit"/>
{{ form.title }}
</form>
{% for task in tasks %}
{{ form.complete }}
{% if form.complete == True %}
<h1><strike>{{task.title}}</strike></h1>
{% else %}
<h1>{{task.title}}</h1>
{% endif %}
{% endfor %}
</div>
views:
def index(request):
tasks = Task.objects.order_by('-created')
form = TaskForm()
if request.method == 'POST':
form = TaskForm(request.POST)
if form.is_valid():
form.save()
return redirect('/')
context = {
'tasks': tasks,
'form': form,
}
return render(request, 'to-do.html', context)
There are some problems with your code I don't know how to explain. Try this. It should work.
<div class="main-container">
<form method="POST" action="/"> # create new task
{% csrf_token %}
{{ form.title }}
<input type="submit" name="new-task"/>
</form>
<form method="post"> # update task status
{% csrf_token %}
{% for task in tasks %}
{% if task.complete %}
<input type="checkbox" name="if_completed" checked value="{{ task.pk }}">
<h1><strike>{{task.title}}</strike></h1>
{% else %}
<input type="checkbox" name="if_completed" value="{{ task.pk }}">
<h1>{{task.title}}</h1>
{% endif %}
{% endfor %}
<input type="submit" name="update-task"/>
</form>
</div>
view.py (Only for form, add your other code with it)
from django.http import HttpResponseRedirect
from django.urls import reverse
def index(request):
tasks = Task.objects.order_by('-created')
form = TaskForm()
if request.method == 'POST':
if 'new-task' in request.POST:
form = TaskForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('home')) # replace home with url name where you want to redirect
elif 'update-task' in request.POST:
task_pk = request.POST.getlist("if_completed")
for i in task_pk:
Task.objects.filter(pk=i).update(complete=True) # I have replace pk with i here
return HttpResponseRedirect(reverse('home')) # replace home with url name where you want to redirect
context = {
'tasks': tasks,
'form': form,
}
return render(request, 'to-do.html', context)
in forms.py
class TaskForm(forms.ModelForm):
class Meta:
model = Task
fields = ('title',)
widgets = {
'title': forms.TextInput(attrs={'placeholder':'Add new task...'})
}
This should work. There may be be some error because of typos or indentation. Let me know if you get any issue
def index(request):
tasks = Task.objects.order_by('-created')
form = TaskForm()
context = {
'tasks': tasks,
'form': form,
}
if request.method == 'POST':
if 'new-task' in request.POST:
form = TaskForm(request.POST)
if form.is_valid():
form.save()
elif 'update-task' in request.POST:
task_pk = request.POST.getlist("if_completed")
for i in task_pk:
Task.objects.filter(pk=pk).update(complete=True)
return render(request, 'to-do.html', context)

How do I use two lists in one Django for tag?

I have two letters that I would like to show in my template at the same time. How can I do it in the template the fastest and easiest way?
Where set_1 = A, B, C, D,
ser_2 = result from my django queryset
Is there something like the below? Any help will be appreciated
{% for b in set_1 and a in set_2 %}
<p>{{ b }} - {{ a }}</p>
{% endfor %}
EDIT:
How can I use this when my queryset returns a list of fields in my form, as in the view below?
views.py
def account(request):
data_now = datetime.datetime.now().strftime("%Y-%m-%d")
test = Time.objects.filter(day_time__day_name='Monday')
#my form
TimeFormSet = modelformset_factory(Time, fields=('free_or_no',), labels={'free_or_no': '*odznacz jeżeli zajęte',})
if request.method == "POST":
formset = TimeFormSet(
request.POST,
queryset=Time.objects.filter(day_time__day_name='Monday'),
)
if formset.is_valid():
formset.save()
return HttpResponseRedirect(reverse('app:account'))
else:
formset = TimeFormSet(queryset=Time.objects.filter(day_time__day_name='Monday'))
list_form = zip(formset, test)
context = {'data_now': data_now, 'time_edit_form': formset, 'test': test, 'list_form': list_form}
return render(request, 'account.html', context)
If I do everything as in your link, my browser returns an error:
IntegrityError at /account/
NOT NULL constraint failed: app_time.time_equivalent
html file
<form action="." method="post">
{% csrf_token %}
{{ time_edit_form.management_form }}
{% for item1, item2 in list_form %}
<p>{{item2}} {{item1}}</p>
{% endfor %}
<button type="submit" class="btn btn-block btn-primary"> ZapiszXXX</button>
</form>