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
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>
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.
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)
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)
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>