Flask + WTForms - Display Fields in FormList - flask

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)

Related

Problem with CheckboxSelectMultiple from a CharField ModelForm in Django

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&#x27. 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.

How to format Django form with UIKit styling

I am struggling to see a way to style a django form in the style of a uikit horizontal form. UIKit has the styling I want and Django has the validation and templating I want.A way to implement a datepicker too would be useful.
I have tried the plain django form template with .as_p and .as_table. I have also tried to use Meta and widgets but couldn't get that to work. I can't see how I can add the needed uikit tags to each element and add the uk-form-controls div.
template.html
<form class="uk-form-horizontal uk-margin-large uk-align-center">
<div class="uk-margin">
<label class="uk-form-label" for="form-horizontal-text">Job Title</label>
<div class="uk-form-controls">
<input class="uk-input uk-form-width-large" id="form-horizontal-text" type="text" placeholder="Some text...">
</div>
</div>
forms.py
class job_form(forms.Form):
job_title = forms.CharField(label='Job Title', max_length=50)
hiring_manager = forms.CharField(label='Hiring Manager', max_length=50)
job_description = forms.CharField(label='Job Description', max_length=50)
salary = forms.IntegerField()
closing_date = forms.DateField()
I am expecting to be able to have the uikit form styling with the templating and validation of django forms but am yet to get it to work.
Django has a variety of ways to override the form behavior and layout. I see you are using forms.Form instance. Simply add your classes to the form class like:
class NiceForm(forms.Form):
solid_field=forms.CharField(widget=forms.TextInput(attrs={'class': 'uk-form-input'}))
Although it is simple, but sluggish, when you want introduce breaking changes to the layout, I would override the template (or render method if you like) to bundle a reusable widget. Simple example to extract the form to external reusable template as you can render them manually as HTML like documentation.
Leave the form clean and use templatetag to override classes:
# _form_icludes.html
{% for field in form.visible_fields %}
<fieldset class="uk-fieldset">
<div class="uk-margin">
<label class="uk-form-label" for="{{ field.id_for_label }}">{{ field.label }}</label>
<div class="uk-form-controls">
{% if field.field.widget.input_type == 'select' %}
{{ field | add_css_cls:'uk-select'}}
{% elif field.field.widget.input_type == 'option'%}
{{ field | add_css_cls:'uk-option'}}
{% elif field.field.widget.input_type == 'checkbox'%}
{{ field | add_css_cls:'uk-checkbox'}}
{% elif field.field.widget.input_type == 'select'%}
{{ field | add_css_cls:'uk-select'}}
{% elif field.field.widget.input_type == 'file'%}
{{ field }}
{% else %}
{{ field | add_css_cls:'uk-input'}}
{% endif %}
{% if field.help_text %}
<span class="uk-text-small uk-text-left uk-text-italic">
{{ field.help_text }}
</span>
{% endif %}
{% if field.errors %}
{% for error in field.errors %}
<p class="uk-flex uk-flex-middle uk-text-danger ">
<span data-uk-icon="icon:warning" class="uk-text-danger uk-margin-small-right"></span>
{{ error | escape }}
<p/>
</div>
{% endfor %}
{% endif %}
</div>
</fieldset>
{% endfor %}
# add_css_cls
from django import template
register = template.Library()
#register.filter
def add_css_cls(value, arg):
css_classes = value.field.widget.attrs.get('class', '').split(' ')
if css_classes and arg not in css_classes:
css_classes = '%s %s' % (css_classes, arg)
return value.as_widget(attrs={'class': css_classes})
There are many different ways.

Dynamic formset problems

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.

Introducing a custom class in the field_default.html template for django bootstrap

The form fields are rendered by a custom template in https://github.com/earle/django-bootstrap/blob/master/bootstrap/templates/bootstrap/field_default.html via the render_field method in the BootStrapMixin class. Code shown here:-
def render_field(self, field):
""" Render a named field to HTML. """
try:
field_instance = self.fields[field]
except KeyError:
raise NoSuchFormField("Could not resolve form field '%s'." % field)
bf = forms.forms.BoundField(self, field_instance, field)
output = ''
if bf.errors:
# If the field contains errors, render the errors to a <ul>
# using the error_list helper function.
# bf_errors = error_list([escape(error) for error in bf.errors])
bf_errors = ', '.join([e for e in bf.errors])
else:
bf_errors = ''
if bf.is_hidden:
# If the field is hidden, add it at the top of the form
self.prefix_fields.append(unicode(bf))
# If the hidden field has errors, append them to the top_errors
# list which will be printed out at the top of form
if bf_errors:
self.top_errors.extend(bf.errors)
else:
# Find field + widget type css classes
css_class = type(field_instance).__name__ + " " + type(field_instance.widget).__name__
# Add an extra class, Required, if applicable
if field_instance.required:
css_class += " required"
if field_instance.help_text:
# The field has a help_text, construct <span> tag
help_text = '<span class="help_text">%s</span>' % force_unicode(field_instance.help_text)
else:
help_text = u''
field_hash = {
'class' : mark_safe(css_class),
'label' : mark_safe(bf.label or ''),
'help_text' :mark_safe(help_text),
'field' : field_instance,
'bf' : mark_safe(unicode(bf)),
'bf_raw' : bf,
'errors' : mark_safe(bf_errors),
'field_type' : mark_safe(field.__class__.__name__),
}
if self.custom_fields.has_key(field):
template = get_template(self.custom_fields[field])
else:
template = select_template([
os.path.join(self.template_base, 'field_%s.html' % type(field_instance.widget).__name__.lower()),
os.path.join(self.template_base, 'field_default.html'), ])
# Finally render the field
output = template.render(Context(field_hash))
return mark_safe(output)
The problem is that I need to introduce a mycustomclass css class in the controls div, like this:-
<div class="control-group{% if errors %} error{% endif %}">
<label class="control-label">{{ label }}</label>
<div class="controls mycustomclass">
{{ bf }}
{% if errors %}
<span class="help-inline">{{ errors }}</span>
{% endif %}
<p class="help-block">{{ help_text }}</p>
</div>
</div> <!-- /clearfix -->
What's the best way to modify django-bootstrap's render_field method to achieve this?
CLARIFICATION
As mentioned by #okm below, I should have css_class carry the custom class and then since 'css': css_class, I need to place the {{ css }} template variable at the appropriate location in the default_field.html.
So for example, if I have
class MyForm(BootstrapForm):
my_special_field = forms.ModelChoiceField(required=True)
and having
<div class="control-group{% if errors %} error{% endif %}">
<label class="control-label">{{ label }}</label>
<div class="controls {{ class }}">
{{ bf }}
{% if errors %}
<span class="help-inline">{{ errors }}</span>
{% endif %}
<p class="help-block">{{ help_text }}</p>
</div>
</div> <!-- /clearfix -->
will result in the rendered html showing
<div class="controls required">
However, how can I specify more arguments (say using **kwargs) in my form code so that they can be used in the render_field function?

Repopulate django form

Wanted effect is passing the id to the request handler and populating the form with that entity. How doable is it with a template? Here are my form, request handler and template
class AForm(djangoforms.ModelForm):
text = forms.CharField(widget=forms.Textarea(attrs={'rows':'11','cols':'70','class':'foo'}),label=_("content").capitalize())
class Meta:
model = A
fields = ['category','currency','price','title','phonenumber','postaladress','name','text','email'] #change the order
class FileUploadFormHandler(I18NHandler):
def get(self):
cookie_django_language = self.request.get('hl', '')
if cookie_django_language:
if cookie_django_language == 'unset':
del self.request.COOKIES['django_language']
else:
self.request.COOKIES['django_language'] = cookie_django_language
translation.activate(cookie_django_language)
self.render_template("upload.html", {
'form': AForm(),
'form_url': blobstore.create_upload_url('/fileupload'),
'logout_url': users.create_logout_url('/'),
})
<table>
{% for field in form %}
<tr><td>
<div class="fieldWrapper">
{{ form.title.errors }}
<label for="id_subject">{% filter capfirst %}{% trans "headline" %}{% endfilter %}</label></td><td>
{{ form.title }}</td></tr>
</div><tr><td>
<div class="fieldWrapper">
{{ form.category.errors }}
<label for="id_subject">{% filter capfirst %}{% trans "content" %}{% endfilter %}</label></td><td>
You should initialize your form with the entity you want to show:
form = AForm(instance = some_model_instance)