Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django: How do you delete child class object without deleting parent class object?

I have the following models (I left out def __unicode__(...) for clarity):

class Person(models.Model):
    first_name = models.CharField(max_length=64, null=True, blank=True)
    middle_name = models.CharField(max_length=32, null=True, blank=True)
    last_name = models.CharField(max_length=64, null=True, blank=True)

class MinorResident(Person):
    move_in_date = models.DateField(null=True)
    move_out_date = models.DateField(null=True)
    natural_child = models.NullBooleanField()

class OtherPerson(Person):
    associate_all_homes = models.BooleanField(default=False)

I have the following view method for using a MinorResident object to create an OtherPerson object, like:

def MinorToAdult(request, minor):
    p = Person.objects.get(id=minor.person_ptr_id)
    o = OtherPerson(p.id)
    o.__dict__.update(p.__dict__)
    o.save()
    return True

This all works great, but I still have a record in the minoresident table pointing to the person record with person_ptr_id. I also have a pointer record in the otherperson table with the same person_ptr_id pointing to the same person, and displaying all of the data as it was before the switch, but with an OtherPerson object instead of MinorResident object. So, I want to delete the MinorResident object, without deleting the parent class Person object. I suppose I can do something like:

p = Person.objects.get(id=minor.person_ptr_id)
o = OtherPerson()
o.__dict__.update(p.__dict__)
o.save()
minor.delete()
return True

But I would like to not have a new record in the Person table if I can help it, since it really isn't a new person, just a person whose an adult now. Maybe I can I do something like this? Or is there a better way to handle model transmutation?

p = Person.objects.get(id=minor.person_ptr_id)
o = OtherPerson(p.id)
o.__dict__.update(p.__dict__)
o.save()
minor.person_ptr_id = None
minor.delete()
return True

I looked at SO #3711191: django-deleting-object-keeping-parent, but I was hoping for an improved answer.

like image 770
Furbeenator Avatar asked Feb 24 '12 23:02

Furbeenator


2 Answers

On Django 1.10.4+ you can use the keep_parents option:

minor.delete(keep_parents=True)

Else I suggest you use deletion.Collector with manual collection:

from django.db.models import deletion

collector = deletion.Collector(using=minor._state.db)
collector.add([minor])
collector.delete()
like image 139
Simon Charette Avatar answered Nov 05 '22 11:11

Simon Charette


Option 1

Explicitly specify your parent_link fields and use an unmanaged model.

class MinorResident(Person):
    person = models.OneToOneField(
        Person,
        parent_link = True,
        primary_key = True,
        db_column = 'person_id'
    )
    move_in_date = models.DateField(null=True)
    move_out_date = models.DateField(null=True)
    natural_child = models.NullBooleanField()


class UnmanagedMinorResident(models.Model):
    person = models.OneToOneField(
        Person,
        primary_key = True,
        db_column = 'person_id'
    )
    move_in_date = models.DateField(null=True)
    move_out_date = models.DateField(null=True)
    natural_child = models.NullBooleanField()

    class Meta:
        managed = False
        db_table = MinorResident._meta.db_table

Now you can call UnmanagedMinorResident.delete() without deleting the parent row.

Option #2

Use a raw SQL query

from django.db import connection

minor = # MinorResident object
c = connection.cursor()
table = MinorResident._meta.db_table
column = MinorResident._meta.pk.column
# In this specific case it is safe to not escape.
sql = "DELETE FROM {0} WHERE {1}={2}".format(table, column, minor.pk)
c.execute(sql)

But you should probably change your data model and use the same table for both adults and minors. The properties you are storing in the MinorResident model do not belong there, they belong on the relationship between the MinorResident and the entity it is moving in/out from/to.

like image 30
Izz ad-Din Ruhulessin Avatar answered Nov 05 '22 10:11

Izz ad-Din Ruhulessin