Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions django_adminlink/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ class LinkFieldAdminMixin:
admin_url_namespace = "admin"

def _convert_list_display_item(self, field_name):
"""
Converts a list display field name to a callable that renders a link for ForeignKey fields.

If the specified field is a ForeignKey, returns a callable that displays the related object as a clickable link to its admin change page. Otherwise, returns the original field name.
"""
if isinstance(field_name, str):
try:
field = self.model._meta.get_field(field_name)
Expand All @@ -30,6 +35,11 @@ def get_list_display(self, request):
return result

def _link_to_model_field(self, field):
"""
Returns a callable that renders a ForeignKey field as a clickable link to the related object's admin change page.

If the related model is not registered with the admin site, returns the field name instead.
"""
related_model = field.related_model
admin_site = self.admin_site_to_link or admin.site
model_admin = admin_site._registry.get(related_model)
Expand All @@ -41,6 +51,15 @@ def _link_to_model_field(self, field):

@admin.display(description=field.name, ordering=f"{field.name}")
def column_render(obj):
"""
Renders a foreign key field as a clickable link to the related object's admin change page.

Args:
obj: The model instance containing the foreign key field.

Returns:
An HTML anchor element linking to the related object's admin page, or None if the field is not set.
"""
key = getattr(obj, field.name)
if key is not None:
return format_html(
Expand All @@ -65,6 +84,12 @@ class SingleItemActionMixin:

@admin.display(description="actions")
def action_button_column(self, obj):
"""
Renders action buttons for each object in the admin list display.

Each button is configured with data attributes for the action name and object primary key,
and triggers the `get_checkboxes` JavaScript function when clicked.
"""
if isinstance(self.action_buttons, dict):
action_buttons = self.action_buttons.items()
else:
Expand All @@ -76,6 +101,12 @@ def action_button_column(self, obj):
)

def get_list_display(self, request):
"""
Extends the list display to include a column of action buttons if any are defined.

If the `action_buttons` attribute is set, appends the `action_button_column` to the list
display; otherwise, returns the default list display.
"""
items = super().get_list_display(request)
if self.action_buttons:
return [*items, self.action_button_column]
Expand All @@ -85,6 +116,12 @@ def get_list_display(self, request):

@property
def media(self):
"""
Extends the admin media to include JavaScript for single-item action buttons.

Returns:
The combined media object with the additional JavaScript file included.
"""
return super().media + Media(js=["js/single_admin_action.js"])


Expand Down
7 changes: 7 additions & 0 deletions django_adminlink/static/js/single_admin_action.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
/**
* Selects a single item in a Django admin list and triggers the specified admin action for that item.
*
* Retrieves the primary key and action from the provided element's data attributes, updates the action selector, unchecks all other items, checks the targeted item, and submits the form to perform the action on that item only.
*
* @param {Element} e - The element containing `data-pk` and `data-action` attributes for the target item and action.
*/
function get_checkboxes(e) {
const pk = e.getAttribute('data-pk');
const action = e.getAttribute('data-action');
Expand Down
Loading