Django 1.11 widgets

by Hélio Correia

django

Django widgets on 1.11

Django had released it's last version with a new way to implement his widgets. Before we have to write in pure python and now we can use html to make it more readable.

In this post, I'll gonna teach you how you can implement one of your one but showing you how to implemente a time picker using this js package pickadate. If you have difficulties to follow along, all the source code can be find on this repo

Create the widget class

The first step is to create the widget class, in my code I create an app with a file that i call widgets. Then I create a class called PickADateDateWidget that inherit from the base widget class. Then I change the template path to implement one on my own.

from django.forms import widgets


class PickADateDateWidget(widgets.Input):
    template_name = 'widgets/pickadate.html'

Add you assets

To make this work you need the js and css files from the pickadate package. To do that you add the class Media inside the PickADateDateWidget class that I create, and copy that files to your static folder that django will find.

from django.forms import widgets


class PickADateDateWidget(widgets.Input):
    class Media:
        css = {
            'all': ('pickadate/themes/default.css', 'pickadate/themes/default.date.css')
        }
        js = ('https://code.jquery.com/jquery-3.2.1.js', 'pickadate/picker.js', 'pickadate/picker.date.js')

    template_name = 'widgets/pickadate.html'

Create the template

Now lets see the template that I created in widgets/pickadate.html

<input name="{{ widget.name }}" {% if widget.value != None %}value="{{ widget.value }}"{% endif %}
       {% include "django/forms/widgets/attrs.html" %} 
       onclick="$(this).pickadate()" />

Here I grab the default template on Widget class and modify to meet my needs.

Make it working

To see it working you need to create a form and specify that widget. To make it really simple I create a task model, registered that model on admin, override it's form and specify that widget on that form.

# forms.py

from django import forms

from .widgets import PickADateDateWidget
from .models import Task


class TaskForm(forms.ModelForm):
    class Meta:
        model = Task
        fields = "__all__"
        widgets = {'due_date': PickADateDateWidget}


# admin.py

from django.contrib import admin

from .forms import TaskForm
from .models import Task


@admin.register(Task)
class TaskAdmin(admin.ModelAdmin):
    form = TaskForm

Now you have the bare minimum widget working. Now I only add one improvement.

Add vars to your template

To pass a variable to widget template it's simply as override the method get_context and add the variables that you need. In this example I had a format for the date

class PickADateDateWidget(widgets.Input):
    class Media:
        css = {
            'all': ('pickadate/themes/default.css', 'pickadate/themes/default.date.css')
        }
        js = ('https://code.jquery.com/jquery-3.2.1.js', 'pickadate/picker.js', 'pickadate/picker.date.js')

    template_name = 'widgets/pickadate.html'

    def get_context(self, name, value, attrs):
        context = super().get_context(name, value, attrs)
        context['options'] = {'format': "yyyy-mm-dd"}
        return context

and in template ends like this

<input name="{{ widget.name }}" {% if widget.value != None %}value="{{ widget.value }}"{% endif %}
       {% include "django/forms/widgets/attrs.html" %}
       onclick="$(this).pickadate({{ options }})" />

And that's it, I hope you like and if you need any question or ideas, leave a commend in bellow.