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

Custom buttons and views for objects in ModelAdmin #25

Open
donhauser opened this issue Jul 15, 2021 · 0 comments
Open

Custom buttons and views for objects in ModelAdmin #25

donhauser opened this issue Jul 15, 2021 · 0 comments
Labels
type:Enhancement New feature or request

Comments

@donhauser
Copy link

Is your proposal related to a problem?

Example:
Imagine you have a model like "Organisation" consisting of attributes like name, employees and so on and you want to create multiple views for it (through the admin panel), e.g. a "list of employees", "organisation overview".
Now, since you are using ModelAdmin, you would like to have a "employee list" and "overview" button beneath each organisation object in the index view, so you can easily access both object-views.

Note: You could use inspect_view for one action, but not for multiple actions.

Currently it is quite tricky implement new action-buttons with separate views because:

  • Adding further buttons is not described directly, the documentation only hints at ButtonHelper
  • Registering new model action-urls is undocumented
  • Far too many steps needed:
    • Create a super class of ButtonHelper
    • Add your button generating function to button ButtonHelper (like delete_button())
    • Overload ButtonHelper.get_buttons_for_obj() and append your button to the existing ones
    • Create a custom view function in your ModelAdmin class (like delete_view())
    • Overload ModelAdmin.get_admin_urls_for_registration() and append your custom view with the action url
    • specify button_helper_class=.. in your ModelAdmin

This problem is also discussed in #7 and #10

Describe the solution you'd like

In contrast to #7 and #10, I am proposing a very minimal change in ButtonHelper and ModelAdmin only.
The goal is to simplify the steps above without breaking any backwards compatibility in Wagtail, so the feature is lightweight but effective:

  • By removing the need to overload the two method above, a lot of code is saved and the complexity is reduced by a lot
  • The button generating functions (add_button(), edit_button(), ..) are all almost the same and can be defaulted for custom buttons
  • Simple custom buttons should be definable in ButtonHelper with a simple data structure (e.g. a list containing the action, label, title, ..)
  • The views (with their actions) should be definable in ModelAdmin using a simpler structure as well

A scratch code can be seen below.

Describe alternatives you've considered

One alternative would be to rewrite ButtonHelper as mentioned in #10.
By changing ButtonHelper heavily, old Wagtail-Projects might be broken, so this is rather a long term goal.

Additional context

Changes to Wagtail:

# Changes to ButtonHelper
class ButtonHelper:
    
    # Default button generator for custom/extra buttons (structured like add_button)
    def extra_button(self, url=None, label='', title=None, classnames_add=None, classnames_exclude=None):
        if classnames_add is None:
            classnames_add = []
        if classnames_exclude is None:
            classnames_exclude = []

        cn = self.finalise_classname(classnames_add, classnames_exclude)
        
        if not title:
            title = label
        
        return {
            'url': url,
            'label': label,
            'classname': cn,
            'title': title,
        }
    
    def get_buttons_for_obj(self, obj, exclude=None, classnames_add=None,
                            classnames_exclude=None):
        
        # OLD CODE HERE
       
        # New code

        # Check if the custom data structure is present
        if hasattr(self, "custom_object_buttons"):
             button_list = self.custom_object_buttons

            for action, kw in button_list:
            
                # TODO unite kw['classnames_add'] and classnames_add
                # TODO unite kw['classnames_exclude'] and classnames_exclude

                # create and append the button using the regular url pattern
                button = self.extra_button(self.url_helper.get_action_url(action, pk), **kw)
                btns.append(button)
        
        return btns

class ModelAdmin:

    def get_admin_urls_for_registration(self):        
        
        # OLD CODE HERE
       
        # New code
        # create url pattern for each custom view, just like for "add", "edit", ...
        urls = urls + tuple(
            re_path(
                self.url_helper.get_action_url_pattern(action),
                view,
                name=self.url_helper.get_action_url_name(action))
            for action, view in self.get_custom_object_views()
        )
        
        return urls

    # fallback method
    def get_custom_object_views(self):
        return []

Adding Buttons would now be much easier:

class MyButtonHelper(ButtonHelper):
    
    # custom definitions
    # (action, attributes)
    custom_object_buttons = [
        ("empolyees", {"label": 'Employee List', "add_class":["some_class"]}),
        ("overview", {"label": 'Overview', "title": 'Show a detailed overview table'}),
        ..
    ]


class MyModelWagtailAdmin(ModelAdmin):

    button_helper_class = MyButtonHelper

    def empolyees_view(self, request, instance_pk):
        # some call to View.as_view(..)

    def overview_view(self, request, instance_pk):
        # some call to OtherView.as_view(..)

    # Define custom object views
    #   This is a function because self is needed
    #   It would be even nicer to have only a list of actions
    #   and to automatically return self.{action}_view
    def get_custom_object_views(self):
        return [
            # (action, view)
            ("empolyees", self.empolyees_view),
            ("overview", self.overview_view),
        ]

If you like this suggestion, I will create a pull request for this feature

@donhauser donhauser added the type:Enhancement New feature or request label Jul 15, 2021
@laymonage laymonage transferred this issue from wagtail/wagtail Jul 26, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type:Enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant