Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add CSS class to widget/field with Django 1.11 template-based form rendering

Note: This question shouldn't be conflated with past questions that are similar but before Django 1.11, when they released template-based form rendering.

I understand that Django now has template-based form rendering. From what I understand, this is supposed to fix the issue of having to inject CSS classes from the view or the form, rather than keeping all of the HTML/CSS in the templates.

That is my goal: to keep my forms and views focused on what is displayed, and my templates focused on how that is displayed. So I want to keep all HTML/CSS in my templates.

So, my questions are:

  • How do I add a class (for example, form-text) to all TextInput widgets from the template system?
  • How do I add a class (for example, alert-warning) to all error messages (validation failures) from the template system?

I may have misunderstood something about this new feature, so if I did, feel free to let me know if this isn't how it works or if I am asking the impossible. Ideally, I would like to implement these form rendering changes to the master template.

Sample Problem

views.py:

class SignUp(generic.edit.CreateView):

    model = models.User
    template_name = 'usermgmt/sign_up.html'
    form_class = forms.UserCreateForm
    success_url = '/sign_up_done/'

templates/master.html (I want to put something in here that causes all TextInput widgets to get a class):

<html>
<head>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ" crossorigin="anonymous">
<title>{% block title %}{% endblock %} | Website</title>
</head>
<body>
  <div class="content-wrapper clearfix">
    {% block main %}{% endblock %}
  </div>
</body>
</html>

templates/usermgmt/sign_up.html:

{% extends 'master.html' %}

{% block title %}Sign Up{% endblock %}

{% block main %}
<h1>Sign Up</h1>
<p>Enter your email to sign up!</p>
<form class="form-group" method="post">
  {% csrf_token %}
  <input type="hidden" name="next" value="{{ next }}">
  {{ form.as_p }}
  <button class="btn btn-primary" type="submit">Sign Up</button>
</form>
{% endblock %}
like image 942
Greg Schmit Avatar asked Feb 05 '17 05:02

Greg Schmit


People also ask

What a widget is Django how can you use it HTML input elements?

A widget is Django's representation of an HTML input element. The widget handles the rendering of the HTML, and the extraction of data from a GET/POST dictionary that corresponds to the widget. The HTML generated by the built-in widgets uses HTML5 syntax, targeting <! DOCTYPE html> .

What is form AS_P in Django?

{{ form.as_p }} – Render Django Forms as paragraph. {{ form.as_ul }} – Render Django Forms as list.

What is Attrs Django?

attrs . A dictionary containing HTML attributes to be set on the rendered DateInput and TimeInput widgets, respectively. If these attributes aren't set, Widget. attrs is used instead.


2 Answers

Continuing from José Tomás Tocino's answer above, In Django 1.11 I found that my directory structure needed to be:

main/templates/django/forms/widgets/text.html

like image 85
Anna Howell Avatar answered Oct 04 '22 14:10

Anna Howell


As far as I've understood from the release notes of Django 1.11 and from my own testing, the new features only allows us to easily customize the way widgets are rendered by simply providing HTML files for each widget type. In previous Django versions you had to provide a subclass of forms.widget.Widget and assign that widget to your model/form fields one by one (IIRC).

If you wanna make all TextInput widgets have a specific class, you're gonna have to add your custom text.html (and probably your own attrs.html as well).

For example, let's say I have a myforms project with a main app. According to the docs:

If you use the TemplatesSetting renderer, overriding widget templates works the same as overriding any other template in your project. You can’t override built-in widget templates using the other built-in renderers.

So we need to set our renderer in the settings.py file:

FORM_RENDERER = 'django.forms.renderers.TemplatesSetting'

But if we only do that, we'd need to provide templates for all input elements. According to the docs:

Using this renderer along with the built-in widget templates requires [...] [adding] 'django.forms' in INSTALLED_APPS and at least one engine with APP_DIRS=True.

So we add django.forms to INSTALLED_APPS. Now, in order to add our customization for, say, text input elements, we need to create the file main/templates/django/forms/widgets/text.html and add our custom HTML code there, like:

<input type="text" class="myclass" value="{{widget.value}}">

So, if we show a form in a template, that code will appear whenever a CharField need to be rendered, for example.

But all of this doesn't matter if you need a per-field level of control, for example if each field needs a different class. In that case, you should combine what I wrote with django-widget-tweaks so you can have more flexible widget customization.

like image 40
José Tomás Tocino Avatar answered Oct 04 '22 14:10

José Tomás Tocino