How to format Django form with UIKit styling - django

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.

Related

Use two forms at the same time

I want to submit two forms in my template. The reason for that is, that I have two separate models, each with a model form, which I have to submit at the same time to create the desired result:
I have these two forms:
class BootstrapModelForm(ModelForm):
def __init__(self, *args, **kwargs):
super(BootstrapModelForm, self).__init__(*args, **kwargs)
for field in iter(self.fields):
self.fields[field].widget.attrs.update({
'class': 'form-control'
})
class DeckForm(ModelForm):
class Meta:
model = Deck
exclude = ('dust',)
class GuideForm(BootstrapModelForm):
class Meta:
model = Guide
exclude = ('author', 'upvotes', 'downvotes', 'release_date', 'modified', 'deck')
def __init__(self, *args, **kwargs):
super(GuideForm, self).__init__(*args, **kwargs)
self.fields['title'].label = 'Title of your AWESOME Guide!'
self.fields['public'].label = 'Check, when you want to make your guide public'
self.fields['public'].widget.attrs.update({
'class': 'checkbox'
})
self.fields['introduction'].label = 'Introduction (not required, but recommended)'
A deck is a part of the Guide that gets created. Since the user should be able to create his own deck while writing his guide, I need two forms.
In my views I handled it like this:
def guide_create(request):
if request.method == "POST":
deck_form = DeckForm(request.POST)
guide_form = GuideForm(request.POST)
if guide_form.is_valid() and deck_form.is_valid():
new_deck = deck_form.save(commit=False)
new_deck.dust = 0
new_deck.save()
new_guide = deck_form.save(commit=False)
new_guide.author = Account.objects.get(id=request.user.id)
new_guide.deck = Deck.objects.get(id=new_deck.id)
new_guide.save()
else:
print(guide_form.errors)
print(deck_form.errors)
else:
deck_form = DeckForm(prefix = 'DeckForm')
guide_form = GuideForm(prefix = 'GuideForm')
return render(request, 'hsguides/guide_create.html', {
'DeckForm': DeckForm,
'GuideForm': GuideForm,
})
I use commit=False so I still can insert the deck in my guide. Now my problem comes with creating the template. When I submit the forms with my current set up, I receive no errors any more but the guide and the deck aren't saved either!
<div style="width: 60%; margin: 0 auto;">
<form class="form-horizontal" method="POST" action="{% url 'GuideCreate' %}"> {% csrf_token %}
<fieldset>
{% for field in DeckForm %}
{% if field.errors %}
<div class="class-group error">
<label class="control-lable">{{ field.label }}</label>
<div class="controls">{{ field }}
<span class="help-inline">
{% for error in field.errors %}{{ error }}{% endfor %}
</span>
</div>
</div>
{% else %}
<div class="control-group">
<label class="control-label">{{ field.label }}</label>
<div class="controls">{{ field }}</div>
</div>
{% endif %}
{% endfor %}
</fieldset>
<fieldset>
{% for field in GuideForm %}
{% if field.errors %}
<div class="class-group error">
<label class="control-lable">{{ field.label }}</label>
<div class="controls">{{ field }}
<span class="help-inline">
{% for error in field.errors %}{{ error }}{% endfor %}
</span>
</div>
</div>
{% else %}
<div class="control-group">
<label class="control-label">{{ field.label }}</label>
<div class="controls">{{ field }}</div>
</div>
{% endif %}
{% endfor %}
</fieldset>
<div class="form-actions" style="margin-top: 4px;">
<button type="submit" class="btn btn-default">Submit</button>
</div>
</form>
</div>
Edit I don't receive an error message any more, the deck gets created as well but the guide that belongs to it not.
Just Change
return render_to_response('hsguides/guide_create.html', {
'DeckForm': DeckForm,
'GuideForm': GuideForm,
})
to
return render(request,'hsguides/guide_create.html', {
'DeckForm': DeckForm,
'GuideForm': GuideForm,
})
You're saying the guide doesn't get created. I see this line new_guide = deck_form.save(commit=False)..... shouldn't it be new_guide = guide_form.save(commit=False) ?
Not 100% but this might be a mistake. Hopefully, it's not just because you refactored the code for putting it here.
I didnot check the whole thing, but this place is wrong, it should be:
if request.method == "POST":
deck = DeckForm(request.POST)
if deck.is_valid():
# ....
you are not packing the POST data into your forms..
the same goes for the second form as well
Try this, you might forgot to pass the RequestContext:
from django.template import RequestContext
return render_to_response('hsguides/guide_create.html',
{'DeckForm': DeckForm, 'GuideForm': GuideForm},
RequestContext(request))
It passes the csrf token to the template.

Django: How to output error_message in paragraph <p> instead of list <li> format

I have a simple form which uses a SessionWizardView to spread it over a number of pages. Below is an example of one of the questions.
first_name = forms.CharField(max_length=100, label='What is your first name?', error_messages={'required': 'Please enter your first name'})
Which renders out as
<label for="id_0-first_name">What is your first Name?</label>
<ul class="errorlist">
<li>Please enter your first name</li>
</ul>
<input id="id_0-first_name" maxlength="100" name="0-first_name" type="text" />
Can anyone tell me hwo to change the error output so that it is in <p> Paragraph </p> format rather than <li> List item </li> format?
I am using Django 1.6.2
You'll have to create a class that does renders the HTML as you would want it. See the docs here.
The example from the docs:
from django.forms.util import ErrorList
class DivErrorList(ErrorList):
def __unicode__(self):
return self.as_divs()
def as_divs(self):
if not self: return u''
return u'<div class="errorlist">%s</div>' % ''.join([u'<div class="error">%s</div>' % e for e in self])
f = ContactForm(data, auto_id=False, error_class=DivErrorList)
f.as_p()
You can do as #schillingt suggested and create your own error list class.
Or, if you want to handle this in your template, you can use something like:
<form method="post" action="/some-view/">
... other fields, etc. omitted ...
<!-- The label and form field -->
{{ form.first_name.label_tag }}
{{ form.first_name }}
<!-- Output any errors -->
{% for error in form.first_name.errors %}
<p>{{ error }}</p>
{% endfor %}
... other fields, etc. omitted ...
<button type="submit">Submit</button>
</form>
Update
In order to do this in a cleanly repeatable way, make a template named form-field.html:
{{ field.label_tag }}
{{ field }}
<!-- Output any errors -->
{% for error in field.errors %}
<p>{{ error }}</p>
{% endfor %}
Then, update your main template:
<form method="post" action="/some-view/">
... other fields, etc. omitted ...
{% with field=form.first_name %}
{% include "form-field.html" %}
{% endwith %}
... other fields, etc. omitted ...
<button type="submit">Submit</button>
</form>
You can then make updates to the single form-field.html template and update all of your forms, and it makes your main template a bit simpler

Completely stripping certain HTML Tags in Django forms

I have a ModelForm that posts news items to a database, and it uses a javascript textarea to allow the authorized poster to insert certain pieces of HTML to style text, like bold and italics. However, since I have the template output using the "safe" filter, it outputs all the HTML the form widget tries to pass on. This includes a bothersome <br> tag that never goes away, making it so you can submit without form validation reading the field as empty and stopping you. How can I make that I can not only filter the <br> tag, but completely remove it from the data? Here is relevant code:
Models.py:
from django.db import models
from django.forms import ModelForm, forms
from django.contrib.auth.models import User
# Create your models here.
class NewsItem(models.Model):
user = models.ForeignKey(User)
date = models.DateField(auto_now=True)
news = models.TextField(max_length=100000, blank=False, help_text='HELP TEXT')
def __unicode__(self):
return u'%s %s %s' % (self.user, self.date, self.news)
class NewsForm(ModelForm):
class Meta:
model = NewsItem
exclude=('user','date',)
Views.py:
from news.models import NewsForm, NewsItem
from django.shortcuts import render
from django.http import HttpResponseRedirect, HttpResponse
def news(request):
if request.method == 'POST':
item = NewsItem(user=request.user)
form = NewsForm(request.POST, instance=item)
if form.is_valid():
form.save()
return HttpResponseRedirect('/news/')
else:
form = NewsForm()
news_list = NewsItem.objects.all()
return render(request, 'news_list.html', {'news_list': news_list, 'form': form})
news_list.html:
{% extends "base.html" %}
{% block title %}News in the Corps{% endblock %}
{% block content %}
<h2 id="page_h">News in the Corps</h2>
{% if user.is_authenticated %}
<h3>Post News</h3>
<script src="{{ STATIC_URL }}nicEdit.js" type="text/javascript"></script>
<script type="text/javascript">bkLib.onDomLoaded(nicEditors.allTextAreas);</script>
<div id="news_poster">
<form id="news_poster" action="/news/" method="POST">{% csrf_token %}
{{ form }}
<input type="submit" value="Submit" />
</form>
</div>
{% endif %}
<ul id="events_list">
{% if news_list %}
<div id="news_list">
{% for news in news_list %}
{% if news.id == 1 %}
<hr />
{% endif %}
<div id="{{ news.id }}" class="news_item">
<p class="poster">Posted By: {{ news.user }} | Posted On: {{ news.date }} | Link</p>
<div id="news_item">
{{ news.news|safe }}
</div>
</div>
<hr />
{% endfor %}
</div>
{% endif %}
</ul>
{% endblock %}
You can try the removetags template filter:
{{ news.news|removetags:"br"|safe }}
I can't help but thinking that the "removetags" as Timmy O'Mahony suggested might work if it was structured like this:
{{ news.news|safe|removetags:"br"}}
Give it a shot and see if it works. I would reply, but my karma's not height enough to directly reply to an answer with a suggestion.

Access form field attributes in templated Django

I've been doing some custom forms with django but I don't get how to access attributes that a specific form field has attached via the forms.py.
def putErrorInTitle (cls):
init = cls.__init__
def __init__ (self, *args, **kwargs):
init(self, *args, **kwargs)
if self.errors:
for field_error in self.errors:
self.fields[field_error].widget.attrs['title'] = self.errors[field_error][0]
self.fields[field_error].widget.attrs['class'] = "help_text error_field"
cls.__init__ = __init__
return cls
That's how I attached the attibutes to the field.
<dl class="clearfix two">
<dd>
<label for="id_diagnosis">Diagnostico:</label>
<select class="{{form.id_diagnosis.class}}" id="id_equipment_activity-{{ forloop.counter0 }}-id_diagnosis" name="equipment_activity-{{ forloop.counter0 }}-id_diagnosis">
{% for x,y in form.fields.id_diagnosis.choices %}
<option value="{{ x }}" {% ifequal form.id_diagnosis.data|floatformat x|floatformat %}selected="selected"{% endifequal %}>{{ y }}</option>
{% endfor %}
<option value="1000" {% ifequal form.id_diagnosis.data|floatformat '1000'|floatformat %}selected="selected"{% endifequal %}>Otro</option>
</select>
</dd>
<dd class="vertical_center" id="optional_diagnosis"><label for="optional_diagnosis">Diagnostico opcional:</label>{{ form.optional_diagnosis }}</dd>
</dl>
I've been trying to access its attributes:
class="{{form.id_diagnosis.class}}", class="{{form.id_diagnosis.widget.class}}"
And I don't seem to find clear documentation about what's accessible and what's not. Really I would rather have old fashion documentation than django "friendly" one
In other cases it can can be useful to set and get field attributes.
Setting in form's init function:
self.fields['some_field'].widget.attrs['readonly'] = True
... and accessing it in a template:
{{ form.some_field.field.widget.attrs.readonly }}
It looks like you just want to display form errors for each field.
After the form is cleaned or validated in the view, the fields should contain
the error messages. So that you can display them in the template like so:
<form action='.' method='post'>
...
<div class='a-field'>
{{ form.field_1.errors|join:", " }}
{{ form.field_1.label_tag }}
{{ form.field_1 }}
</div>
...
</form>
If however you really want to display the form field attributes then you
can try something like:
{{ form.field_1.field.widget.attrs.maxlength }}
The above answers are correct, however, I'd like to add a note for those who are accessing form fields in a loop.
If you're doing this in a loop like this
{% for field in form %}
{{ field.field.widget.attrs.placeholder }} # field.field is the key here
{% endfor %}

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?