Django Admin color picker
Tags: django, usability
Published: 2021-03-31 08:14:56

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](https://github.com/fabiocaccamo/django-colorfield), but there's an open issue about it using GPLv3-licensed code while being MIT-licensed. :grimacing: &nbsp;So, I dug around a bit.

## Basic color picker
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.

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

Then, I found a [StackOverflow answer](https://stackoverflow.com/questions/39859224/how-to-use-html5-color-picker-in-django-admin) 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:

```python
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:
```python
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!

## Adding a tooltip

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](https://stackoverflow.com/a/5129454) 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:

```python
    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.