reK1NDLE
reK1NDLE

Reputation: 115

Deleting SQL data in associated OneToOneField model from admin

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

Answers (2)

reK1NDLE
reK1NDLE

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

Priyeshj
Priyeshj

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

Related Questions