I have the following defined in my model:
class TaskLink(models.Model):
task = model.ForeignKey(Task)
link_choices = (
models.Q(app_label="accounts", model="location"),
# Other models are also linked to here.
)
linked_content_type = \
models.ForeignKey(
ContentType,
limit_choices_to=link_choices
)
linked_object_id = models.PositiveIntegerField()
linked_object = \
generic.GenericForeignKey(
'linked_object_content_type',
'linked_object_id'
)
This model links Task
objects with any of the models in the link_choices
tuple. In this case, the accounts.Location
model is in this list.
My problem comes when the deletion of a Location
object results in the cascade-deletion of related TaskLink
objects. Deletion fails with the following error message:
django.core.exceptions.FieldError: Cannot resolve keyword 'object_id' into field. Choices are: id, linked_object, linked_object_content_type, linked_object_content_type_id, linked_object_id, task, task_id
The view is an instance of django.views.generic.DeleteView
with only the pk_url_kwarg
parameter and model set (and permissions decorators added to the dispatch method); it worked linked_object_fine before I added the TaskLink
model to the mix.
What am I missing?
EDIT: It seems this may be a bug in Django; when cascade-deleting objects through generic foreign keys, Django ignores the field name strings you pass to the constructor for the GenericForeignKey
field and looks instead for the content_type
and object_id
fields, which, in my case, didn't exist. This effectively limits the number of generic foreign keys a model may have to 1 unless you won't be running into cascade deletion.
I have sent this issue through the Django mailing list as that behavior may be intentional.
rename field names of TaskLink
linked_content_type >>> content_type
linked_object_id >>> object_id
or write pre signal while deleting "Location" object to delete linked object "TaskLink"
from django.db.models.signals import pre_delete
from django.dispatch import receiver
@receiver(pre_delete, sender=Location, dispatch_uid='location_delete_signal')
def deleted_gfk_TaskLink(sender, instance, using, **kwargs):
ctype = ContentType.objects.get_for_model(sender)
obj = TaskLink.objects.get(linked_content_type=ctype, linked_object_id=instance.id)
obj.delete()
reference for custom signals:
https://micropyramid.com/blog/using-djangos-built-in-signals-and-writing-custom-signals/