KayleeTheMech
KayleeTheMech

Reputation: 498

Enforce or test on_delete behavior of all ForeignKey fields using a specific model

Let's say I have a proxy user model as

class UserWithProfile(User):
    profile_description = models.TextField()

    class Meta:
        proxy = True
        ordering = ('first_name', )

I want to make certain that all data which could in the future be associated with a UserWithProfile entry is deleted when this profile is deleted. In other words I want to guarantee the on_delete behavior of all existing and future ForeignKey fields referencing this model.

How would one implement either a test checking this, or raise an error when another on_delete behavior is implemented?

I know it would be possible to make a custom ForeignKey class, which is what I will be probably doing, ...

class UserWithProfileField(models.ForeignKey):
    def __init__(self, *args, **kwargs):
        kwargs.setdefault('to', UserWithProfile)
        kwargs.setdefault('on_delete', models.CASCADE)
        super().__init__(*args, **kwargs)

... however that couldn't stop future users from using the ForeignKey class with a different on_delete behavior.

Upvotes: 3

Views: 50

Answers (2)

Alper
Alper

Reputation: 3973

I don't get the invariants you are starting out with:

  • It's irrelevant whether you want to delete references to User or UserWithProfile since these are the same table?
  • You cannot police what other tables and model authors do and in which way shape or form they point to this table. If they use any kind of ForeignKey that's fine, but they could also point to the table using an unconstrained (integer?) field.

Could you make a test that bootstraps the database and everything, iterates over all models (both in this app and others) and checks every ForeignKey that is there to see if it is pointing to this model and it is setup correctly? That should serve the intended goal I believe.

Upvotes: 1

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476709

Instead of setdefault, you can override the on_delete parameter:

class UserWithProfileField(models.ForeignKey):
    def __init__(self, *args, **kwargs):
        kwargs['to'] = UserWithProfile
        kwargs['on_delete'] = models.CASCADE
        super().__init__(*args, **kwargs)

regardless what the user will now use for to=… or on_delete=…, it will use UserWithProfile and CASCADE.

Strictly speaking one can of course still try to alter the attributes of the ForeignKey, but that is more complicated, especially since Django constructs a ForeignObjectRel object to store relation details.

Note that a proxy model [Django-doc] is not used to add exta fields to the model. THis is more to alter the order, etc. and define new/other methods.

Upvotes: 1

Related Questions