Save Formset in DJANGO - python

I have a formset from a model, but when I try to save more than one formset I get the error:
KeyError at /auditoria/auditoria_tab/ 'id_control'
My view:
#staff_member_required
def AuditoriaView(request):
class RequiredFormset(BaseFormSet):
def __init__(self,*args,**kwargs):
super(RequiredFormset,self).__init__(*args,**kwargs)
for form in self.forms:
form.empty_permitted = False
auditoriaFormset = formset_factory(AuditoriaForm,max_num=31,formset=RequiredFormset)
if request.method == 'POST':
usuario = request.user
accion = 'Registro nuevo Detalle de Auditoria'
formset = auditoriaFormset(request.POST)
for form in formset.forms:
if formset.is_valid():
obj = form.save(commit=False)
#obj.id_control = control
obj.auditor = request.user
obj.save()
else:
formset = auditoriaFormset()
return render_to_response('audit_auditoria.html',
{'formset':formset},
context_instance=RequestContext(request))
My form:
class AuditoriaForm(forms.ModelForm):
id_control = forms.ModelChoiceField(queryset=Control.objects.all().order_by('nombre'),\
required=True,label='Control', widget=forms.Select(attrs={'class':'form-control'}))
id_analista = UserModelNameChoiceField(queryset=User.objects.filter(is_staff=False),\
required = True, label='Analista', widget=forms.Select(attrs={'class':'form-control'}))
fecha = forms.DateField(widget=DateInputCustom())
id_tipoerror = forms.ModelChoiceField(queryset=TipoError.objects.all(),required=True, label='Tipo Falla', widget=forms.Select(attrs={'class':'form-control'}))
id_sharepoint = forms.IntegerField(label='Sharepoint', widget=forms.NumberInput(attrs={'class':'form-control','style': 'width:80px'}))
id_estado = forms.ModelChoiceField(queryset=Estado.objects.all(),required=True, label='Estado', widget=forms.Select(attrs={'class':'form-control'}))
observaciones = forms.CharField(widget=forms.Textarea(attrs={'rows':5,'cols':30}))
class Meta:
model = DetalleAuditoria
exclude = ('auditor',)
def __init__(self,*args,**kwargs):
super(AuditoriaForm,self).__init__(*args,**kwargs)
self.helper = FormHelper(self)
def clean(self):
cleaned_data = super(AuditoriaForm,self).clean()
idc = cleaned_data['id_control']
fechac = cleaned_data['fecha']
try:
DetalleAuditoria.objects.get(id_control = idc,fecha = fechac )
raise forms.ValidationError('Esta Incidencia ya ha sido registrada')
except DetalleAuditoria.DoesNotExist:
pass
return cleaned_data
Template:
<form id="form_audit" class="form" method="post" action="/auditoria/auditoria_tab/">
<table class="table table-responsive table-condensed table-bordered">
{{ formset.management_form }}
{% for form in formset.forms %}
<thead>
<th class="info"colspan="6">
<label >{{ form.id_control.label|capfirst }} </label>
{{ form.id_control }}
</th>
</thead>
<thead>
<th>{{ form.id_analista.label|capfirst }}</th>
<th>{{ form.id_sharepoint.label|capfirst }}</th>
<th>{{ form.fecha.label|capfirst }}</th>
<th>{{ form.id_tipoerror.label|capfirst }}</th>
<th>{{ form.id_estado.label|capfirst }}</th>
<th>{{ form.observaciones.label|capfirst }}</th>
<thead>
<tbody>
<tr class="{% cycle row1,row2 %} formset_row">
<td>{{ form.id_analista }}</td>
<td>{{ form.id_sharepoint }}</td>
<td>{{ form.fecha }}</td>
<td>{{ form.id_tipoerror }}</td>
<td>{{ form.id_estado }}</td>
<td>{{ form.observaciones }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<input type="submit" class="btn btn-info" value="Registrar">
</form>
{% block extra_js %}
<script src="{{STATIC_URL|default:"/static/"}}admin/js/jquery.formset.js"></script>
<script type="text/javascript">
function renewDatePickers() {
$('.datepicker').datepicker('destroy');
$('.datepicker').datepicker({
format: "yyyy-mm-dd",
startDate: "2014-01-01",
endDate: "2020-01-01",
autoclose: true
});
}
$(renewDatePickers);
$('.formset_row').formset({
addText: 'Agregar Detalle Auditoria',
deleteText: 'Eliminar',
prefix: '{{ formset.prefix }}',
added: renewDatePickers
});
</script>
{% endblock extra_js %}
how can I use the field ID_CONTROL from the first formset in the other formsets, when I want to save the data.
I am not sure if I am using in the right way the formsets?.
Any advice
Thanks in Advance
UPDATE:
I get the error in the "clean" function. When the view try to save the second formset ther error appeare in this line in the:
idc = cleaned_data['id_control']
In the error page I can see the post and I can see for the first formset the id_control value, but for the second formset I see all the fields except id_control.

It seems to me a non sense to put the same control id into the next forms in that way.
Anyway, to solve what you asking for, this should be valid (having different "fecha" value the forms):
id_control=None
if request.method == 'POST':
usuario = request.user
accion = 'Registro nuevo Detalle de Auditoria'
formset = auditoriaFormset(request.POST)
for counter, form in enumerate(formset.forms):
if formset.is_valid():
obj = form.save(commit=False)
if counter>0:
obj.id_control = id_control
obj.auditor = request.user
obj.save()
if counter==0:
id_control=obj.id_control
else:
formset = auditoriaFormset()

Related

How to add Django Object Models to A Separate List

I am creating an app for a school schedule using Django. Admin users can currently add classes to a list. Student users can view that list but I want to create a button/link that will add that class object to a separate list that will act as their applied classes.
Models.py
class Class(models.Model):
def __str__(self):
return self.name
name = models.CharField(max_length=50)
crn = models.CharField(max_length=5, validators=[RegexValidator(r'^\d{1,10}$')])
grade_mode = models.CharField(max_length=50)
subject = models.CharField(max_length=3)
course_num = models.CharField(max_length=4, validators=[RegexValidator(r'^\d{1,10}$')])
section_num = models.CharField(max_length=3, validators=[RegexValidator(r'^\d{1,10}$')])
credit_hours = models.FloatField(validators=[MinValueValidator(0.0), MaxValueValidator(5.000)])
teacher = models.CharField(max_length=50)
Views.py
#login_required
#dec.student_required
def index(request):
class_list = Class.objects.all().order_by("-name")
# Allow for Posting of New Classes to Schedule.
if request.method == "POST":
form = ClassesForm(request.POST)
if form.is_valid():
form.save()
form = ClassesForm()
messages.success(request, "Class Added to Registry!")
else:
form = ClassesForm()
context_dict = {'classes': class_list,
'form': form}
return render(request, 'index.html', context=context_dict)
Index.HTML Add Button
<table>
<tbody>
{% if classes %}
{% for class in classes %}
<tr>
<td>Class Name: </td>
<td>Subject: </td>
<td>CRN: </td>
<td>Course Number: </td>
<td>Section Number: </td>
<td>Credit Hours: </td>
<td>Teacher: </td>
<td>Grade Mode: </td>
<td>Add Class</td>
</tr>
<tr>
<td>{{ class.name }}</td>
<td>{{ class.subject }}</td>
<td>{{ class.crn }}</td>
<td>{{ class.course_num }}</td>
<td>{{ class.section_num }}</td>
<td>{{ class.credit_hours }}</td>
<td>{{ class.teacher }}</td>
<td>{{ class.grade_mode }}</td>
<td>
<form method="GET">
{% csrf_token %}
<button class="addToSchedule" type="submit" value="add" name="Add Class">
Add Class
</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
{% else %}
<strong>There are no classes added.</strong>
{% endif %}
</table>
Image on Website.
So far I can print out the models on their own separate list as shown above but I don't know where to go in regards to the button or if a button is even the correct way of doing it. I want to click on a button and that model will be added and saved to the Added Classes section above.

how to instance and update multiple row in a table with Django

I have a simple table with some rows:
My goal is to instantiate the current value of the quantity and eventually save the new data for all the lines.
At the moment I have a simple view:
#login_required
def compilaDocMultiRow(request,pk):
member = get_object_or_404(testaDoc, pk=pk)
family = corpoDoc.objects.filter(doc=pk)
if request.method == "POST":
form = multiCorpoDocForm(request.POST or None)
if form.is_valid():
reg = form.save(commit=False)
reg.doc_id = member.pk
reg.save()
return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
else:
print(form.errors)
else:
form = multiCorpoDocForm()
return render(request, "compilaDocMultiRow.html", {'family':family, 'member':member, 'form':form})
Is there a way to do this using only Django?
EDIT 1
I was able to instantiate the value with widget tweaks. I am left with the problem of passing the pk of the line to the form and saving the value.
This is the html code:
{% for item in family %}
<tr>
<td>{{ item.item}}</td>
<td>{{ item.desc }}</td>
{% with field=form.qt %}
<td width="15%">{% render_field field class="form-control" placeholder=item.qt %}
</td>
{% endwith %}
<td></td>
<td></td>
</tr>
{% endfor %}

Django Class based Form only updating the final post value

As the title states, I have a table that I'm attempting to update that only updates the final value in the post, from what I understand if I want to update multiple records I must iterate over my request object and update a form instance with the specified ID in my db.
Before submission all records have a price_checked of 0.
and then after - you can see the final value from the post request updates all the records!
postgres table
The code in question that updates my model form instance.
def post(self,request):
if request.method=='POST':
for key in request.POST.getlist('product_id'):
product_update = ProductTable.objects.get(id=key)
form = ProductUpdateForm(request.POST,instance=product_update)
print(form)
if form.is_valid():
form.save()
messages.success(request,message='price checked...')
return redirect('product')
is anyone able to assist? I've been at this point for over 2 weeks.
models/form/view for reference.
models.py
from django.db import models
class ProductTable(models.Model):
id = models.AutoField(
primary_key=True,
editable=False
)
product_name = models.TextField(max_length=255,null=False)
price = models.DecimalField(max_digits=6,decimal_places=2,null=False)
from_date = models.DateTimeField()
to_date = models.DateTimeField(null=True)
price_checked = models.IntegerField(default=0,null=False)
def __str__(self : str) -> str:
return f"{self.product_name} - {self.id} with a price check of {self.price_checked}"
forms.py
from django.forms import ModelForm
from .models import ProductTable
class ProductUpdateForm(ModelForm):
class Meta:
model = ProductTable
fields = ('price_checked',)
views.py
from typing import Any, Dict
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import redirect
from django.views.generic import ListView
from django.db import transaction
from .forms import ProductUpdateForm
from .models import ProductTable
class TableDataView(LoginRequiredMixin,ListView):
model = ProductTable
context_object_name = 'product'
template_name = 'tables/product.html'
def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
context = super().get_context_data(**kwargs)
context['product'] = ProductTable.objects.filter(pk__in= [4607,4642, 4645])
return context
def post(self,request):
if request.method=='POST':
for key in request.POST.getlist('product_id'):
product_update = ProductTable.objects.get(id=key)
form = ProductUpdateForm(request.POST,instance=product_update)
print(form)
if form.is_valid():
form.save()
messages.success(request,message='price checked...')
return redirect('product')
product.html
{% extends '_base.html' %} {% block content %} {% load crispy_forms_tags %}
<form method="POST" action="">
{% csrf_token %}
{{ form|crispy }}
<div class="container mx-flex pt-5 mb-5">
<table class="table" id="table">
<thead>
<tr>
<th>Product</th>
<th>Price</th>
<th>Date</th>
<th>Input</th>
</tr>
</thead>
<tbody>
{% for data in product %}
<tr>
<th>{{ data.product_name }}</th>
<th>{{ data.price }}</th>
<th>{{ data.from_date }}</th>
<th>
<input type="hidden" name="product_id" value="{{data.id}}" />
<input class="form" name="price_checked" id="priced_checked" type="number" placeholder="{{ data.price_checked }}"/>
<input type="submit" value="OK">
</th>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</form>
{% endblock content %}
request.POST
<QueryDict: {'csrfmiddlewaretoken': ['p0j3e0UbrY1VuFEAnJWopaCICCxOPj8v2OkLRZZiGlUa4YtxGwduD2bAIrm91VKe'], 'product_id': ['4607', '4642', '4645'], 'price_checked': ['1', '2', '3']}>
after read formset documents,I think it should looks like this.
the view python return a formset to template,and in the post function create formset by request.POST
def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
context = super().get_context_data(**kwargs)
ProductFormSet = modelformset_factory(ProductTable)
context['product_formset'] = ProductFormSet(queryset=ProductTable.objects.filter(pk__in= [4607,4642, 4645]))
return context
def post(self,request):
if request.method=='POST':
ProductFormSet = modelformset_factory(ProductTable)
formset = ProductFormSet(request.POST)
if formset.is_valid():
formset.save()
messages.success(request,message='price checked...')
return redirect('product')
in the template I have mark the changes,the important thing is {{ product_formset.management_form }} in the begin form.and {{ form.id }} of each loop,the each cell data should be {{ form.product_name.value() }} or {{ form.product_name.data }},you can try that
{% extends '_base.html' %} {% block content %} {% load crispy_forms_tags %}
<form method="POST" action="">
{% csrf_token %}
{{ form|crispy }}
<!-- changed -->
{{ product_formset.management_form }}
<div class="container mx-flex pt-5 mb-5">
<table class="table" id="table">
<thead>
<tr>
<th>Product</th>
<th>Price</th>
<th>Date</th>
<th>Input</th>
</tr>
</thead>
<tbody>
<!-- changed -->
{% for form in product_formset %}
{{ form.id }}
<tr>
<td>{{ form.product_name.value() }}</td>
<td>{{ form.price.value() }}</td>
<td>{{ form.from_date.value() }}</td>
<td>
{{ form.price_checked }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<input type="submit" value="submit">
</form>
{% endblock content %}
maybe this will not help you,because I never used the django form things.
I guess you are a precise man who want to follow every django's principle.
But as I know,traditional form has much limitation,some ajax or reactjs has more flexible.
A couple changes to support formsets:
Use the same formset for updating and displaying the objects, but disable the fields you don't want to be updated (make them read-only):
class ProductUpdateForm(ModelForm):
disabled_fields = ['product', 'price', 'date_added', 'date_removed']
class Meta:
model = ProductTable
fields = ('product', 'price', 'date_added', 'date_removed', 'price_checked',)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in self.disabled_fields:
self.fields[field].disabled = True
Then update the view to use the form for displaying and for updating:
from django.forms import modelformset_factory
class TableDataView(LoginRequiredMixin,ListView):
model = ProductTable
context_object_name = 'product'
template_name = 'tables/product.html'
def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
context = super().get_context_data(**kwargs)
ProductTableFormset = modelformset_factory(ProductTable, form=ProductUpdateForm,)
context['formset'] = ProductTableFormset(queryset=ProductTable.objects.filter(pk__in=[4607,4642, 4645]))
return context
def post(self,request):
ProductTableFormset = modelformset_factory(ProductTable, form=ProductUpdateForm)
if request.method == 'POST':
formset = ProductTableFormset(request.POST, queryset=ProductTable.objects.filter(pk__in=[4607,4642, 4645]))
if formset.is_valid():
formset.save()
messages.success(request,message='price checked...')
return redirect('product')
And in your template, render the whole formset:
{% extends '_base.html' %}
{% block content %}
{% load crispy_forms_tags %}
<form method="POST" action="">
{% csrf_token %}
{{ form|crispy }}
<div class="container mx-flex pt-5 mb-5">
<table class="table" id="table">
{{ formset }}
</table>
</div>
</form>
{% endblock content %}
EDIT: If you want the simplest change you can do to fix this (so you don't have to work on the styles for the formset), you can do away with the form for the post and update the products as is:
def post(self,request):
if request.method == 'POST':
products_to_update = []
for id, price_checked in zip(request.POST.getlist('product_id'), request.POST.getlist('price_checked')):
product = ProductTable.objects.get(id=id)
product.price_checked = price_checked
products_to_update.append(product)
ProductTable.objects.bulk_update(products_to_update, ['price_checked'])
messages.success(request,message='price checked...')
return redirect('product')

How to export to XML format from DJango SQLIte

I have a table of datasets in a django template like so:
{% extends 'base.html' %}
{% block title %}Catalogs{% endblock %}
{% block content %}
<table class="table table-bordered" id="tblData">
<thead>
<tr>
<th>DatasetName</th>
<th>Type</th>
<th>Classification</th>
<th>OriginalSource</th>
<th>OriginalOwner</th>
<th>YearOfOrigin</th>
</tr>
</thead>
</table>
{% for catalog in object_list %}
<div class="container">
<table class="table table-bordered">
<tbody>
<tr>
<td>
<form>
<p><input type="checkbox" id="agreeCheckbox" name="agreeCheckbox" value="agreeCheckbox" onchange="toggleLink(this);"></p>
</form>
</td>
<td>{{ catalog.DatasetName }}</td>
<td>{{ catalog.Type }}</td>
<td>{{ catalog.Classification }}</td>
<td>{{ catalog.OriginalSource }}</td>
<td>{{ catalog.OriginalOwner }}</td>
<td>{{ catalog.YearOfOrigin }}</td>
</tr>
</tbody>
</table>
</div>
<div class="card-footer text-center text-muted">
Update |
Delete |
Export to XML
</div>
{% endfor %}
{% endblock content %}
I have implemented the check box functionality for each of the rows of the table like so:
function toggleLink(checkBox)
{
var link1 = document.getElementById("agreeLink1");
var link2 = document.getElementById("agreeLink2");
var link3 = document.getElementById("agreeLink3");
if (checkBox.checked)
{
link1.style.display = "inline";
link2.style.display = "inline";
link3.style.display = "inline";
}
else {
link1.style.display = "none";
link2.style.display = "none";
link3.style.display = "none";
}
}
I have a model of datasets like so:
class Catalog(models.Model):
DatasetName = models.CharField(max_length=280)
Type = models.CharField(max_length=280)
Classification = models.CharField(max_length=280)
OriginalSource = models.CharField(max_length=280)
OriginalOwner = models.CharField(max_length=280)
YearofOrigin = models.IntegerField(default=0)
def get_absolute_url(self):
return reverse('catalog_detail', args=[str(self.id)])
def __str__(self):
return self.DatasetName
I have implemented a serializerin the view.py file that exports all the datasets like so:
def export_to_xml(request):
from django.core import serializers
data = serializers.serialize("xml", Catalog.objects.all())
from django.core.files import File
f = open('catalogs.xml', 'w')
myfile = File(f)
myfile.write(data)
myfile.close()
return HttpResponse("All done!")
I want to export only those datasets whose checkboxes have been checked. Each dataset is written as a table with a checkbox. I want to export in XML format only those datasets whose checkbox has been checked. Does anybody have any idea on how to do it?
Something like this.
template
<form>
<p><input type="checkbox" id="agreeCheckbox" name="agreeCheckbox" value="{{catalog.id}}" onchange="toggleLink(this);"></p>
</form>
views.py
def export_to_xml(request):
if request.method == 'POST':
#gives list of id of inputs
list_of_input_ids=request.POST.getlist('agreeCheckbox')
from django.core import serializers
data = serializers.serialize("xml", Catalog.objects.filter(id__in=list_of_input_ids))
from django.core.files import File
f = open('catalogs.xml', 'w')
myfile = File(f)
myfile.write(data)
myfile.close()
return HttpResponse("All done!")

Using queryset to 'filter' within form to display results from DB depending on option selected

I'm trying to develop a view that has a list of records. Within that view is a 'filter' box I'd like to use so that when users select 'HK' or 'JP' as a location. It only displays records submitted for 'HK' or 'JP' depending on whats been selected > Submitted.
I have the layout working, but whenever I'm submitting a new 'filter' option, this returns all results no matter what.
Can anyone provide some insight? I suspect the problem lies in the views context, but I am just stuck.
views.py
def CLIENT_Overtime_Results(request):
overtime_data = Overtime.objects.all()
location = None
if request.method == 'POST':
form = OvertimeForm(data=request.POST)
if form.is_valid():
location = form.data['location']
overtimeid = Location.objects.all()
#overtime_period = Overtime.objects.filter(
else:
form = OvertimeForm()
template_name = "overtime/UBS_Overtime_Results.html"
context = {
'form': form,
'location': location,
'overtime_data': overtime_data,
}
return render(request, template_name, context)
Forms.py
class OvertimeForm(forms.ModelForm):
location = forms.ModelChoiceField(
queryset=Location.objects.all(),
widget=forms.Select(attrs={'class': 'form-control'}))
class Meta:
model = Overtime
fields = ['location']
Filter button/options referenced in 'col-md-3'
HTML
<div class="row">
<form method="POST">
{% csrf_token %}
<div class="col-md-3">{{ form.location }}</div>
<div class="col-md-3">
<button type="submit" class="btn btn-default">{% trans "Filter" %}</button>
</div>
<div class="col-md-6">
<button type="submit" class="btn btn-default pull-right m-left-5" name="pdf">{% trans "PDF Report" %}</button>
<button type="submit" class="btn btn-default pull-right" name="excel">{% trans "Excel Report" %}</button>
</div>
</form>
</div>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>{% trans "ID" %}</th>
<th>{% trans "Date" %}</th>
{% if not location %}
<th>{% trans "Location" %}</th>
{% endif %}
<th>{% trans "Employee Name" %}</th>
<th>{% trans "Client" %}</th>
<th>{% trans "Hours" %}</th>
<th>{% trans "Overtime Type" %}</th>
<th>{% trans "Billable Type" %}</th>
<th>{% trans "Approval" %}</th>
</tr>
</thead>
<tbody>
{% for od in overtime_data %}
<tr>
<td>{{ od.overtime_ID }} </td>
<td>{{ od.overtimeDateStart }}</td>
{% if not location %}
<td>{{ od.location }}</td>
{% endif %}
<td>{{ od.employee }}</td>
<td>{{ od.client }} </td>
<td>{{ od.hours }} </td>
<td>{{ od.overtime_type }}</td>
<td>{{ od.billable_type }}</td>
<td>{{ od.approval|linebreaksbr }}</td>
</tr>
{% empty %}
<tr>
<td colspan="9" class="text-center">{% trans "No overtime history." %}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
This is how the page looks right now: http://imgur.com/ZjKzWMA
Answering my own question for future people coming to this page! As recommended by Daniel Roseman. I needed to include a filtering in the form.is_valid(): argument.
I added the following to my code:
overtime_data = Overtime.objects.filter(location=location)
This essentially filters and matches based on the updated location matching the location field in my model.
Updated code looks like this:
def CLIENT_Overtime_Results(request):
overtime_data = Overtime.objects.all()
location = None
if request.method == 'POST':
form = OvertimeForm(data=request.POST)
if form.is_valid():
location = form.data['location']
overtimeid = Location.objects.all()
overtime_data = Overtime.objects.filter(location=location) #Added for filtering*
else:
form = OvertimeForm()
template_name = "overtime/UBS_Overtime_Results.html"
context = {
'form': form,
'location': location,
'overtime_data': overtime_data,
}
return render(request, template_name, context)

Categories