Reputation: 1086
I Have a model AB that holds two foreign keys A_id and B_id.
class AB(models.Model):
A_id = models.ForeignKey('A')
B_id = models.ForeignKey('B')
field_1 = models.CharField(max_length=200, blank=True)
field_2 = models.CharField(max_length=200, blank=True)
When editing A or B, AB items are edited inlines, what I want to achieve is that when editing let's say B I want to keep the selected AB items and set the foreign key B_id to null instead of deleting them.
thanks for any hint
Upvotes: 2
Views: 1473
Reputation: 1297
You can use a custom inline form set and override the delete_existing method which is available in django 1.11+.
from django.forms.models import BaseInlineFormSet
from django.db import models
from django.contrib import admin
class Publisher(models.Model):
pass
class Book(models.Model):
publisher = models.ForeignKey(Publisher, null=True)
class CustomInlineFormSet(BaseInlineFormSet):
def delete_existing(self, obj, commit=True):
"""Unhook a model instead of deleting it."""
if commit:
obj.publisher = None
obj.save()
class BooktInline(admin.TabularInline):
formset = CustomInlineFormSet
This changes it so that the 'delete' action on admin inline formsets will unhook the inline model instead of deleting it.
Upvotes: 2
Reputation: 968
I wound up here because I had the same question. I think the previous answer misses the issue here -- the use case here is the user checks the "delete" checkbox on an InlineModelAdmin
, not that they delete the model linked by the foreign key.
I think you can simplify the original problem, consider just that model B has a nullable foreign key to model A:
class A(models.Model):
pass
class B(models.Model):
linked_a = models.ForeignKey(A, null=True)
Then the admin lists each B linked to an A using an inline:
class BInline(TabularInline):
model = B
class AModelAdmin(ModelAdmin):
inlines = [BInline]
The question is, is there a way to make the "delete" checkbox on BInline
result in B.linked_a = None
rather than deleting the instance of B?
The reason this seems like a logical operation is that if you used a ManyToManyField
to join these two objects, that's what would happen -- it wouldn't delete B, it would just "unlink" it.
Unfortunately, the answer as far as I can tell is that you can't do this easily. In both cases the inline is showing a database row, but while the inline for a ForeignKey
is showing the related object itself, the inline for a ManyToManyField
is showing a row from the join table (eg. the relationship). So in terms of database operations the "delete" action is the same, it's just that in one case you delete the related object, in the other case you just delete the relationship.
Upvotes: 3
Reputation: 3536
If I understand this correctly, what you want is protection against cascade deletion. If this is the case, you need to specify what django should do on deletion of an A or B model. From the docs:
When an object referenced by a ForeignKey is deleted, Django by default emulates the behavior of the SQL constraint ON DELETE CASCADE and also deletes the object containing the ForeignKey. This behavior can be overridden by specifying the on_delete argument. For example, if you have a nullable ForeignKey and you want it to be set null when the referenced object is deleted:
In order to set the ForeignKey null, you can do it like this:
A_id = models.ForeignKey('A', null=True, on_delete=models.SET_NULL)
B_id = models.ForeignKey('B', null=True, on_delete=models.SET_NULL)
Good luck and hope this helps.
Upvotes: 1