Login

Link raw_id_fields (both ForeignKeys and ManyToManyFields) to their change pages

Author:
EmilStenstrom
Posted:
October 4, 2010
Language:
Python
Version:
1.4
Score:
6 (after 6 ratings)

UPDATE: Now works in Django 1.4

Based on luc_j:s snippet (http://djangosnippets.org/snippets/2108/) to show values in ManyToManyFields in the admin. This snippets does that, but also links each value to its corresponding admin change page.

To use, just set the raw_id_fields to the value you want, and let your form inherit from ImproveRawIdFieldsForm.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
from django import forms
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.encoding import smart_unicode
from django.utils.html import escape

class VerboseForeignKeyRawIdWidget(ForeignKeyRawIdWidget):
    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})
            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(obj))
        except (ValueError, self.rel.to.DoesNotExist):
            return '???'

class VerboseManyToManyRawIdWidget(ManyToManyRawIdWidget):
    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})
                x = smart_unicode(obj)
                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(x))]
            except self.rel.to.DoesNotExist:
                str_values += [u'???']
        return u', '.join(str_values)

class ImproveRawIdFieldsForm(admin.ModelAdmin):
    def formfield_for_dbfield(self, db_field, **kwargs):
        if db_field.name in self.raw_id_fields:
            kwargs.pop("request", None)
            type = db_field.rel.__class__.__name__
            if type == "ManyToOneRel":
                kwargs['widget'] = VerboseForeignKeyRawIdWidget(db_field.rel, site)
            elif type == "ManyToManyRel":
                kwargs['widget'] = VerboseManyToManyRawIdWidget(db_field.rel, site)
            return db_field.formfield(**kwargs)
        return super(ImproveRawIdFieldsForm, self).formfield_for_dbfield(db_field, **kwargs)

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 2 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 2 months, 3 weeks ago
  3. Serializer factory with Django Rest Framework by julio 9 months, 2 weeks ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 10 months, 1 week ago
  5. Help text hyperlinks by sa2812 11 months ago

Comments

Cerinin (on February 26, 2012):

Thanks, works perfectly! I can't imagine why this functionality isn't included by default!

#

EmilStenstrom (on April 25, 2012):

Updated to work in Django 1.4.

#

AndyHilton (on December 29, 2012):

This sounds like just what I am looking for but struggling to see where to put the pieces (as with so many django snippets it's hard when it is just raw code without a note to say where it should be placed !) - I tried adding the above to my admin.py but cannot then see how to inherit my form from ImproveRawIdFieldsForm (my form is currently in forms.py) - am sure I am close !! Any helpful pointers ?

#

EmilStenstrom (on February 12, 2013):

@AndyHilton: Here's some example usage: https://gist.github.com/EmilStenstrom/4761449

#

kase (on August 11, 2013):

so... how i use in INLINE form?

#

rwmcfa1 (on August 8, 2014):

if you change line 45 to the follow it will also support OneToOneRel's

if type in ('ManyToOneRel', 'OneToOneRel'):

also a good ideal to add the following else after line 48 to avoid losing special handling in super's formfield_for_dbfield for raw_id_fields we don't otherwise handle (assuming there's more than 3 types)

else:
    # we don't have anything special for this type
    return super(ImproveRawIdFieldsForm, self).formfield_for_dbfield(db_field, **kwargs)

#

hobarrera (on January 27, 2016):

Note that you need your admin to inherit from ImproveRawIdFieldsForm, not your form, as stated above (this should become obvious if you actually read the code though).

#

ensignexpendable (on February 2, 2016):

To answer @kase (from a few years ago but I've just found a need for this) you can subclass TabularInline in the same way that you can ModelAdmin. Add this:

class ImproveRawIdFieldsInline(admin.TabularInline): def formfield_for_dbfield(self, db_field, **kwargs): if db_field.name in self.raw_id_fields: kwargs.pop("request", None) type = db_field.rel.__class__.__name__ if type == "ManyToOneRel": kwargs['widget'] = VerboseForeignKeyRawIdWidget(db_field.rel, site) elif type == "ManyToManyRel": kwargs['widget'] = VerboseManyToManyRawIdWidget(db_field.rel, site) return db_field.formfield(**kwargs) return super(ImproveRawIdFieldsInline, self).formfield_for_dbfield(db_field, **kwargs)

Also of note is that this code snippet still works as of Django 1.9.1

#

ensignexpendable (on February 2, 2016):

Oops, formatted properly we have

class ImproveRawIdFieldsInline(admin.TabularInline):
    def formfield_for_dbfield(self, db_field, **kwargs):
        if db_field.name in self.raw_id_fields:
            kwargs.pop("request", None)
            type = db_field.rel.__class__.__name__
            if type == "ManyToOneRel":
                kwargs['widget'] = VerboseForeignKeyRawIdWidget(db_field.rel, site)
            elif type == "ManyToManyRel":
                kwargs['widget'] = VerboseManyToManyRawIdWidget(db_field.rel, site)
            return db_field.formfield(**kwargs)
        return super(ImproveRawIdFieldsInline, self).formfield_for_dbfield(db_field, **kwargs)

#

Please login first before commenting.