Skip to content

Commit

Permalink
add link for foreign key in display field
Browse files Browse the repository at this point in the history
  • Loading branch information
cuongnb14 committed Oct 10, 2023
1 parent f7ccf1d commit 25e7379
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 44 deletions.
2 changes: 2 additions & 0 deletions admin_extended/base/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .custom_table_admin_page import TableData, CustomTableAdminPage
from .extended_admin_model import ExtendedAdminModel
39 changes: 39 additions & 0 deletions admin_extended/base/custom_table_admin_page.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from dataclasses import dataclass

from django.contrib import admin
from django.shortcuts import render
from django.urls import path


@dataclass
class TableData:
header: str
table_titles = []
table_rows = []

def add_rows(self, row: list):
self.table_rows.append(row)


class CustomTableAdminPage(admin.ModelAdmin):
model = None

def get_urls(self):
view_name = '{}_{}_changelist'.format(self.model._meta.app_label, self.model._meta.model_name)
return [
path('', self.custom_view, name=view_name),
]

def get_table_data(self):
"""
return list of TableData
"""
raise NotImplementedError()

def custom_view(self, request, *args, **kwargs):
context = {
**admin.site.each_context(request),
'tables': self.get_table_data(),
}

return render(request, 'admin/custom/custom_table_page.html', context)
Original file line number Diff line number Diff line change
@@ -1,29 +1,13 @@
import copy
from dataclasses import dataclass

from django.contrib import messages
from django.contrib import admin
from django.shortcuts import render
from django.urls import path
from .mixins import UIUtilsMixin, ObjectToolModelAdminMixin
from .settings import ADMIN_EXTENDED_SETTINGS
from .mixins import UIUtilsMixin, ObjectToolModelAdminMixin, DispayLinkAdapter
from .utils import has_search_fields
from ..settings import ADMIN_EXTENDED_SETTINGS


def has_search_fields(field):
model_admin = admin.site._registry.get(field.related_model)
return model_admin and model_admin.search_fields

@dataclass
class TableData:
header: str
table_titles = []
table_rows = []

def add_rows(self, row: list):
self.table_rows.append(row)


class ExtendedAdminModel(ObjectToolModelAdminMixin, UIUtilsMixin, admin.ModelAdmin):
class ExtendedAdminModel(ObjectToolModelAdminMixin, UIUtilsMixin, DispayLinkAdapter, admin.ModelAdmin):
"""
Extend base model admin: tabbable inline model, separate view, edit model,...
Expand Down Expand Up @@ -147,26 +131,3 @@ def get_inline_instances(self, request, obj=None):
request.is_tabbed = self.tab_inline
return super().get_inline_instances(request, obj)


class CustomTableAdminPage(admin.ModelAdmin):
model = None

def get_urls(self):
view_name = '{}_{}_changelist'.format(self.model._meta.app_label, self.model._meta.model_name)
return [
path('', self.custom_view, name=view_name),
]

def get_table_data(self):
"""
return list of TableData
"""
raise NotImplementedError()

def custom_view(self, request, *args, **kwargs):
context = {
**admin.site.each_context(request),
'tables': self.get_table_data(),
}

return render(request, 'admin/custom/custom_table_page.html', context)
52 changes: 51 additions & 1 deletion admin_extended/mixins.py → admin_extended/base/mixins.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import json
from django.utils.html import format_html
from django.shortcuts import redirect
from django.urls import path, reverse


Expand Down Expand Up @@ -122,3 +121,54 @@ def changelist_view(self, request, extra_context=None):
extra_context['change_list_object_tools'] = self._get_render_change_list_object_tools(request)

return super().changelist_view(request, extra_context)


class DispayLinkAdapter:

def _foreign_key_link(self, field_name, description):
"""
Converts a foreign key value into clickable links.
If field_name is 'parent', link text will be str(obj.parent)
Link will be admin url for the admin url for obj.parent.id:change
"""

def _display_fn(obj):
linked_obj = getattr(obj, field_name)
if linked_obj is None:
return '-'
app_label = linked_obj._meta.app_label
model_name = linked_obj._meta.model_name
view_name = f'admin:{app_label}_{model_name}_change'
link_url = reverse(view_name, args=[linked_obj.pk])
return format_html('<a href="{}">{}</a>', link_url, linked_obj)

_display_fn.short_description = description
return _display_fn


def convert_display_fields(self, list_display):
field_mapping = {}
for field in self.model._meta.fields:

field_mapping[field.attname] = {
'class_name': field.__class__.__name__,
'verbose_name': field.verbose_name,
}

if field.__class__.__name__ == 'ForeignKey':
field_mapping[field.attname[:-3]] = field_mapping[field.attname] # Eg `user_id` -> `user` key have same info

results = [list_display[0]]
for field_name in list_display[1:]: # ignore first field
field_info = field_mapping.get(field_name)
if field_info and field_info['class_name'] == 'ForeignKey':
results.append(self._foreign_key_link(field_name, field_info['verbose_name']))
else:
results.append(field_name)

return results

def get_list_display(self, request):
list_display = super().get_list_display(request)
return self.convert_display_fields(list_display)
5 changes: 5 additions & 0 deletions admin_extended/base/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from django.contrib import admin

def has_search_fields(field):
model_admin = admin.site._registry.get(field.related_model)
return model_admin and model_admin.search_fields

0 comments on commit 25e7379

Please sign in to comment.