How to Link Multiple Child Classes to the Same Class

I’m currently working on refactoring a set of data model classes in my application:

  • Contract
  • ContractDocument (Abstract Base Class)
    – GeneratedContractDocument (Child class of ContractDocument)
    – UserUploadedContractDocument (Child class of ContractDocument)

I need to create a new “At most One” relationship between Contract and the child classes, such that a Contract can contain one GeneratedContractDocument OR one UserUploadedContractDocument OR neither, but never BOTH.

I’m trying to decide between:

  • Multiple foreign keys on Contract that each point to the respective child class.
  • A generic foreign key on Contract that can point to each child class.
  • A single OneToOneField on ContractDocument that allows each child class to link back to Contract.

I was initially most in favor of the third option, however I’m concerned about the cleanliness/complexity of enforcing the uniqueness across child classes for Contract. I’d love a recommendation (and explanation) for which option seems to be the best.

Thanks in advance!

Welcome @Angusman17 !

This is one of those cases where there aren’t any well-defined solutions based solely on the relational model. Any solution is going to require some management based in code.

Beyond the options you’ve identified, there’s a fourth option that would be my choice.
(This is in the absence of understanding the complete application, the underlying data requirements, and the entities being modeled - factors that in reality could affect the design.)

I’d make ContractDocument a concrete class, and the GeneratedContractDocument and UserUploadedContractDocument child classes. (See MultiTable inheritance)

This shifts the management of the child classes to the ContractDocument class, which superficially appears to be the better place to try and handle the necessary constraints. You can define methods on both the model and its manager to ensure that only one of the child classes exist.

You end up with ContractDocument having a OneToOneField with Contract, and the implicit OneToOneFields in the two child classes to ContractDocument.

In the general case, I’m not a fan of generic foreign keys - they have value and are necessary in some cases, but this isn’t one of them.

As a general principle, I’m also not a fan of nullable foreign keys.

That leaves your third option - but with the requirement that the Contract manage the “single child” constraint. Superficially, this “feels” like the wrong location to manage that constraint.

Thanks for the welcome Ken!

Ah, thanks for calling out that 4th option Ken! I’ll try to digest this suggestion and determine if it feels more “correct” than the 1st option I presented in the context of my app. In general, I’m also weary of adding nullable foreign keys on a model; feels like cluttering the model’s schema more than necessary so point taken. I assume it doesn’t actually make storage space any worse, at least.