On Friday, September 27, 2013 1:29:29 AM UTC+3, Xof wrote:
>
>
> On Sep 26, 2013, at 3:28 PM, Christophe Pettus 
> <x...@thebuild.com<javascript:>> 
> wrote: 
> > Perhaps a CASCADE_DB and SET_NULL_DB options on on_delete? 
>
> And, to be clear, I *am* volunteering to take a go at this code, not just 
> whine. :) 
>

Some things to consider:
  1. What to do if given DB doesn't support cascades in DB (sqlite at 
least, no idea of MySQL)? Initial feeling is that Django should do the 
cascades in Python code in these cases.
  2. What to do if you have delete signals + db cascades set for given 
model? Options are to do nothing at all, give a warning (manage.py check 
might be able to do so) or raise an error in model validation.
  3. A model definition like A -- db cascade -> B -- cascade in python -> C 
is another problematic case. a_obj.delete() will cascade to B, but then 
that deletion will fail because of C constraint not cascading. Again 
possibilities are do nothing/warn/error
  4. A slight variation of above - generic foreign key cascades - here it 
will be impossible to handle the cascades in DB (unless we want to write 
custom triggers for this). And, the inconsistent state left behind will not 
be spotted by the DB either as there aren't any constraints in the DB for 
generic foreign keys. So, this is slightly worse than #3.
  5. Parent cascades: If you have model Child(Parent), then there will be 
foreign key from child to parent, but not from parent to child. This means 
that DB can't cascade child model deletion to the parent model. So, there 
is again possibility for inconsistent state. So, if you have Child -- db 
cascade -> SomeModel, and you delete somemodel instance then what to do to 
get the Child's parent table data deleted?

Numbers #4 and #5 seem hardest. Especially the parent cascade case seems 
hard, for generic foreign keys just documenting "don't do that" seems good 
enough.


For reference, the problematic data model is this:

class Parent(Model):
    id = AutoField()

class SomeModel(Model):
    pass

class Child(Parent):
    parent_ptr = models.OneToOneField(Parent, on_delete=DB_CASCADE, 
parent_link=True)
    somemodel = models.ForeignKey(SomeModel, on_delete=DB_CASCADE)

It generates schema like this:

create table parent(
    id serial primary key
);
create table child(
    parent_ptr_id integer primary key references parent(id) on delete 
cascade,
    somemodel_id integer not null references somemodel(id) on delete cascade
);

create table somemodel(
    id serial primary key
);

Now, parent data deletion will correctly cascade to child in the DB, but 
child data deletion will not cascade to parent. Django's interpretation is 
that when you delete a child instance, you delete also all parent data at 
the same time. When somemodel delete cascades to child in the DB then 
associated parent data will not be deleted. Adding nullable foreign key 
from parent to child might work, that is add a SymmetricOneToOneField into 
Django. Data model would be:

create table parent(
    id serial primary key,
    child_id integer references child(parent_ptr_id) on delete cascade -- 
this of course also needs deferrable initially deferred qualifier to 
work... 
);
create table child(
    parent_ptr_id integer primary key references parent(id) on delete 
cascade,
    ...
);

 - Anssi

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-developers+unsubscr...@googlegroups.com.
To post to this group, send email to django-developers@googlegroups.com.
Visit this group at http://groups.google.com/group/django-developers.
For more options, visit https://groups.google.com/groups/opt_out.

Reply via email to