Skip to content

Instantly share code, notes, and snippets.

@gregplaysguitar
Last active October 19, 2022 05:52
Show Gist options
  • Save gregplaysguitar/b97aa5b96d4f322009ccb63a4a8621ce to your computer and use it in GitHub Desktop.
Save gregplaysguitar/b97aa5b96d4f322009ccb63a4a8621ce to your computer and use it in GitHub Desktop.
Show object description and a link next to django's raw_id_fields, for ForeignKey and ManyToMany fields
"""
Show customisable str representation of the linked objects next to each raw id
field in a django admin change form. Works for ForeignKey, OneToOneField, and
ManyToManyField fields.
Example:
from django.contrib import admin
from admin_raw_id import ImprovedRawIdFieldsAdmin
from .models import MyModel
@admin.register(MyModel)
class MyModelAdmin(ImprovedRawIdFieldsAdmin):
...
Inspired by https://djangosnippets.org/snippets/2217/
"""
from django.contrib import admin
from django.contrib.admin.sites import site
from django.contrib.admin.widgets import ManyToManyRawIdWidget, \
ForeignKeyRawIdWidget
from django.core.urlresolvers import reverse
from django.utils.html import escape
class VerboseForeignKeyRawIdWidget(ForeignKeyRawIdWidget):
"""Shows customisable str representation of the linked object next to the
raw id field for a ForeignKey or OneToOneField in a django admin change
form. To customise pass get_str as a keyword argument when initialising
the widget.
"""
def __init__(self, *args, **kwargs):
self.get_str = kwargs.pop('get_str', str)
super(VerboseForeignKeyRawIdWidget, self).__init__(*args, **kwargs)
def label_for_value(self, value):
key = self.rel.get_related_field().name
try:
obj = self.rel.to._default_manager.using(
self.db).get(**{key: value})
except (ValueError, self.rel.to.DoesNotExist):
return '???'
change_url = reverse(
"admin:%s_%s_change" % (obj._meta.app_label,
obj._meta.object_name.lower()),
args=(obj.pk,)
)
return '&nbsp;<strong><a href="%s">%s</a></strong>' % (
change_url, escape(self.get_str(obj)))
class VerboseManyToManyRawIdWidget(ManyToManyRawIdWidget):
"""Shows customisable str representation of the linked object next to the
raw id field for a ManyToManyField in a django admin change form. To
customise pass get_str as a keyword argument when initialising the
widget.
"""
def __init__(self, *args, **kwargs):
self.get_str = kwargs.pop('get_str', str)
super(VerboseManyToManyRawIdWidget, self).__init__(*args, **kwargs)
def label_for_value(self, value):
values = value.split(',')
str_values = []
key = self.rel.get_related_field().name
for v in values:
try:
obj = self.rel.to._default_manager.using(self.db).get(
**{key: v})
except self.rel.to.DoesNotExist:
str_values += ['???']
else:
change_url = reverse(
"admin:%s_%s_change" % (obj._meta.app_label,
obj._meta.object_name.lower()),
args=(obj.pk,)
)
str_values += ['<strong><a href="%s">%s</a></strong>' % (
change_url, escape(self.get_str(obj)))]
return ', '.join(str_values)
class ImprovedRawIdFieldsAdmin(admin.ModelAdmin):
def formfield_for_dbfield(self, db_field, **kwargs):
if db_field.name in self.raw_id_fields:
kwargs.pop("request", None)
rel_type = db_field.rel.__class__.__name__
if rel_type == "ManyToManyRel":
kwargs['widget'] = VerboseManyToManyRawIdWidget(
db_field.rel, site)
else:
# assume it's a ManyToOneRel or OneToOneRel
kwargs['widget'] = VerboseForeignKeyRawIdWidget(
db_field.rel, site)
return db_field.formfield(**kwargs)
return super(ImprovedRawIdFieldsAdmin, self).formfield_for_dbfield(
db_field, **kwargs)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment