In my Home Inventory app I implemented the model for Measurement and each Item has its own Measurement.

Later I got tired of selecting measurement value for each new item in the UI as in 99% cases I selected “pcs”. I didn’t want to hardcode initial value, so I started to investigate: how to allow only one row to have a specific field value? Or, in my case, how to create a Django model where only one row can have value True?

Solution

The solution is to add unique constraint on that field. Here’s my Measurement model:

class Measurement(models.Model):
    """Model representing measurement of the item, e.g. Pieces"""

    name = models.CharField(max_length=100, unique=True, help_text="Measurement unit")
    is_default = models.BooleanField(
        default=False,
        help_text="If True, it's default measurement for new item."
        "Only one measurement can be default.",
    )

    class Meta:
        ordering = ["name"]
        constraints = [
            models.UniqueConstraint(
                fields=["is_default"],
                name="Only one measurement can be default",
                condition=models.Q(is_default=True),
            )
        ]

So the constraint

constraints = [
            models.UniqueConstraint(
                fields=["is_default"],
                name="Only one measurement can be default",
                condition=models.Q(is_default=True),
            )
        ]

makes it possible for only one measurement to have is_default value set to True.

Make it work in the admin

Although the solution works, it’s also nice to make it foolproof in the admin UI.

To achieve that, I wrote the following code:

@admin.register(Measurement)
class MeasurementAdmin(RelatedFieldAdmin):
    search_fields = ("name",)
    list_display = (
        "name",
        "is_default",
    )

    def get_readonly_fields(self, request, obj=None):
        """
        Make 'is_default' readonly if some measurement is already marked as default
        """
        if not obj.is_default and Measurement.objects.all().filter(is_default=True):
            return self.readonly_fields + ("is_default",)
        return self.readonly_fields

Overriding get_readonly_fields makes it impossible to even try to select addition measurement as default. In case one measurement is already a default measurement, for all other measurements is_default field is not editable.

Apply default value in the form

I wanted to display the default value in the form. In the view that renders that form, I first request that value.

However, there’s no requirement for default value to exist, so this case should be handled as well.

try:
    default_measurement = Measurement.objects.get(is_default=True)
except Measurement.DoesNotExist:
    default_measurement = ""

Then I simply pass initial arg to the form:

initial={"measurement": default_measurement},

and the default measurement value is displayed in measurement field for all new items. That’s it!