Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document/Images Usage page: 'ManyToOneRel' object has no attribute 'verbose_name’ #11482

Open
terrsoshi opened this issue Jan 19, 2024 · 3 comments
Labels
status:Unconfirmed Issue, usually a bug, that has not yet been validated as a confirmed problem. type:Bug

Comments

@terrsoshi
Copy link

terrsoshi commented Jan 19, 2024

Issue Summary

Users cannot view the usage details of documents or images (where they’re being used) used at least once in any multiple Parentalkey levels models from the Wagtail admin site.

When images/documents are used in certain parts of the website, and users try to get the usage information on where those images/documents are being used at, the following error occurs at trying to get the verbose_name from the right child field (determined through the reference index):

AttributeError at /admin/documents/usage/1/
'ManyToOneRel' object has no attribute 'verbose_name

This seems to be a problem in referencing the verbose_name of the correct child field, likely stemming from the multiple ParentalKey level nested structure of the ClusterableModel/Orderable models in the example (Resource → ResourceGroup → ResourceItem, etc.).

Affected Code:

if isinstance(field, models.ManyToOneRel):
child_field = field.related_model._meta.get_field(model_path_components[2])
return capfirst(child_field.verbose_name)

Current Expected: ManyToOneRel → Child Field with verbose_name (Image/Document)
Actual: ManyToOneRelManyToOneRel → Child Field with verbose_name (Image/Document)

Ultimately, we might be looking at a recursive fix for multi-level ManyToOneRel fields.

Video demonstrating the issue (Steps 6 to 10 in Steps to Reproduce below):
https://www.loom.com/share/8dade8f9e25e44c38b4a863c870d8098?sid=bf96dd4a-0b96-4413-8e89-47e201b88808

Steps to Reproduce

  1. Start a fresh Wagtail project following the tutorial at https://docs.wagtail.org/en/stable/getting_started/tutorial.html up until the Start the server step.

  2. Replace the contents of mysite/home/models.py with the following code:

    from django.db import models
    
    from wagtail.models import Orderable
    from wagtail.documents import get_document_model_string
    from wagtail.admin.panels import FieldPanel, InlinePanel
    
    from modelcluster.fields import ParentalKey
    from modelcluster.models import ClusterableModel
    
    
    class ResourceItem(Orderable):
        resource_group = ParentalKey(
            "home.ResourceGroup", related_name="item"
        )
    
        document = models.ForeignKey(
            get_document_model_string(),
            null=True,
            on_delete=models.SET_NULL,
        )
    
        panels = [
            FieldPanel("document"),
        ]
    
    
    class ResourceGroup(ClusterableModel, models.Model):
        resource = ParentalKey("home.Resource", related_name="group")
    
        panels = [
            InlinePanel("item", label="Resource")
        ]
    
    
    class Resource(ClusterableModel, models.Model):
        pass
  3. Create a wagtail_hooks.py file at the mysite/home directory with the following contents:

    from wagtail.admin.panels import InlinePanel
    from wagtail.snippets.models import register_snippet
    from wagtail.snippets.views.snippets import SnippetViewSet
    
    from .models import Resource
    
    
    class ResourceViewSet(SnippetViewSet):
        model = Resource
        panels = [
            InlinePanel("group", label=" Resource Group")
        ]
    
    
    register_snippet(ResourceViewSet)
  4. On your terminal, run python manage.py makemigrations then python manage.py migrate from the project root directory (eg. ../mysite)

  5. Browse and login to the Wagtail Admin site at localhost:8000/admin with your admin account.

  6. Using the sidebar, navigate to SnippetsResources.

  7. Perform the following: Add resourceAdd resource groupAdd resource (Under Resource Group 1) → Choose a document (Upload any file) → Save

  8. Once the new Resource object is created, using the sidebar, navigate to Documents and select the document you just uploaded.

  9. On the right/bottom of the page, under Usage, select Used 1 time

  10. The following error should occur:

    AttributeError at /admin/documents/usage/1/
    
    'ManyToOneRel' object has no attribute 'verbose_name'

Expected Outcome:

No errors occur, and the usage page of the selected document can be viewed, with the correct verbose_name of field references listed under the Field column. e.g. Resource group → Resource item → Document

  • I have confirmed that this issue can be reproduced as described on a fresh Wagtail project: Yes

Technical details

  • Python version: 3.8.14
  • Django version: 3.2.23
  • Wagtail version: 5.2.2
  • Browser version: Screenshots are on Firefox, but this should occur on any browsers.

Working on this

I'm keen to work on this issue once it has been accepted.

@terrsoshi terrsoshi added status:Unconfirmed Issue, usually a bug, that has not yet been validated as a confirmed problem. type:Bug labels Jan 19, 2024
@terrsoshi
Copy link
Author

After working on the issue for some time, I've had some luck with a potential recursive fix:

if isinstance(field, models.ManyToOneRel):
    label = f"{capfirst(field.related_model._meta.verbose_name)}"
    idx = 2
    child_field = field.related_model._meta.get_field(model_path_components[idx])
    while isinstance(child_field, models.ManyToOneRel):
        label += f" → {capfirst(child_field.related_model._meta.verbose_name)}"
        idx += 2
        child_field = child_field.related_model._meta.get_field(model_path_components[idx])
    label += f" → {capfirst(child_field.verbose_name)}"
    return label

This fix should also fix an initial issue where the first field level's verbose_name is excluded from the returned string.

For example, for a field reference of ResourceItemDocument within a ResourceGroup object:
Current result: Document
This fix's result: Resource item → Document

While this potential fix addresses the reported issue in this case, more testing is required for the other cases. I welcome any feedback or insights from the community to ensure the solution's robustness.

@Ross-Clark
Copy link

This solution works perfectly for the problem I have been having.
Would you be comfortable opening a PR?

@terrsoshi
Copy link
Author

I have created a PR for this issue at #11808.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status:Unconfirmed Issue, usually a bug, that has not yet been validated as a confirmed problem. type:Bug
Projects
None yet
Development

No branches or pull requests

2 participants