Django Datepicker and Autocomplete not rendering when used together - javascript

i am using Django, bootstrap_datepicker_plus and django-autocomplete-light. I had no issues when i used the 2 libraries separately, but when i attempt to use them both in the same form, for some reason, only the autocomplete widget is working, while the datepicker widget is not. I am not sure why, maybe it is due to conflicting libraries, but are there any solutions for this issue? I tried to search online but could not find any solution that would help me. Any help is appreciated and welcomed!
The code is below:
forms.py
class SalesTaskPlaceHolder(forms.ModelForm):
class Meta:
model = SalesTask
fields = ['title', 'description', 'priority', 'status', 'rnd_project', 'salesExtra',
'rndExtra', 'start_date', 'due_date']
exclude = ['task_id']
widgets = {
'salesExtra': autocomplete.ModelSelect2Multiple(url='sales-extra-autocomplete'),
'rndExtra': autocomplete.ModelSelect2Multiple(url='rnd-extra-autocomplete'),
'start_date': DatePickerInput(),
'due_date': DatePickerInput(),
}
def __init__(self, *args, **kwargs):
self.id = kwargs.pop('id',None)
super(SalesTaskPlaceHolder, self).__init__(*args, **kwargs)
self.fields['title'].widget.attrs['placeholder'] = 'Title of your task'
self.fields['description'].widget.attrs['placeholder'] = 'What is this task about?'
self.fields['status'].widget.attrs['placeholder'] = '0-100'
self.fields['salesExtra'].label = 'Sales Personnel'
self.fields['rndExtra'].label = 'RnD Personnel'
self.fields['rnd_project'].queryset =
RndProject.objects.filter(customerRequirement__sales_project=self.id)
views.py
class SalesExtraAutoComplete(autocomplete.Select2QuerySetView):
def get_queryset(self):
qs = SalesExtra.objects.all()
if self.q:
qs = qs.filter(user__username__istartswith=self.q)
return qs
class TaskCreateView(LoginRequiredMixin, CreateView):
model = SalesTask
form_class = SalesTaskPlaceHolder
template_name = 'rnd/task_create.html'
def get_form_kwargs(self):
kwargs = super(TaskCreateView, self).get_form_kwargs()
kwargs['id'] = self.kwargs['id']
return kwargs
def form_valid(self, form):
project = get_object_or_404(SalesProject, sales_project_id=self.kwargs['id'])
form.instance.sales_project = project
return super(TaskCreateView, self).form_valid(form)
def get_success_url(self):
return reverse('rnd-projects', kwargs={'id': self.object.sales_project.sales_project_id})
task_create.html
{% extends 'rnd/base.html' %}
{% load static %}
{% load crispy_forms_tags %}
{% block content %}
<link rel="stylesheet" type="text/css" href="{% static 'rnd/test.css' %}">
<form method="POST" id='submitform'> {% csrf_token %}
{{ form|crispy }}
<input type="submit" value="Save" id='submitbtn' class='btn w3-teal' />
</form>
<script type="text/javascript">
$('#submitbtn').click(function() {
if ($('#id_title').val().length != 0 && $('#id_description').val().length != 0 &&
$('#id_priority').val().length != 0 && $('#id_status').val().length != 0 &&
$('#id_start_date').val().length != 0 && $('#id_due_date').val().length != 0) {
$('#submitbtn').hide();
$('#submitform').append('<p>Submitting your form now...</p>')
}
});
</script>
{{ form.media }}
{% endblock content %}

I had the same issue. It looks like both Autocomplete and DatePicker are trying to load JQuery, which resulted in initializing JQuery multiple times. Since I was already loading in JQuery independently, I just deleted the JQuery.init.js file from static/autocomplete_light/. Everything seems to be working now.

Related

Django and jQuery: jQuery not working, but receiving no errors

I cannot figure out why my jQuery script isn't working. I've read through a bunch of stackOverflows but haven't been able to fix this issue.
I have a form, and when a checkbox is checked, I want to enable a form field with the id div_id_newLockName. If the checkbox is unchecked, I want the form field to be disabled and greyed out. The checkbox has id div_id_newNameBool I have written code that I think should work, but it doesn't. I'm not receiving any errors though, which makes troubleshooting difficult. I'm hoping a second set of eyes can help. A picture of what the form looks like can be found here: In this picture, the "change name?" checkbox corresponds with div_id_newNameBool, and the "Lock Name" char field is what I want to toggle enable or disable.
base.html (contains jQuery script)
<!doctype html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<style>
<!-- unimportant css stuff -->
</style>
<body class="w3-content" style="max-width:1200px">
<!-- !PAGE CONTENT! -->
{% block content %}{% endblock %}
<!-- End page content -->
</div>
<script>
$(document).ready(function(){
if ($("#div_id_newNameBool").is(':checked')){
$("#div_id_newLockName").prop("disabled", false);
}
else {
$("#div_id_newLockName").prop("disabled", true);
alert("DISABLED");
}
});
</script>
</body>
</html>
HTML page for form
{% extends "homepg/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<!-- Top header -->
<header>
</header>
<form method="POST">
<div class="w3-display-container w3-container">
{% csrf_token %}
<fieldset class="form-group" style="font-size:20px">
<legend class="border-bottom mb-4" style="font-size:20px, p">Edit Lock Information </legend>
{{ form|crispy }}
</fieldset>
<button type="submit" style="width: 200px; padding: 10px; left: 50%; margin:10px;">Change Lock</button>
</div>
</form>
{% endblock content %}
models.py
class Lock(models.Model):
owner = models.ForeignKey(User, on_delete=models.CASCADE)
lockName = models.CharField(max_length=20)
roomLoc = models.ForeignKey(Room, on_delete=models.CASCADE, null=True, blank=True)
state = models.BooleanField(default=0)
code1 = models.PositiveIntegerField(default=0, validators=[MinValueValidator(1000), MaxValueValidator(9999)])
code2 = models.PositiveIntegerField(default=0, validators=[MinValueValidator(1000), MaxValueValidator(9999)])
code3 = models.PositiveIntegerField(default=0, validators=[MinValueValidator(1000), MaxValueValidator(9999)])
code4 = models.PositiveIntegerField(default=0, validators=[MinValueValidator(1000), MaxValueValidator(9999)])
# create a dunder method to control how we want this to printed out
def __str__(self):
return self.lockName
forms.py
class changeLockForm(forms.Form):
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super(changeLockForm, self).__init__(*args, **kwargs)
self.fields['roomLoc'] = forms.ModelChoiceField(queryset=Models.Room.objects.filter(owner=None) | Models.Room.objects.filter(owner=user), label='Room Location')
self.fields['lockName'] = forms.ModelChoiceField(queryset=Models.Lock.objects.filter(owner=None) | Models.Lock.objects.filter(owner=user), label="Current Lock Name")
newNameBool = forms.BooleanField(label='Change Name? Y/N')
newLockName = forms.CharField(min_length=2, max_length=20, label='Lock Name', disabled=True)
#roomLoc = forms.ModelChoiceField(queryset=Models.Room.objects.filter(user=request.user), label='Room Location')
state = forms.BooleanField(required=False, label='LOCKED/UNLOCKED')
code1 = forms.IntegerField(initial=1000, min_value=1000, max_value=9999, label='First Code')
code2 = forms.IntegerField(initial=1000, min_value=1000, max_value=9999, label='Second Code')
code3 = forms.IntegerField(initial=1000, min_value=1000, max_value=9999, label='Third Code')
code4 = forms.IntegerField(initial=1000, min_value=1000, max_value=9999, label='Fourth Code')
class Meta:
model = Models.Lock
fields = ('lockName','newNameBool','newLockName','roomLoc', 'state', 'code1', 'code2', 'code3', 'code4')
field_order = ['lockName', 'newNameBool', 'newLockName', 'roomLoc', 'state', 'code1', 'code2', 'code3', 'code4']
views.py
def changeLock(request):
if request.method == "POST":
# if get post request will create form that has request.POST data!
form = changeLockForm(request.POST, user=request.user)
if form.is_valid():
locksObj = Model.Lock()
# will tell us if form valid when submitted
# form.cleaned_data is a DICTIONARY
tempBool = form.cleaned_data['newNameBool']
if tempBool:
locksObj.lockName = form.cleaned_data['newLockName']
elif not tempBool:
locksObj.lockName = form.cleaned_data['lockName']
else:
print("WHOOPSIE! clearly I coded something wrong wtf")
locksObj.state = form.cleaned_data['state']
locksObj.roomLoc = form.cleaned_data['roomLoc']
locksObj.code1 = form.cleaned_data['code1']
locksObj.code2 = form.cleaned_data['code2']
locksObj.code3 = form.cleaned_data['code3']
locksObj.code4 = form.cleaned_data['code4']
locksObj.owner = request.user
# get the old alarm name
allCodes = Model.Lock.objects.filter(owner_id=request.user)
# old Alarm Name
currentName = allCodes.values_list('lockName', flat=True)[0]
# if the old alarm name and new alarm name aren't equal...
# delete the old alarm entry
entry = Model.Lock.objects.get(lockName=str(currentName))
entry.delete()
# replace it with the new data
locksObj.save()
messages.success(request, 'Lock ' + str(locksObj.lockName) + ' edited!')
return redirect("status-page")
else:
# if not POST request, will create a blank form!
form = changeLockForm(user=request.user)
return render(request, 'users/changeLockForm.html', {'form': form})

Django Dynamic Inline Formset without any fields in the parent model not working

i am new to Django and i have been trying to create a dynamic inline form but ran into an issue. I am trying to create an inline form with user-customisable number of inline forms (models: DataSheetProperties) with foreign key to models: ProvisionalData. However, in the parent model (ProvisionalData), I do not want the user to fill in any fields, but for me to do the filling of the fields in the backend. Meaning, the user should only see the child model (DataSheetProperties) and be able to add or remove instances of that inline form as they see fit. The rendering of the form is okay, but when i press the 'save' button, I can't seem to save it and it does not even run through the form_valid() method in the views.py. I figured it was because there was no fields in the form of the parent model for the user to fill in, hence django does not recognise that the form was being submitted. Hence, I tried to add in a field into the forms.py (ProvisionalDataCreateForm) under the Div() section of the __init_ method. After adding the field, I was able to save the inline form successfully. However, I would hope that the user would not be able to see and fill in any of the fields in the parent model and form as I would want that form to be automatically filled up in the backend. Hope that you guys can help to give some suggestions, all answers are welcome. Thanks in advance!
I downloaded the django-dynamic-formset plugin from this link to use in my dynamic inline formset.
https://github.com/elo80ka/django-dynamic-formset
Also, the code for my dynamic inline formset can from this website.
https://dev.to/zxenia/django-inline-formsets-with-class-based-views-and-crispy-forms-14o6
forms.py
class ProvisionalDataForm(forms.ModelForm):
class Meta:
model = DataSheetProperties
fields = ['properties', 'name','method','value','units']
exclude = []
ProvisionalDataFormSet = inlineformset_factory(ProvisionalData, DataSheetProperties,
form=ProvisionalDataForm, extra=1,
can_delete=True)
class ProvisionalDataCreateForm(forms.ModelForm):
class Meta:
model = ProvisionalData
exclude = []
def __init__(self, *args, **kwargs):
super(ProvisionalDataCreateForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = True
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-md-3 create-label'
self.helper.field_class = 'col-md-9'
self.helper.layout = Layout(
Div(
Fieldset('Add properties',
Formset('data')),
HTML("<br>"),
ButtonHolder(Submit('submit', 'save')),
)
)
models.py
class ProvisionalData(models.Model):
status = (
('active', 'Active'),
('inactive', 'Inactive'),
)
provisionaldata_id = models.AutoField(primary_key=True)
rndProject = models.ForeignKey('RndProject', on_delete=models.CASCADE, null=True, blank=True)
status = models.CharField(max_length = 20, choices=status, default='active')
justification = models.TextField(max_length=200, null=True, blank=True)
class DataSheetProperties(models.Model):
options = (
('physical','Physical'),
('mechanical','Mechanical'),
('thermal','Thermal'),
)
status = (
('active', 'Active'),
('inactive', 'Inactive'),
)
name = models.CharField(max_length=50, default='Property Name')
method = models.CharField(max_length=50, default='Test Method')
units = models.CharField(max_length=50, default='Unit of Measurement')
value = models.CharField(max_length=50, default='Typical Value')
properties = models.CharField(max_length=50, choices=options)
data = models.ForeignKey('ProvisionalData', on_delete=models.CASCADE)
status = models.CharField(max_length = 20, choices=status, default='active')
views.py
class ProvisionalDataSheetView(CreateView):
model = ProvisionalData
form_class = ProvisionalDataCreateForm
template_name = 'rnd/provisional_datasheet.html'
def get_context_data(self, **kwargs):
context = super(ProvisionalDataSheetView, self).get_context_data(**kwargs)
if self.request.POST:
context['data'] = ProvisionalDataFormSet(self.request.POST)
else:
context['data'] = ProvisionalDataFormSet()
return context
def form_valid(self, form):
form.instance.rndProject = RndProject.objects.filter(rnd_project_id = self.kwargs['pk'])[0]
context = self.get_context_data()
data = context['data']
with transaction.atomic():
self.object = form.save()
if data.is_valid():
data.instance = self.object
data.save()
return super(ProvisionalDataSheetView, self).form_valid(form)
def get_success_url(self):
return reverse('rnd-project-details', kwargs={'pk': self.object.rndProject.rnd_project_id})
formset.html
{% load static %}
{% load crispy_forms_tags %}
<table>
{{ formset.management_form|crispy }}
{% for form in formset.forms %}
<tr class="{% cycle 'row1' 'row2' %} formset_row-{{ formset.prefix }}">
{% 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|as_crispy_field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<br>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js">
</script>
<script src="{% static 'rnd/jquery.formset.js' %}" type="text/javascript"> </script>
<script type="text/javascript">
$('.formset_row-{{ formset.prefix }}').formset({
addText: 'add another',
deleteText: 'remove',
prefix: '{{ formset.prefix }}',
});
</script>
provisional_datasheet.html
{% extends 'rnd/base.html' %}
{% load static %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container">
<div class="card">
<div class="card-header">
Create provisional datasheet
</div>
<div class="card-body">
{% crispy form %}
</div>
</div>
</div>
{% endblock content %}

Tempus Dominus Datepicker widget not cloning properly in Django Model Formset when using Dynamic Formset Library

I have a model formset set up using Django Dynamic Formset along with Crispy Forms and the Tempus Dominus datepicker library. It's a simple formset for users to add operating expense line entries -- date, category, description, and amount.
My datepicker works correctly with a non-dynamic formset. However, when using dynamic formsets and a new form is added, the widget ends up targeting the last form in the formset that it was cloned from and not the current form (https://imgur.com/a/2GFYZ90). Instead of incrementing properly (i.e. form_3, form_4, form_5, etc.), it just refers to the last form of the formset (i.e. form_2) before the new ones are added. As a result, if I try to toggle the datepickers in any of the dynamically added forms, it just triggers the one in form_2. Regular text fields clone correctly without any problems.
As you can see in lines 19-23 of opex.html, I'm attempting to implement the destroy and re-initialize functions as suggested by some older Stack Overflow posts (1, 2, and 3) but I'm a JS noob so I'm not sure how to proceed.
models.py
class OpEx(models.Model):
GEN_EXP = 'GE'
OFFICE_SUP = 'OS'
NR_EXP = 'NR'
CATEGORY_CHOICES = [
(GEN_EXP, 'General Expenses'),
(OFFICE_SUP, 'Office Supplies'),
(NR_EXP, 'Non-Recurring Expenses'),
]
exp_user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, default=0)
exp_date = models.DateField(verbose_name='Date')
category = models.CharField(max_length=2, choices=CATEGORY_CHOICES, default=GEN_EXP)
description = models.CharField(max_length=255)
amount = models.DecimalField(verbose_name='Amount ($)', max_digits=10, decimal_places=2)
forms.py
class OpExEntryForm(ModelForm):
class Meta:
model = OpEx
exclude = ('id', 'exp_user')
class OpExFormSetHelper(FormHelper):
def __init__(self, *args, **kwargs):
super(OpExFormSetHelper, self).__init__(*args, **kwargs)
self.form_method = 'POST'
self.form_class = 'form-inline'
self.form_show_labels = False
self.layout = Layout(
Row(
Column('exp_date', css_class='col-sm-2'),
Column('category', css_class='col-sm-3'),
Column('description', css_class='col-sm-4'),
Column(PrependedText('amount', "<span class='fa fa-dollar-sign'"), css_class='col-sm-2'),
css_class='opex-formset-row'
)
)
self.render_required_fields = True
self.add_input(Submit('submit', 'Update operating expenses'))
views.py
def opex(request):
OpExFormSet = modelformset_factory(OpEx, exclude=('id', 'exp_user'),
widgets={'exp_date': DatePicker(attrs={'append': 'fa fa-calendar'})},
form=OpExEntryForm, extra=0, can_delete=True)
helper = OpExFormSetHelper()
queryset = OpEx.objects.filter(exp_user=user)
if request.method == 'POST':
formset = OpExFormSet(request.POST, queryset=queryset)
# Irrelevant code...
else:
formset = OpExFormSet(queryset=queryset)
return render(request, 'opex.html', {'formset': formset, 'helper': helper})
opex.html
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% load static %}
{% block extra_head %}
{{ form.media }}
{% endblock %}
{% block content %}
{% crispy formset helper %}
<!-- Load Django Dynamic Formset -->
<script type="text/javascript" src="{% static 'js/jquery.formset.js' %}"></script>
<script type="text/javascript">
$(function() {
$('.opex-formset-row').formset({
addText : 'add another',
deleteText : 'remove',
added: function (row) {
var datePicker = $(row).find('input[name$="exp_date"]');
if (datePicker.length > 0) {
datePicker.datetimepicker('destroy');
}
}
});
})
</script>
{% endblock %}

How to implement Bootstrap DatePicker (Year/Month) to Django Admin Filters?

How do I include an inline bootstrap calendar selector into the Django admin page?
I was creating a custom form and template from the following: Django admin add custom filter
It works with a text field. I would like to add the calendar to this.
How do I include the JS and CSS files for this specific form?
How do I pass the value selected to my form?
YearMonthForm
class YearMonthForm(forms.Form):
def __init__(self, *args, **kwargs):
self.field_name = kwargs.pop('field_name')
super(YearMonthForm, self).__init__(*args, **kwargs)
self.fields['%s__gte' % self.field_name] = forms.DateField()
YearMonthFilter
class YearMonthFilter(admin.filters.FieldListFilter):
template = 'maindashboard/filter.html'
def __init__(self, *args, **kwargs):
field_path = kwargs['field_path']
self.lookup_kwarg_since = '%s__gte' % field_path
super(YearMonthFilter, self).__init__(*args, **kwargs)
self.form = YearMonthForm(data=self.used_parameters, field_name=field_path)
def expected_parameters(self):
return [self.lookup_kwarg_since]
# no predefined choices
def choices(self, cl):
return []
def queryset(self, request, queryset):
if self.form.is_valid():
filter_params = {
p: self.form.cleaned_data.get(p) for p in self.expected_parameters()
if self.form.cleaned_data.get(p) is not None
}
startBillingDate = filter_params["billingDate__gte"]
year = startBillingDate.year
month = startBillingDate.month
day = calendar.monthrange(startBillingDate.year, startBillingDate.month)[1]
endDate = date(year, month, day)
filter_params["billingDate__lte"] = endDate
return queryset.filter(**filter_params)
else:
return queryset
filter.html
{% load i18n admin_static %}
<h3>{% blocktrans with filter_title=title %} By {{ filter_title }} {% endblocktrans %}</h3>
{% with choices.0 as i %}
<label>Filter by Year-Month:</label>
<div id='InlineMenu'></div>
<form method="GET" action="">
{{ spec.form.media }}
{{ spec.form.as_p }}
<p class="submit-row">
{# create hidden inputs to preserve values from other filters and search field#}
{% for k, v in i.get_query.items %}
<input type="hidden" name="{{ k }}" value="{{ v }}">
{% endfor %}
<input type="submit" value="{% trans "Search" %}">
<input type="reset" value="{% trans "Clear" %}">
</p>
</form>
{% endwith %}
I'm not sure what the {{ spec.form.media }} tags do. But I've seen in other examples where they add a media function to the form.
Folder Structure
Folder Structure:
-projectportal
- maindashboard
- templates
- filter.html
- static
- maindashboard
- css
- MonthPicker.min.css
- js
- monthPicker.js
- monthPicker.min.js
How do I make references to the css and js files for this specific form?
Update
Figured out how to reference the JS and CSS files for this form/template.
However, the entire Administration page is styled differently now.
The calendar is now showing up under the admin filters.
Now I need to figure out how to catch the date change events and submit the form.
#property
def media(self):
return forms.Media(
css= {'all': ('maindashboard/css/bootstrap.min.css', 'maindashboard/css/bootstrap-theme.min.css', 'maindashboard/css/bootstrap-datepicker.min.css',)},
js = ('maindashboard/js/jquery-3.3.1.slim.min.js', 'maindashboard/js/bootstrap.min.js', 'maindashboard/js/bootstrap-datepicker.js', 'maindashboard/js/bootstrap-datepicker.min.js', 'maindashboard/js/monthPicker.js'))
The css and js files are referenced from the media method in the YearMonthForm class.
Year Month selected is passed after the form submitted in the monthPicker.js file.
$("#InlineMenu").datepicker().on("changeDate", function(e){
// Increase month by 1
$("#id_billingDate__gte").val(e.date.getFullYear() + "-" + (e.date.getMonth()+1) + "-1");
$("#billing_calendar").submit();
});

Django formsets no data on post

I have a form that uses multiple formsets. The formset forms are added dynamically via JS. I have been looking at a few different places to help myself along.
Add a dynamic form to a django formset using javascript in a right way
And
A nice post by Kevin Dias - Django class-based views with multiple inline formsets
The problem I am having is that when I post my data the outer form has data, but non of my formsets actually have any data in the cleaned_data dictionary when I start looping through them. Any thoughts on what I may be missing? The second formset is added with a very similar JS method.
Forms
class ShippingForm(Form):
is_partial = BooleanField(label='Partial?')
class ShippingActualProduct(Form):
box_no = CharField(label='Box Number', max_length=3)
upc = CharField(
widget=forms.TextInput(attrs={'class':'upcAjax'}),
)
serial_no = CharField(
label='Serial Number',
widget=forms.TextInput(attrs={'class':'serial'}),
)
sku = CharField(
widget=forms.TextInput(attrs={'class':'skuAjax'}),
)
description=CharField(label='Description')
on_hand=CharField(label='On Hand')
def __init__(self, *args, **kwargs):
super(ShippingActualProduct,self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_show_labels = True
self.helper.form_class = 'form-inline'
class ShippingNonInventoryProduct(Form):
non_box_no = CharField(label='Box Number', max_length=3)
quantity = IntegerField()
description = CharField()
serial_no = CharField()
def __init__(self, *args, **kwargs):
super(ShippingNonInventoryProduct,self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_show_labels = True
self.helper.form_class = 'form-inline'
ActualProductFormSet = formset_factory(ShippingActualProduct, extra=1, can_delete=True)
NonInventoryProductFormSet = formset_factory(ShippingNonInventoryProduct, extra=1, can_delete=True)
Views
class ShippingCreate(FormView):
template_name = 'jinja2/Shipping/shipping_create.html'
form_class = ShippingForm
success_url = reverse_lazy('shipping_create')
def get_context_data(self, **kwargs):
context = super(ShippingCreate, self).get_context_data(**kwargs)
input_invoice_no = self.request.GET['invoice_no']
try:
self.object = Invoice.objects.get(invoice_no = input_invoice_no)
context['invoice'] = self.object
except Invoice.DoesNotExist:
messages.error(self.request, 'We were unable to find invoice number %s, please try again' % input_invoice_no)
try:
context['voucher'] = Voucher.objects.get(voucher_no = self.object.voucher_no)
except Voucher.DoesNotExist:
messages.error(self.request, 'We were unable to find an installation voucher for claim number %s' % self.object.voucher_no)
context['actual_items_forms'] = ActualProductFormSet(prefix='actual')
context['non_inventory_items_forms'] = NonInventoryProductFormSet(prefix='non')
context['form'] = ShippingForm()
return context
def get(self, request, *args, **kwargs):
self.object = None
context = self.get_context_data()
return render(request, self.template_name, context)
def post(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
actual_product = ActualProductFormSet(self.request.POST, prefix='actual')
non_inv_product = NonInventoryProductFormSet(self.request.POST, prefix='non')
if actual_product.is_valid():
for product in actual_product:
data = product.cleaned_data
sku = data.get('sku')
context={}
return render(request, self.template_name, context)
Template
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% load static from staticfiles %}
{% load socialaccount %}
{% load sitetree %}
{% block headcss %}
{{ block.super }}
<link rel="stylesheet" href="{% static "css/shipping.css" %}">
{% endblock headcss %}
{% block headjs %}
{{ block.super }}
<script src="{% static "js/shipping.js" %}"></script>
{% endblock headjs %}
{% block content %}
{% block messages %}
{{ block.super }}
{% endblock messages %}
<div class="container-fluid">
<form action="." method="post">
<div>
<h3>Item information:</h3>
{% csrf_token %}
{{ actual_items_forms.management_form }}
<div id="actual-items-form-container">
</div>
Add Item
</div>
<div>
<h3>Non Inventory Items Shipped:</h3>
{{ non_inventory_items_forms.management_form }}
<div id="non-inv-items-form-container">
</div>
Add Item
</div>
{{ form.as_p }}
<input type="submit" value="Complete" class="submit btn btn-success" />
</form>
</div>
{% include "jinja2/hub/loading_modal.html" %}
{% endblock content %}
</html>
The JavaScript
function getNewActualItemForm() {
// unbind this ajax call from the overlay displayed when looking data up.
$(document).unbind(".items");
var count = $('#actual-items-form-container').children().length;
$.get("/forms/actualitemform",function(data, status){
var form = data.replace(/__prefix__/g, count);
// Get the html contents of the form, all together to iterate over.
var htmlForm = $('<form>').html(form).contents();
// Just grab the children of that form
var rows = htmlForm.children();
// loop through the inputs locating the DELETE input and label.
$(rows).each( function(index, value) {
var row = $(this);
var del = $(row).find('input:checkbox[id $= "-DELETE"]');
// Only move forward if we have found the DELETE input
if (del.length){
//Write the form ot the Dom so the search for the label will succeed.
$('div#actual-items-form-container').append(form);
var label ='label[for="id_form-' + count + '-DELETE"]';
$(label).hide();
}
});
// update form count
$('#id_actual-TOTAL_FORMS').attr('value', count+1);
// some animate to scroll to view our new form
$('html, body').animate({
scrollTop: $('#actual-item-btn').position().top-200
}, 800);
// get the max box number.
var maxBoxNo = getBoxNo();
// Set focus to the next UPC
var boxID = '#id_form-var-box_no';
var upcID = '#id_form-var-upc';
var nextBox = boxID.replace('var',count);
var nextUpc = upcID.replace('var',count);
// set the box number for the new line.
$(nextBox).val(maxBoxNo);
$(nextUpc).focus();
});
return count;
}
There were two issues that were causing the troubles here.
I had the crispy forms helper rendering the form tags for each formset. This is a bad idea. Setting form_tag = False fixed that.
I had forgotten to set the prefix argument on my formsets in the views I created to grab the next form via JavaScript.
Once both of these were implemented the data was now available from the forms on submission.

Categories