Last active
September 26, 2022 04:42
-
-
Save pymen/7243e864cae2d66771b421530ab651b1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import operator | |
from django.contrib import admin, messages | |
from django.contrib.admin import ModelAdmin | |
from django.urls import reverse | |
from django.db import transaction | |
from django.db.models import Q, QuerySet | |
from django.forms import TextInput | |
from django.utils.html import format_html | |
class FKLinkWidget(TextInput): | |
"""Widget to show html link for FK field instead of default option field""" | |
NO_VALUE_TEXT = 'None' | |
def __init__(self, attrs=None): | |
self.app_label = None | |
self.model_name = None | |
self.pk = None | |
self.repr = None | |
super().__init__(attrs) | |
def set_obj(self, obj): | |
self.app_label = obj._meta.app_label | |
self.model_name = obj._meta.model_name | |
self.pk = obj.pk | |
self.repr = str(obj) | |
def render(self, name, value, attrs=None, renderer=None): | |
if self.pk: | |
orig_input = super(FKLinkWidget, self).render(name, value, attrs, renderer) | |
view_name = f"admin:{self.app_label}_{self.model_name}_change" | |
link_url = reverse(view_name, args=[self.pk]) | |
hidden_input = orig_input.replace('type="text"', 'type="hidden"') | |
link_tpl = '<a href="{}" target="_blank">{}</a>' | |
return format_html(link_tpl + hidden_input, link_url, self.repr) | |
else: | |
return format_html(value or self.NO_VALUE_TEXT) | |
class CustomModelAdmin(admin.ModelAdmin): | |
"""extendable ModelAdmin which provides several custom attributes | |
- fk_links = list of FK fields that should be shown as read-only links on detail page | |
this can prevent loading all choice options by django admin, which results 504 http error | |
""" | |
fk_links = [] | |
def __init__(self, model, admin_site): | |
super().__init__(model, admin_site) | |
intersect = set(self.fk_links).intersection(self.readonly_fields + self.raw_id_fields) | |
if intersect: | |
raise ValueError(f'CustomModelAdmin fields: {intersect} are in readonly or raw_id fields') | |
def get_form(self, request, obj=None, **kwargs): | |
self.obj = obj | |
form = super().get_form(request, obj, **kwargs) | |
return form | |
def formfield_for_dbfield(self, db_field, **kwargs): | |
formfield = super().formfield_for_dbfield(db_field, **kwargs) | |
if db_field.name in self.fk_links: | |
fk_obj = self.get_fk_obj(db_field) | |
if fk_obj: | |
formfield.widget.widget.set_obj(fk_obj) | |
# we disable any actions for that field | |
formfield.widget.can_add_related = False | |
formfield.widget.can_change_related = False | |
formfield.widget.can_delete_related = False | |
return formfield | |
def get_fk_obj(self, db_field): | |
if self.obj: | |
fk = getattr(self.obj, db_field.name) | |
if fk: | |
return fk | |
def formfield_for_foreignkey(self, db_field, request=None, **kwargs): | |
if db_field.name in self.fk_links: | |
kwargs["required"] = False | |
kwargs["widget"] = FKLinkWidget | |
fk_obj = self.get_fk_obj(db_field) | |
queryset = db_field.remote_field.model._default_manager.all() | |
if fk_obj: | |
kwargs["queryset"] = queryset.filter(pk=fk_obj.pk) | |
else: | |
kwargs["queryset"] = queryset.none() | |
formfield = super().formfield_for_foreignkey(db_field, request, **kwargs) | |
return formfield | |
# usage | |
@admin.register(UserProfile) | |
class UserProfileAdmin(CustomModelAdmin): | |
fk_links = ['user',] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment