2012-02-24 2 views
4

У меня есть следующие модели (я ушел из def __unicode__(...) для ясности):Django: Как удалить объект дочернего класса без удаления объекта родительского класса?

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) 

У меня есть следующий метод просмотра для использования объекта MinorResident создать OtherPerson объект, как:

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 

Этом все отлично работает, но у меня все еще есть запись в таблице миноризаций, указывающая на запись человека с person_ptr_id. У меня также есть запись указателя в таблице otherperson с тем же person_ptr_id, указывающим на одного и того же человека, и отображением всех данных, как это было до коммутатора, но с объектом OtherPerson вместо объекта MinorResident. Итак, я хочу удалить объект MinorResident, не удаляя объект Person родительского класса. Я полагаю, что я могу сделать что-то вроде:

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

Но я хотел бы, чтобы не иметь новую запись в таблице Person, если я могу помочь ему, так как он на самом деле не новый человек, просто человек, которого взрослый сейчас. Может, я смогу сделать что-то подобное? Или есть лучший способ справиться с модельной трансмутацией?

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 

Я смотрел на SO #3711191: django-deleting-object-keeping-parent, но я надеялся на улучшение ответа.

ответ

4

Вариант 1

Явные указать ваши parent_link полей и использовать неуправляемую модель.

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 

Теперь вы можете вызвать UnmanagedMinorResident.delete() без удаления родительской строки.

Вариант № 2

Используйте необработанный SQL запрос

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) 

Но вы, вероятно, следует изменить модель данных и использовать ту же таблицу, как для взрослых и детей. Свойства, которые вы храните в модели MinorResident, там не принадлежат, они относятся к взаимосвязи между MinorResident и объектом, из которого он перемещается/из.

+0

Жаль, что я видел это еще в марте 2012. Я в конечном итоге решил это так, как вы описали, объединив несовершеннолетних и взрослых как объекты-резиденты, с указанием даты рождения, чтобы определить взрослую жизнь. И да, я переключился так, что движение и выход - часть отношений, а не человека. – Furbeenator

+2

Как и в Django 1.9, ответ прост, как Foo.delete (keep_parents = True) – kert

+2

Обратите внимание, что delete (keep_parents = True) будет досадно удалять из моделей с внешними ключами в родительскую модель. – MrDBA

5

На Django 1.10.4+ вы можете использовать keep_parents вариант:

minor.delete(keep_parents=True) 

Else я предлагаю вам использовать deletion.Collector с ручным сбором:

from django.db.models import deletion 

collector = deletion.Collector(using=minor._state.db) 
collector.add([minor]) 
collector.delete() 
+0

Это ведет к сохранению связанных записей в родительской модели без изменений, избегает неуправляемых таблиц и позволяет избежать необработанного sql. Ура! – MrDBA

+0

Это возможно даже в Django 1.9 –

+1

@AmrullahZunzunia опция 'keep_parents' нарушена в Django 1.9. См. [Этот билет] (https://code.djangoproject.com/ticket/27407). –

Смежные вопросы