(private?) API change for subclassed fields

Hi,

One of the highlighted new features for Django 6.1 is Database-level delete options for ForeignKey.on_delete. It is indeed an important feature. But as part of its implementation, ForeignKey’s check() method now passes the configured databases down to its _check_on_delete() part.

I have written a custom field which is a subclass of ForeignKey, and has unique requirements of the on_delete parameter (as well as other unique checks). So I implemented my own check() as

    def check(self, **kwargs):
        return [
            *self._some_specific_check(),
            *super().check(**kwargs),
        ]

    def _some_specific_check(self):
        # ...

    def _check_on_delete(self):
        # ...

Trusting the parent check() to call my _check_on_delete(). Current main-branch Django breaks this (there’s an additional, unexpected argument), but there is nothing about it in the release notes.

Part of me says “this is on you, for overriding a private method”. Another part of me says “I did the most reasonable thing, there should have been a warning in the release notes”.

If people feel like my other part, I’m more than willing to write that warning, but I’m really not sure. My first part says this may open a door that, as a project, we’d much rather keep closed.

What do you think?

This is correct. It is documented at API stability | Django documentation | Django.
Internal APIs are one of the exceptions to the “stability and backwards-compatibility promise”. They may be changed at any time.

Anytime you directly rely upon or override an internal API, you need to be aware that the next upgrade could break your code.

1 Like

Let me ping @felixxm and @charettes here.

Either way, I don’t see us needing to adopt a policy about documenting such changes, but I think we’ve added release notes in the past where we’ve known a tricky migration is coming up. (Even if not covered by the stability policy.)

2 Likes

It seems to me that you overrode a private method, and the failure would occur the first time you ran nearly any management command on Django 6.1, because most management commands run system checks. (Or a type checker… :wink: ). I think this is a reasonable kind of thing to expect when touching internal parts, and the best kind of obvious failure.

1 Like

I share the general sentiment that we should not commit to documenting private APIs change that we make.

As @carltongibson pointed out we do so when it’s not clear how code should be adapted from the resulting error but I see this more as a courtesy.

In this case the error was likely obvious (a TypeError for a mismatched call signature) so I think it’s fair to assume that users who venture into overriding internals are familiar enough with the code base to navigate them adequately.

1 Like

Thank you all, and yes, you’re right, this doesn’t need a mention in the release notes.