-
Notifications
You must be signed in to change notification settings - Fork 13
Discussion: Addressing reasonable model.full_clean() behavior. #35
Description
Original ninja issue: vitalik/django-ninja#443
Overview
Basically there are two parts to this issue:
model.full_clean()is not called by default (known Django decision for backwards compatibility)model_full_clean()is not async compatible, so it would be possible to call this in an unsafe manner when left to users to decide how or when to call.
Background
In a nutshell, model.full_clean() performs model-level validation checks. Django has historically disabled this by default, but recommends manually calling it on save(), which has been a fairly contentious decision and infamous footgun, as not doing so can lead to data corruption.
Discussion
First off, I think that the Django decision to not call model.full_clean() by default is somewhat defensible for Django core, where legacy apps may have existed for many years. However, ninja and shinobi are new frameworks (and generally new use cases) on top of Django and it would be perfectly reasonable to call model.full_clean() by default and instead allow people to opt out.
The reason I recommend enabling by default is Django and ninja's "just works no surprises" approach, which is widely loved. However, this is at odds with being able to specify validations at the model level that you reasonably could assume would be enforced but default to not enforcing them. IMO, it is more surprising that you would define validation at the model level and then choose to ignore it, especially given we already have the ability to use Schema-level validation for more bespoke validation use cases that would not be applied to all interactions with a particular model.
Possible Solutions
While enabling by default addresses reasonable expectations, given the async focus on Django Ninja, it is possible this could lead to issues with the model.full_clean() behavior not being async compatible. One idea, from the original ninja issue:
One option Is there possibly some way forward using a flag as described above + one the async adapter functions in conjunction with an asyncio.get_running_loop() test to see if the code is running async? E.g. do nothing in sync context, and in async call the adapter to ensure it's a safe/sync operation?
Would love to hear more thoughts on this - thank you!