Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

django ManyToManyField and on_delete

ForeignKeys on django have the attribute on_delete to specify the behavior when the referenced object is deleted. Is there any way to get something similar for ManyToManyField?

Suppose I have the following model

class House(models.Model):     owners = models.ManyToManyField(Person) 

The default behavior is to cascade, so if I delete a person that happens to own a house, it just vanishes from owners (that is, obviously, it no longer owns any houses). What I'd like to have is that if a person is an owner, it can not be deleted. That is, I want on_delete=models.PROTECT. Is this possible?

I know internally ManyToManyField is translated to another model with two ForeignKeys (in this case one to house and one to person), so it should be possible to achieve this. Any ideas how to? I'd like to avoid setting the through attribute to a new model, because this would result in a new table (I'd like to keep the old one).

Edit: I've tracked where django creates the appropriate m2m model:

def create_many_to_many_intermediary_model(field, klass):     from django.db import models     # ...      # Construct and return the new class.     return type(name, (models.Model,), {         'Meta': meta,         '__module__': klass.__module__,         from_: models.ForeignKey(klass,                                  related_name='%s+' % name,                                  db_tablespace=field.db_tablespace),         to: models.ForeignKey(to_model,                               related_name='%s+' % name,                               db_tablespace=field.db_tablespace)     }) 

The relevant line is

to: models.ForeignKey(to_model,                       related_name='%s+' % name,                       db_tablespace=field.db_tablespace) 

I'd like it to be

to: models.ForeignKey(to_model,                       related_name='%s+' % name,                       db_tablespace=field.db_tablespace,                       on_delete=models.PROTECT) 

Any way to do this other than monkey patching the whole thing and creating a new class for ManyToManyField?

like image 994
Clash Avatar asked Mar 08 '13 20:03

Clash


People also ask

What is ManyToManyField Django?

ManyToManyRel is used by the ManyToManyField to implement the relationship object for the Field base class which it extends.

What is On_delete in Django?

The on_delete method is used to tell Django what to do with model instances that depend on the model instance you delete. (e.g. a ForeignKey relationship). The on_delete=models. CASCADE tells Django to cascade the deleting effect i.e. continue deleting the dependent models as well.

How do I merge two Django models?

1 Answer. Show activity on this post. In your models Device and History models are related with a foreign key from History to DeviceModel, this mean when you have a History object you can retrieve the Device model related to it, and viceversa (if you have a Device you can get its History).

How do you implement many-to-many relationships in Django?

To define a many-to-many relationship, use ManyToManyField . What follows are examples of operations that can be performed using the Python API facilities. You can't associate it with a Publication until it's been saved: >>> a1.


1 Answers

I think the smartest thing to do is use an explicit through table. I realise that you've stated you would prefer not to "because this would result in a new table (I'd like to keep the old one)."

I suspect your concern is over losing the data you have. If you're using South, you can easily "convert" your existing, automatic intermediate table to an explicit one OR, you can create a completely new one, then migrate your existing data to the new table before dropping your old one.

Both of these methods are explained here: Adding a "through" table to django field and migrating with South?

Considering the change you'd like to make to its definition, I'd probably go with the option of creating a new table, then migrating your data over. Test to make sure all your data is still there (and that your change does what you want), then drop the old intermediate table.

Considering that these tables will both only hold 3 integers per row, this is likely to be a very manageable exercise even if you have a lot of houses and owners.

like image 151
mkoistinen Avatar answered Sep 28 '22 05:09

mkoistinen