Automatically use `help_text` for `db_comment`?

I totally missed that django now supports database comments on model fields. That’s great! My next thought was that it would be really nice if help_text could be put into the db_comment field in some sort of automatic way. We always complete the help_text field, so it’d be a huge win to sync it to the DB somehow.

For example, we have this field:

cluster_start = models.ForeignKey(
    OpinionCluster,
    help_text="The starting cluster for the visualization",
)

And I guess what we’re going to wind up doing is something like:

cluster_start_help = "The starting cluster for the visualization",
cluster_start = models.ForeignKey(
    OpinionCluster,
    help_text=cluster_start_help,
    db_comment=cluster_start_help,
)

And that’s OK, I suppose. But it’d be really nice to just have it sync.

In the issue that landed the db_comment attribute, somebody mentioned a setting for this, ENABLE_DB_COMMENT_WITH_HELP_TEXT, but doing it via a setting wasn’t popular and it died on the vine. In the PR that landed it, somebody suggested using verbose_name for this, but, “verbose_names are for users and db_comments are for DBAs.”

I agree with all of this, but it does feel redundant to me to have to describe a field twice.

I wonder if there’s a way to do something like:

cluster_start = models.ForeignKey(
    OpinionCluster,
    help_text="The starting cluster for the visualization",
    db_comment=True,
)

If that could work automatically, that’d be really nice. I’m curious what other folks think.

Have a great day and happy holidays,

Mike

This is something I just started implementing in my project. For my use case, I wanted to dynamically generate a column’s database comment on integer fields with choices attribute.

The only way I can think of, and one I’m using, is defining a custom field.

We do something like that for the help text. We define choices outside of the field definition and then do something like (notionally):

help_text=f"Flavors of pizza. Options are: {', '.join(choice for choice in choices)}",

It more or less works fine without needing a custom field.

db_comment and help_text are fundamentally different as the former is for developers and database admins and the other for the end users visiting or using the website.

These fields may hold the same value, but I don’t believe that having them sync would benefit a lot of use-cases. Thinking about my projects, I come up with a lot of fields where I don’t want my help_text be the db_comment, because it would not offer any helpful information to the DB admin – actually, I think it would just be confusing.

I think adding an optional sync would add unwelcome complexity to the codebase and documentation. Reusing the same string on both parameters where it makes sense is my preference.

3 Likes

Yeah, I’d also be -1 on adding any kind of automatic copying here. For one, it would bloat schemas on projects that define help_text for the UI, but never need db_comment - which I think is the vast majority of users, as using raw SQL is rare.

You can also use a walrus to avoid the extra line and have a kind of explcit marker that db_comment copies from help_text:

cluster_start = models.ForeignKey(
    OpinionCluster,
    help_text=(ht := "The starting cluster for the visualization"),
    db_comment=ht,
)

Sounds like there’s no love for this idea, but I do think my subject should have been:

Add feature to use help_text for db_comment

Instead of the heading I used, which suggested doing it “automatically.” Sorry if that was confusing!

Maybe we’ll play with the walrus approach. It’s funky, but I still find all walrus code weird. I’m surprised people find the help_text field to be such an ill fit for the db_comment. I find it to be a huge step up from the nothing that we have now. :slight_smile:

1 Like

The consensus seems to be that setting help_text and db_comment to the same value isn’t something that should be in Django, but for those who still want to do it (me), I made a decorator that I figured would be nice to share here.

It uses the docstring to populate unset help_text and db_comment attributes (uses regex to extract from the format we currently use at Free Law, but that should be pretty easy to modify).

FIELD_DOCSTRING_EXTRACTION_RE = re.compile(
    r":(?:var|ivar|cvar)\s+([a-z_]+):([^:]+)", re.IGNORECASE | re.MULTILINE
)


def document_model(model: type[models.Model]) -> type[models.Model]:
    """
    Decorator for Django models to use docstrings to populate unset
    help_text and db_comment field attributes.
    """
    model_fields: dict[str, models.Field] = dict(
        [(field.name, field) for field in model._meta.local_fields]
    )
    docstring = model.__doc__
    ivar_docs = [
        (match.group(1).strip(), match.group(2).strip())
        for match in FIELD_DOCSTRING_EXTRACTION_RE.finditer(docstring)
    ]
    field_docs = dict(
        [
            (field_name, docstring.replace("\n", " "))
            for field_name, docstring in ivar_docs
            if field_name in model_fields
        ]
    )

    for field_name, field in model_fields.items():
        if field_name not in field_docs:
            continue

        doc = field_docs[field_name]
        if isinstance(field.help_text, str) and len(field.help_text) == 0:
            field.help_text = doc
        if (
            isinstance(field.db_comment, str)
            and len(field.db_comment) == 0
            or field.db_comment is None
        ):
            field.db_comment = doc
        print(getattr(model, field_name))

    return model