16

I'm listing a model in Django's admin via a TabularInline. Inside this inline, I'd like to use Django's model traversal syntax to list data in other models referenced from this model via foreign keys. e.g.

class MyRelatedModel(models.Model):
    name = models.CharField(max_length=50)
    created = models.DateField(auto_now_add=True)
    other = models.ForeignKey('MyOtherRelatedModel')

class MyOtherRelatedModel(models.Model):
    name = models.CharField(max_length=50)
    created = models.DateField(auto_now_add=True)
        
class MyRelatedModelInline(admin.TabularInline):
    model = MyRelatedModel
    fields = ['name', 'created', 'other__name']
    #readonly_fields = ['name', 'created', 'other__name']

However, the usage of 'other__name' throws the ImproperlyConfigured error:

'MyRelatedModelInline.fields' refers to field 'other__name' that is missing from the form

Is the model traversal syntax not supported in ModelAdmin instances? If it is supported, what am I doing wrong?

EDIT: If I uncomment readonly_fields, the error becomes:

Caught AttributeError while rendering: 'MyMainModelAdmin' object has no attribute '__name__'
Peter Bašista
  • 749
  • 9
  • 22
Cerin
  • 60,957
  • 96
  • 316
  • 522
  • 3
    possible duplicate of [Displaying ForeignKey data in Django admin change/add page](http://stackoverflow.com/questions/5665133/displaying-foreignkey-data-in-django-admin-change-add-page) – Daniel Roseman Apr 15 '11 at 15:49
  • See my answer to that question. – Daniel Roseman Apr 15 '11 at 15:50
  • @Roseman, No, that is a different issue. See my edit. – Cerin Apr 15 '11 at 16:04
  • For some reason people seem to be having a problem with my answer to that question. It's simple: define a *method* that returns the value (using the standard dot syntax) and use that method's name in readonly_fields. This works. – Daniel Roseman Apr 15 '11 at 18:55

2 Answers2

32

As per Roseman's suggestion above, you can display related (one-to-one or many-to-one) data in a readonly manner with Inline admin models. Here's a little example, just to make sure that we are all on the same page. You can see below that there are three ways to achieve your goal (if I understand that goal correctly).

models.py:

class Company(models.Model):
    name = models.CharField(max_length=50)

class Employee(models.Model):
    name = models.CharField(max_length=50)
    company = models.ForeignKey('Company')
    car = models.ForeignKey('Car')

    def model_callable(self):
        return self.car.rego

class Car(models.Model):
    rego = models.CharField(max_length=50)

admin.py:

def unbound_callable(emp):
    return emp.car.rego

class EmployeeInline(admin.TabularInline):
    model = Employee
    fields = ('name', 'model_callable', 'model_admin_callable', unbound_callable)
    readonly_fields = ('model_callable', 'model_admin_callable', unbound_callable)

    def model_admin_callable(self, emp):
        return emp.car.rego

class CompanyAdmin(admin.ModelAdmin):
    model = Company
    inlines = (EmployeeInline,)

admin.site.register(Company, CompanyAdmin)

As you can see, 'readonly_fields' is treated in the same manner as 'list_display' as per the Django documentation for contrib.admin (from 1.2 onwards).

In the above example, when you are editing a Company, you will see its employees inlined. Each row will have an employee name in an editable textbox and next to the name you will see a readonly bit of text for the employee's car's rego (emp.car.rego).

Referring to your original question, you wanted to reference the related data as 'other__name'. This won't work. Expressions like other__name or car__rego only have special meaning as keyword arguments in filters when running Django queries. For example, when fetching an employee who has a car with a particular rego number:

Employee.objects.filter(car__rego='111')

Hope that helps.

j

jsw
  • 2,173
  • 2
  • 13
  • 14
1

Check this Example, it might clear your doubt to render related fields on admin site.

from django.contrib import admin
from . import models


@admin.register(models.Profile)  #Decorator
class ProfileAdmin(admin.ModelAdmin):
    list_display = ['first_name', 'last_name', 'membership', 'phone', 'birth_date']  # Columns to display
    list_select_related = ['user']  # To avoid extra queries

    def first_name(self, profile):
        return profile.user.first_name  # Foreign key relationship
    
    def last_name(self, profile):
        return profile.user.last_name  # Foreign key relationship

In this example I am rendering Profile Model, In which user is Foreign Key to User Model, and User model contains first_name, last_name.

janos
  • 120,954
  • 29
  • 226
  • 236