Reputation: 115
I've got a model that references another model via OneToOneField
so that when you use Django's built-in delete_selected
admin action, the associated model's data is not deleted. I'd like to write a custom admin action to delete the data in that associated model as well.
Here's my model:
class Party(models.Model):
TYPE_CHOICES=(
('P','Person'),
('O','Organization')
)
partyType = models.CharField(max_length=1, choices=TYPE_CHOICES)
name = models.CharField(max_length=100)
comment = models.CharField(max_length=500,blank=True)
accessIdCrossRef=models.IntegerField(blank=True, null=True)
mailingLists = models.ManyToManyField(MailingList)
inMainList=models.BooleanField(default=False)
inSubList=models.BooleanField(default=False)
class Meta:
db_table='party'
ordering=['name',]
def __unicode__(self):
return self.name
class Person(models.Model):
party = models.OneToOneField(Party, editable=False)
firstName=models.CharField(max_length=60)
lastName=models.CharField(max_length=60)
...
def save(self):
if None == self.party :
print 'Creating party for person'
p = Party()
p.partyType = 'P'
p.save()
self.party = p
# Get address to set party name used in list
city=""
state=""
postalCode=""
try:
partyAddress = PartyPostalAddress.objects.get(party=self.party)
address = partyAddress.postalAddress
city=address.city
state=address.state
postalCode=address.postalCode
except PartyPostalAddress.DoesNotExist:
pass
self.party.name = '%s, %s - %s, %s %s' %(self.lastName, self.firstName, city, state, postalCode)
self.party.save()
super(Person,self).save()
My assumption was to write a def delete()
in my model like this:
def delete(self):
self.party.delete()
self.delete()
And an admin action like so:
class PersonAdmin(admin.ModelAdmin):
list_display = ('lastName','firstName')
search_fields = ('firstName', 'lastName')
actions=['really_delete_selected']
def get_actions(self, request):
actions = super(PersonAdmin, self).get_actions(request)
del actions['delete_selected']
return actions
def really_delete_selected(self, request, queryset):
for obj in queryset:
obj.delete()
if queryset.count() == 1:
message_bit = "1 person was"
else:
message_bit = "%s people were" % queryset.count()
self.message_user(request, "%s successfully deleted." % message_bit)
really_delete_selected.short_description = "Delete selected entries"
That deletes person.party and most of person, but throws an error because person's party OneToOneField is now empty. The specific error is:
"AssertionError at /admin/common/person/ Party object can't be deleted because its id attribute is set to None."
Any ideas? This, this, and this question are related, but only one of them utilizes the OneToOneField and he did that erroneously.
Upvotes: 1
Views: 300
Reputation: 115
Got it cleaned up and working!
My model:
class Party(models.Model):
name = models.CharField(max_length=100)
...
class Person(models.Model):
party = models.OneToOneField(Party, editable=False)
firstName=models.CharField(max_length=60)
lastName=models.CharField(max_length=60)
def delete(self):
d = self.party.id
Party.objects.get(id__exact=d).delete()
My admin:
class PersonAdmin(admin.ModelAdmin):
actions=['really_delete_selected']
def get_actions(self, request):
actions = super(PersonAdmin, self).get_actions(request)
del actions['delete_selected']
return actions
def really_delete_selected(self, request, queryset):
for obj in queryset:
obj.delete()
if queryset.count() == 1:
message_bit = "1 person was"
else:
message_bit = "%s people were" % queryset.count()
self.message_user(request, "%s successfully deleted." % message_bit)
really_delete_selected.short_description = "Delete selected entries"
...
Upvotes: 1
Reputation: 1345
I am getting a feeling that it should be as simple as switching the sequence of deleting the two (unless you have tried this already). Since the person is associated with party, once you delete person, you cannot access party. Hence you should do
person.party.delete()
person.delete()
Upvotes: 1