Skip to content

modeladmin PROTECTED with related_name="+" #30

@m3brown

Description

@m3brown

Issue Summary

This is a bug related to wagtail/wagtail#5437, which added exception handling for deletion of objects that throw a ProtectedError.

I observed that the existing code does not work if the ForeignKey/OneToOneField relationship uses related_name="+".

AttributeError at /admin/videos/video/delete/18/
'Video' object has no attribute '+'
...
Exception Location: /wagtail/contrib/modeladmin/views.py in post, line 814

The code at line 814 is qs = getattr(self.instance, rel.get_accessor_name()), and it makes sense that the code fails when rel.get_accessor_name() == "+".

Steps to Reproduce

  1. Start a new project with wagtail start myproject
  2. Create a videos app with a Video model (code sample below)
  3. Create a VideoPage model (code sample below)
  4. Register the model admin (code sample below)
  5. Create a couple sample videos and video pages
  6. Select a video in the admin interface that is associated with a page, and delete the video
  7. Confirm the AttributeError occurs
# models.py
class Video(models.Model):
    title = models.CharField(max_length=255)
    
    panels = [
        FieldPanel("title"),
    ]

class VideoPage(Page):
    video = models.ForeignKey(
        "videos.Video",
        on_delete=models.PROTECT,
        related_name="+",
    )

    content_panels = Page.content_panels + [
        FieldPanel("video", widget=VideoChooser),
    ]
# wagtail_hooks.py
from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register

class VideoModelAdmin(ModelAdmin):
    @cached_property
    def model(self):
        from videos.models import Video

        return Video

modeladmin_register(VideoModelAdmin)

Any other relevant information. For example, why do you consider this a bug and what did you expect to happen instead?

The workarounds for this scenario I'm aware of are:

  • Don't use PROTECT
  • Don't use related_name="+"
    • This is appealing, but becomes a nuisance when using inheritance, because the related_name needs to be unique. The solution I found was to inject the class name into the related_name: related_name="%(app_label)s_%(class)s_related_pages",
  • I have confirmed that this issue can be reproduced as described on a fresh Wagtail project: no

Technical details

  • Python version: 3.6
  • Django version: 2.2.17
  • Wagtail version: 2.11.3
  • Browser version: Edge 87

Metadata

Metadata

Assignees

No one assigned

    Labels

    type:BugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions