I left some notes on the PR but I think the crux of the issue here is that 
you are trying to change the meaning of Model.save(force_insert=True) from 
force the insert of the current model to force the insert of the model and 
all its bases.

This is a problem not only for QuerySet.create but likely for a many other 
use cases in the wild that need a way to only force the creation of the 
leaf table but not others. In other words, the change you are proposing 
take one feature way (only insert leaf model) to introduce a new one 
(insert all bases) and thus is fundamentally backward incompatible.

To me the only way forward here is to introduce a new feature but as you've 
mentioned changing the signature of Model.save() is comple.

The only viable option I can think of is to change the accepted type for 
force_insert from bool to Type[Model] | Tuple[Type[Model]] to allow 
save_parent to determine which one should be forced inserted and which 
shouldn't.

In the case of the reported usage case

```
class ParentModel(models.Model):
    id = models.BigIntegerField(primary_key=True)

class ChildModel(ParentModel): pass
```

That would mean that force_insert=ParentModel must be passed to opt-in into 
the full insertion mode (both issubclass(ChildModel, ParentModel) and 
issubclass(ParentModel, ParentModel))

```
ChildModel(id=1).save(force_insert=ParentModel)
```

That seems  like a viable option to me because

- An empty tuple is falsy which is coherent with force_insert=False
- A non-empty tuple is truthy and which is coherent with the fact the 
current leaf model must be inserted (current behavior)
- Complex MTI scenario forced insertion needs can be targeted with 
specially crafted base tuples (think of diamond inheritance)
- If a deep ancestor must be inserted then it means that all its children 
must also be inserted (makes sense due to how children must reference to 
parent)
- force_insert=models.Model can be used as a generic way to specify that 
all bases must be inserted independently of the chain of models involved

Hope that makes sense!

Cheers,
Simon

Le jeudi 18 mai 2023 à 22:46:35 UTC-4, Akash Sen a écrit :

> *[*Looking for an alternate approach*]*
> *Ticket link : *https://code.djangoproject.com/ticket/30382
>
> *Problem : *When saving we pass force_insert=True to prevent the extra 
> UPDATE statement that precedes the INSERT. The force_insert flag is 
> respected on the child table but not on the parent.
>
> *The main problem : *On our first go this issue would seem an easy 
> picking as we just have to pass the force_insert flag while saving the 
> parents, but as mentioned here 
> <https://code.djangoproject.com/ticket/30382#comment:2> it is going to 
> create a disaster of integrity errors in the Model.objects.create() method 
> with non-abstract inherited parents as the create method assumes 
> force_insert=True.
>
> *Approach 1: *Set the force_insert=False inside create method if that 
> contains a non-abstract parent model. 
>
> The problem with it is it generates an extra query while creating the non 
> abstract child model when the parent exists, more details can be found 
> here... 
> <https://github.com/django/django/pull/16830#issuecomment-1540182568> (Tried 
> in the PR)
>
> *Approach 2: *Pass an extra flag to the save method and if the method 
> call is coming from create then prevent passing force_insert flag(i.e set 
> it to false) while saving parents when called via create, but as we cannot 
> change the signature of the save method we cannot simply take an extra 
> flag, so this comes down to a very simple question : 
>
> *How to pass a flag to a python method without changing its signature:*
> I can think of three approaches for this,
> 1. Inspecting a stack frame(Deprecated because of performance issues) - 
> Tried in the PR
> 2. Using a global variable (Deprecated because of performance issues)
> 3. Using a class variable (Deprecated because of backward compatibility as 
> names can collide with method, variable, property name of a model) - Tried 
> in the PR
>
> *PR link :* https://github.com/django/django/pull/16830
>
> If someone can suggest an alternate approach that would be great!
>

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers  (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-developers+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-developers/09ed76ee-6db9-4687-a18e-8416221f329bn%40googlegroups.com.

Reply via email to