Django Admin color picker

When I add a new model to the Django app at work, I try to make sure the results in the Django admin have good usability, too. Just because it’ll be mainly developers (and a few project managers) seeing it doesn’t mean I can neglect usability. Consequently, I spend some time on hacking the Django admin. My manager says I’ve been making it do things he didn’t even realize were possible.

The most recent thing I did was add a color field to a model. I considered Django Colorfield, but there’s an open issue about it using GPLv3-licensed code while being MIT-licensed. 😬  So, I dug around a bit.

First, I set up the model to have a primary_color that’s a CharField of length 7, to accommodate a standard"#FF00FF" RGB HEX code.

class MyModel(models.Model):
     primary_color = models.CharField(max_length=7, default="#FFFFFF")

Then, I found a StackOverflow answer that pointed out that the Django docs include how to override the widget used for a form element, so you can change which HTML5 rendering is used for <input> fields. Cool! So I just needed to add a forms.py like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from django.forms import ModelForm
from django.forms.widgets import TextInput
from myapp.models import MyModel


class MyModelForm(ModelForm):
    class Meta:
        model = MyModel
        fields = "__all__"
        widgets = {
            "primary_color": TextInput(attrs={"type": "color"}),
        }

and update the admin.py to use it:

from myapp.forms import MyModelForm

@admin.register(MyModel)
class MyModel(admin.ModelAdmin):
    form = MyModelForm

and I’d be all set, right? Indeed, it did now show me a filled-in color block in the admin!

But I still wasn’t happy. I wanted a way to see the HEX code for the color just by looking at it in the admin. With the HTML5 element’s behavior, you have to click on the color block to get a pop-up color picker and go from there to find the HEX. In Chrome on my Mac, I had to click two more times to get it to HEX mode. Ugh.

So, I dug around some more. I wasn’t able to find a way to display the HEX alongside the color block, so I decided to try for putting it in a tooltip. I needed a way to get the instance of the form field, so I could read its value. With further searching, I stumbled upon a mostly-unrelated StackOverflow answer that mentioned self.instance. Great, now I just had to figure out where self was accessible from in ModelForm. Almost there! After consulting the Django docs to see which methods have access to self, I landed on this solution. In that same MyModelForm class in forms.py, I added:

13
14
15
16
17
18
    def __init__(self, *args, **kwargs):
        super(MyModelForm, self).__init__(*args, **kwargs)
        if self.instance:
            self.fields["primary_color"].widget = TextInput(
                attrs={"type": "color", "title": self.instance.primary_color}
            )

This grabs the value of the color field and sets it as the title attribute on the element. Title attributes display as tooltips in the browser.