I need some help building a dynamic formset in django. I understand that some javascript is required. I am no javascript expert. But I found some example code and I'm trying to fit it to work on my particular form. When I click the Add button nothing happens. And I'm at a loss on how to move forward. Any suggestions?
Model is for attendee event registration. A customer can register multiple people to attend an event.
Trying to get an add attendee button.
# models.py
class Customer(models.Model):
event = models.ForeignKey(Event)
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
address1 = models.CharField(max_length=60)
address2 = models.CharField(max_length=60, blank=True)
city = models.CharField(max_length=30)
state = models.CharField(max_length=2)
zipcode = models.CharField(max_length=5)
class Attendee(models.Model):
event = models.ForeignKey(Event)
sponsor = models.ForeignKey(Customer)
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
# forms.py
CustomerFormset = inlineformset_factory(Customer, Attendee, form=AttendeeForm, exclude=('event'), extra=2)
# templates/register.html
<form action="/enroll/register3/{{ event.id }}/" method="post">{% csrf_token %}
<p> Sponsor information: </p>
<table>
{{ customer }}
</table>
<hr>
<p> Students Attending: </p>
{% for attendee in attendees.forms %}
<div class="formgroup">
{{ attendee }}
{% if attendee.nested %}
{% for formset in attendee.nested %}
{{ formset.as_table }}
{% endfor %}
{% endif %}
</div>
{% endfor %}
<div>
Nothing happens when 'add_more' is clicked. ??
<input type="button" value="Add Attendee" id="add_more">
<input type="submit" value="Submit">
</div>
</form>
<script>
$('#add_more').click(function() {
//cloneMore('div.table:last', 'service');
cloneMore('div.table:last', 'formgroup');
});
function cloneMore(selector, type) {
console.log('cloneMore');
var newElement = $(selector).clone(true);
var total = $('#id_' + type + '-TOTAL_FORMS').val();
newElement.find(':input').each(function() {
var name = $(this).attr('name').replace('-' + (total-1) + '-','-' + total + '-');
var id = 'id_' + name;
$(this).attr({'name': name, 'id': id}).val('').removeAttr('checked');
});
newElement.find('label').each(function() {
var newFor = $(this).attr('for').replace('-' + (total-1) + '-','-' + total + '-');
$(this).attr('for', newFor);
});
total++;
$('#id_' + type + '-TOTAL_FORMS').val(total);
$(selector).after(newElement);
}
</script>
It looks like you took javascript from this question, but that you did not adapt your HTML structure.
As I can see in your snippet (which also looks like it will produce highly invalid html, ie. tr in divs):
{% for attendee in attendees.forms %}
<div class="formgroup">
{{ attendee }}
{% if attendee.nested %}
{% for formset in attendee.nested %}
{{ formset.as_table }}
{% endfor %}
{% endif %}
</div>
{% endfor %}
And his:
{{ serviceFormset.management_form }}
{% for form in serviceFormset.forms %}
<div class='table'>
<table class='no_error'>
{{ form.as_table }}
</table>
</div>
{% endfor %}
If you want the same exact javascript piece of code, then you should have the same exact HTML structure. The first thing you're doing in JS is passing div.table:last as selector but you have no div.table in your HTML.
Your best bet is to try to make the javascript yourself. As a webpage developer, javascript will help you a lot.
Related
I am trying to hide certain fields in a form based on select field choices. For example for Select field "system_description_1" If option "Direct" is selected following fields should be hidden (also hidden by default on page load), but when "Indirect" is selected the fields should become visible. This function is absolutely working fine using Jquery, but it is still showing the form label, It is hiding the field but not the label of the field how do I hide them ? I want to hide the field label when I hide the field, but show the field label when I unhide or show them.
Fields:
secondary_circulation_pump_model
secondary_circulation_pump_capacity
models.py:
class Systemprofile(models.Model):
SYSTEMDESC1 = (
('direct', 'Direct'),
('indirect', 'Indirect')
)
SECONDARYPUMPMODEL = (
('grundfos', 'Grundfos'),
('cnp', 'CNP'),
('others', 'Others')
)
SECONDARYPUMPCAPACITY = (
('ltr-hr', 'Ltr/hr'),
('others', 'Others')
)
name = models.CharField(max_length=255, null=True)
system_description_1 = models.CharField(blank=True, max_length=255, null=True, choices=SYSTEMDESC1)
secondary_circulation_pump_model = models.CharField(blank=True, max_length=255, null=True, choices=SECONDARYPUMPMODEL)
secondary_circulation_pump_capacity = models.CharField(blank=True, max_length=255, null=True, choices=SECONDARYPUMPCAPACITY)
forms.py
class CreateSystemprofile(ModelForm):
class Meta:
model = Systemprofile
fields = '__all__'
system_profile.html
{% extends 'base.html' %}
{% load static %}
{% block content %}
{% load crispy_forms_tags %}
<script>
function Hide() {
if(document.getElementById('id_system_description_1').options[document.getElementById('id_system_description_1').selectedIndex].value == "Direct") {
document.getElementById('id_secondary_circulation_pump_model').style.display = 'none';
document.getElementById('id_secondary_circulation_pump_capacity').style.display = 'none';
} else {
document.getElementById('id_secondary_circulation_pump_model').style.display = '';
document.getElementById('id_secondary_circulation_pump_capacity').style.display = '';
}
}
window.onload = function() {
document.getElementById('id_secondary_circulation_pump_model').style.display = 'none';
document.getElementById('id_secondary_circulation_pump_capacity').style.display = 'none';
document.getElementById('id_system_description_1').onchange = Hide;
};
</script>
<br>
<div class="row">
<div class="col-md-6">
<div class="card card-body">
<p><b>Add/Update System Profile:</b></p>
<form action="" method="POST">
<table border="1">
{% csrf_token %}
<table>
<div class="form-group">
System Description 1: <select id="id_system_description_1" onchange="Hide()">
<option value="">select</option>
<option value="Direct">Direct</option>
<option value="Indirect">Indirect</option>
</select></div></table>
<table>
{{ form.name|as_crispy_field }}
{{ form.secondary_circulation_pump_model|as_crispy_field }}
{{ form.secondary_circulation_pump_capacity|as_crispy_field }}
</table>
<hr>
<input class="btn btn-sm btn-info" type="submit" name="submit">
</form>
</div>
</div>
</div>
{% endblock %}
Now see the output, If I show the field it is absolutely fine, that is when I select "Indirect" option, but when I select "Direct" option it displays the field label, please see the image below:
WHEN I HIDE:
WHEN I SHOW:
PLEASE HELP ME WITH JQUERY OR CSS OR ANY OTHER WAY TO RESOLVE THIS.
This is the jQuery method for selecting with label:
$('label[for="id_secondary_circulation_pump_model"]').css('display', 'none');
$('label[for="id_secondary_circulation_pump_capacity"]').css('display', 'none');
I'm having trouble getting my form to save in Dajngo due to the following error on validation:
<ul class="errorlist"><li>pt_medical_condition<ul class="errorlist"><li>Select a valid choice.
['anx', 'Bip'] is not one of the available choices.
>pt_surgical_history<ul class="errorlist"><li>Select a valid choice.
['bre', 'pca'] is not one of the available choices.
I've got this model:
class pt_data(models.Model):
condition_choices = [('ane', 'Anemia'), ('anx', 'Anxiety'), ('art', 'Arthritis'),
('ast', 'Asthma'), ('Bip', 'Bipolar'), ('ca', 'Cancer'), ('clo', 'Clotting disorder'),
('chf', 'CHF'), ('mdd', 'Depression'), ('cop', 'COPD'), ('ger', 'GERD'),
('gla', 'Glaucome'), ('hiv', 'HIV/AIDS'), ('ibs', 'IBS/Crohn\'s'),
('hld', 'High cholesterol'), ('ckd', 'Kidney disease'), ('ner', 'Nerve/Muscle disease'),
('ocd', 'OCD'), ('ost', 'Osteoporosis'), ('pai', 'Pain disorder'), ('pts', 'PTSD'),
('sch', 'Schizophrenia'), ('sei', 'Seizures'), ('sca', 'Sickle cell anemia'),
('su', 'Substance use disorder'), ('thy', 'Thyroid disease')]
surgery_choices = [('app', 'Appendix removal'), ('bra', 'Brain surgery'),
('bre', 'Breast surgery'), ('cabg', 'CABG'), ('pca', 'Cardiac stent'),
('cho', 'Gallbladder removal'), ('col', 'Bowel surgery'), ('csec', 'C-section'),
('fra', 'Bone fracture repair'), ('her', 'Hernia repair'), ('hys', 'Uterus removal'),
('joi', 'Joint replacement'), ('lun', 'Lung surgery'), ('spi', 'Spine/back surgery'),
('thy', 'Thyroid surgery'), ('ton', 'Tonsil removal'), ('strf', 'Tubal ligation'),
('strm', 'Vasectomy'), ('wei', 'Weight reduction surgery')]
pt_medical_condition = models.CharField(max_length=100, blank=True, null=True,
choices=condition_choices)
pt_surgical_history = models.CharField(max_length=100, blank=True, null=True, choices=surgery_choices)
And this form:
class ptForm(forms.ModelForm):
class Meta:
model = pt_data
fields = ('__all__')
widgets = {
'pt_medical_condition': CheckboxSelectMultiple(attrs={'class': 'cond_checkbox'}),
'pt_surgical_history': CheckboxSelectMultiple(attrs={'class': 'surg_checkbox'}),
}
And this is my HTML:
{% for check in form.pt_medical_condition %}
{% if forloop.counter|divisibleby:3 %}
<div> </div>
{% endif %}
<label class='mx-2' id="{{ check.choice_label }}">
{{ check.tag }} {{ check.choice_label }}
</label>
{% endfor %}
{% for check in form.pt_surgical_history %}
{% if forloop.counter|divisibleby:2 %}
<div> </div>
{% endif %}
<label class='mx-2' id="{{ check.choice_label }}">
{{ check.tag }} {{ check.choice_label }}
</label>
{% endfor %}
The HTML is rendered just fine, the problem seems to arise on submit. It looks like the correct characters are there, however they are surrounded by incorrect characters. The following "anx" is appropriate but won't be validatated by Django because everything else that has been tacked on 'anx'. Could it be a problem with posting the information with AJAX? Or maybe using a CSRF cookie to POST? Has anyone else encountered this problem? Any suggestions would be helpful.
I'm trying to build a form with dynamic fields (click a plus to add extra fields).
forms.py:
class ActionForm(Form):
key = SelectField("Type: ", coerce=int, choices=[(0, "Option 1"), (1, "Option 2"), (2, "Opeion 3")], default=0)
value = StringField('Value: ')
class EditForm(Form):
content = StringField("Content: ")
actions = FieldList(FormField(ActionForm))
status = RadioField("Status: ", coerce=int, choices=[(0, "Inactive"), (1, "Active")], default=1)
submit = SubmitField("Submit")
View Template (Won't Render the Fields from ActionForm):
<form method="POST">
{{ form.csrf_token }}
{{ form.actions.label }}
<div class="form-group input-group">
{% for action in form.actions %}
{% for field in action %}
{{ field() }}
{% endfor %}
{% endfor %}
</div>
{{ form.status.label }}{{ form.status }}
{{ form.submit() }}
</form>
Problem:
In my form, I just see a blank spot where the ActionForm fields should appear.
In other words, I can't iterate through form.actions (to show the SelectField() and StringField()).
What am I doing wrong?
FieldList takes a min_entries keyword argument - if you set it, it will ensure that there are at least n entries:
class EditForm(Form):
content = StringField("Content: ")
actions = FieldList(FormField(ActionForm), min_entries=1)
OK, so this is my first time using formsets. I am trying to create a table that I can dynamically add rows to and fill out each row, then submit and have Django put them all in the database. Every time I submit it only adds the first form.
File views.py:
#main_context_wrapper
def bacteriaForm2(request,context):
if not request.user.is_authenticated():
#If user isn't authenticated, then just redirect to login
return HttpResponseRedirect('/login/')
BacteriaFormSet = formset_factory(BacteriaForm)
if request.POST:
bacteriaformset = BacteriaFormSet(request.POST, request.FILES)
if bacteriaformset.is_valid():
context["error"] = ""
for form in bacteriaformset:
form.save()
return HttpResponseRedirect('/')
else:
context["error"] = "validation"
context["bacteriaformset"] = BacteriaFormSet
context.update(csrf(request))
return render_to_response('bacteriaForm.html', context)
else:
#The request was a GET, add the form to the context
context["bacteriaformset"] = BacteriaFormSet()
#Add all siteInfo objects to allow for dynamic site info drop down menus
siteInfo = SiteInfo.objects.all()
context["siteInfo"] = siteInfo
return render(request, "bacteriaForm.html", context)
Template:
{% extends "inherited/main.html" %}
{% block content %}
<h1> Bacteria Entry Form </h1>
<form action='/bacteriaForm/' method="post">{% csrf_token %}
{{bacteriaformset.management_form}}
{% if error == "validation" %}
<div class="alert alert-danger">
<p>
<strong>Error: </strong>Form not completed properly.
</p>
</div>
{% endif %}
<table id="id_forms_table">
<tr>
{% for field in bacteriaformset.forms.0 %}
{% if not field.is_hidden %}
<th>{{ field.label }}</th>
{% endif %}
{% endfor %}
</tr>
{% for f in bacteriaformset.management_form %}
{{ f }}
{% endfor %}
{% for f in bacteriaformset.forms %}
<tr id="{{ f.prefix }}-row" class="dynamic-form">
{% for field in f %}
{% if not field.is_hidden %}
<td>
{{ field.errors }}
{{ field }}
</td>
{% else %}
<td valign="bottom">{{ field }}</
{% endif %}
{% endfor %}
<td{% if forloop.first %} class="hidden"{% endif %}>
<a id="remove-{{ form.prefix }}-row" href="javascript:void(0)" class="delete-row">delete</a>
</td>
</tr>
{% endfor %}
<tr>
<td colspan="4">add property</td>
</tr>
</table>
<div>
<input type="submit" value="submit" />
</div>
</form>
<script>
$(function () {
$('.add-row').click(function() {
return addForm(this, 'form');
});
$('.delete-row').click(function() {
return deleteForm(this, 'form');
});
});
function updateElementIndex(el, prefix, ndx) {
var id_regex = new RegExp('(' + prefix + '-\\d+)');
var replacement = prefix + '-' + ndx;
if ($(el).attr("for")) $(el).attr("for", $(el).attr("for").replace(id_regex, replacement));
if (el.id) el.id = el.id.replace(id_regex, replacement);
if (el.name) el.name = el.name.replace(id_regex, replacement);
}
function addForm(btn, prefix) {
var formCount = parseInt($('#id_' + prefix + '-TOTAL_FORMS').val());
var row = $('.dynamic-form:first').clone(true).get(0);
$(row).removeAttr('id').insertAfter($('.dynamic- form:last')).children('.hidden').removeClass('hidden');
$(row).children().not(':last').children().each(function() {
updateElementIndex(this, prefix, formCount);
$(this).val('');
});
$(row).find('.delete-row').click(function() {
deleteForm(this, prefix);
});
$('#id_' + prefix + '-TOTAL_FORMS').val(formCount + 1);
return false;
}
function deleteForm(btn, prefix) {
$(btn).parents('.dynamic-form').remove();
var forms = $('.dynamic-form');
$('#id_' + prefix + '-TOTAL_FORMS').val(forms.length);
for (var i=0, formCount=forms.length; i<formCount; i++) {
$(forms.get(i)).children().not(':last').children().each(function() {
updateElementIndex(this, prefix, i);
});
}
return false;
}
$("#id_date").datepicker();
</script>
{% endblock %}
File Models.py
class BacteriaEntry(models.Model):
"""
A bacteria entry contains information about a test set's
bacteria levels at a specific time. BacteriaEntry utilizes the
Djano model.
"""
siteNumber = models.IntegerField()
date = models.DateField()
sampleNumber = models.IntegerField(primary_key=True)
ecoliRawCount = models.IntegerField(null=True)
ecoli = models.DecimalField(decimal_places=10, max_digits=20, null=True)
ecoliException = models.IntegerField(null=True)
coliformRawCount = models.IntegerField(null=True)
coliform = models.DecimalField(decimal_places=10, max_digits=20, null=True)
coliformException = models.IntegerField(null=True)
comments = models.CharField(max_length=2000, null=True)
"""Returns the sample number of the Bacteria Entry"""
def __unicode__(self):
return smart_unicode(self.sampleNumber)
Here is some post data
<QueryDict: {u'form-0-date': [u'02/24/2014'], u'form-0-comments': [u'65'], u'form- MAX_NUM_FORMS': [u'1000', u'1000'], u'form-0-coliformRawCount': [u'5'],
u'form-0-coliform': [u'65'], u'form-0-ecoliException': [u'56'], u'form-TOTAL_FORMS': [u'1', u'1'], u'form-0-sampleNumber': [u'1554'], u'form-0-ecoliRawC
ount': [u'35'], u'form-0-coliformException': [u'56'], u'form-INITIAL_FORMS': [u'0', u'0'], u'csrfmiddlewaretoken': [u'VSnaJCW6R9z8iEKib46cHuBJ6AKTPPUT'],
u'form-0-ecoli': [u'51'], u'form-0-siteNumber': [u'100']}>
I'm not exactly sure where my problem is. I have spent a lot of time trying to figure this out. I am not sure what is wrong.
You're not printing formset.non_form_errors, or form.non_field_errors in any of the child forms, in your template - there's probably something in one of those that is causing validation to fail.
I should also point out that there is no need to instantiate the form separately from the formset. It makes no sense to create a separate BacteriaForm instance and pass it to the template, then validate it on post: that's what the formset is for, after all.
I'm a bit confused with this, not sure why you need both a Form and a FormSet, especially because the FormSet contains the same Forms as is the separate Form...
But if that is the case, then you should use prefix on the Form and/or on the FromSet:
bacteriaForm = BacteriaForm(request.POST, prefix='the_one_bacteria')
bacteriaformset = BacteriaFormSet(request.POST, request.FILES, prefix='bacterias')
and also in the else: part of the View.
Pardon my plural.
I'm building a ecommerce site with django.
I'm creating the page where the orders are processed.
I want to make it so that when an item is "delivered" that order will go to the bottom of the list.
views.py
def orders(request, hour):
#changes status of order from submitted to shipped
if request.method == 'POST':
SUBMITTED = 1
PROCESSED = 2
SHIPPED = 3
CANCELLED = 4
order_id = request.POST['order_id']
this_order = Order.objects.get(pk=order_id)
if this_order.status == SUBMITTED or this_order.status == PROCESSED:
this_order.status = SHIPPED
elif this_order.status == SHIPPED:
this_order.status = SUBMITTED
this_order.save()
return HttpResponseRedirect('/orders/' + hour)
#Get all orders from the past 24 hours
tz=pytz.timezone('America/Los_Angeles')
now_nonaware = datetime.datetime.now()
now = timezone.make_aware(now_nonaware,tz)
orders = Order.objects.filter(date__range=[now - datetime.timedelta(hours=20), now]).filter(time=hour)
#get all orders from every college drop
revelle_orders = orders.filter(location = "revelle")
muir_orders = orders.filter(location = "muir")
marshall_orders = orders.filter(location = "marshall")
erc_orders = orders.filter(location = "erc")
warren_orders = orders.filter(location = "warren")
sixth_orders = orders.filter(location = "sixth")
orderlocations = {"revelle": revelle_orders, "muir" : muir_orders, "marshall" : marshall_orders,
"erc": erc_orders, "warren": warren_orders, "sixth": sixth_orders}
orders_dict = {"orderlocations" : orderlocations, "hour": hour}
return render_to_response('orders.html', orders_dict, context_instance=RequestContext(request))
HTML:
Order page for {{hour}}
</br>
</br>
{% for location, orders in orderlocations.items %}
{% if orders %}
{{ location|capfirst }}
<table>
<tr>
<td>#</td>
<td>Name</td>
<td>Email</td>
<td>Order</td>
<td>Delivered</td>
<td>Phone</td>
</tr>
{% for ord in orders %}
{% for food in ord.orderitem_set.all %}
<tr>
{% if forloop.counter == 1 %}
<td>{{forloop.parentloop.counter}}</td>
<td>{{ord.full_name}}</td>
<td>{{ord.email}}</td>
{% else %}
<td colspan="3"></td>
{% endif %}
<td>{{food.name}} (x{{food.quantity}})</td>
{% if forloop.counter == 1 %}
<td>
<form action="" method="POST">
{% csrf_token %}
<input type="hidden" name="order_id" value="{{ ord.pk }}"/>
<input type="hidden" name="action=" value="toggledelivery"/>
<button type="submit">{% ifnotequal 3 ord.status %} Not {% endifnotequal %}Delivered</button>
</form>
</td>
<td>{{ord.phone}}</td>
{% endif %}
</tr>
{% endfor %}
{% endfor %}
</table>
</br>
</br>
{% endif %}
{% endfor %}
This sounds like a job for model ordering meta options on a class.
For example in your model class add a sub-class called Meta:
class MyModel(models.Model):
# model fields here...
class Meta:
ordering = ['status']
You can add a - in front of status to sort in descending order. You can also chain together multiple fields for ordering:
class Meta:
ordering = ['status', '-order_date']
hmm since there's this part
<button type="submit">{% ifnotequal 3 ord.status %} Not {% endifnotequal %}Delivered</button>
maybe something like this in your orders view?
orders = Order.objects.filter(date__range=[now - datetime.timedelta(hours=20), now]).filter(time=hour).extra({"is_completed_order": "status == 3"}).order_by('is_completed_order')
e.g. compute an extra is_completed_order boolean field and order on that