“ list_display” ใน Django ModelAdmin สามารถแสดงคุณสมบัติของช่อง ForeignKey ได้หรือไม่?


296

ฉันมีPersonรูปแบบที่มีความสัมพันธ์กับคีย์ต่างประเทศBookซึ่งมีหลายฟิลด์ แต่ฉันกังวลมากที่สุดauthor(CharField มาตรฐาน)

ในPersonAdminแบบจำลองของฉันฉันต้องการแสดงbook.authorโดยใช้list_display:

class PersonAdmin(admin.ModelAdmin):
    list_display = ['book.author',]

ฉันได้ลองวิธีการทั้งหมดที่ชัดเจนในการทำเช่นนั้นแล้ว แต่ดูเหมือนว่าจะไม่มีอะไรทำงาน

ข้อเสนอแนะใด ๆ

คำตอบ:


472

เป็นตัวเลือกอื่นคุณสามารถค้นหาได้เช่น:

class UserAdmin(admin.ModelAdmin):
    list_display = (..., 'get_author')

    def get_author(self, obj):
        return obj.book.author
    get_author.short_description = 'Author'
    get_author.admin_order_field = 'book__author'

ไม่ควรเป็นทั้งคู่get_authorเนื่องจากเป็นสิ่งที่สตริงที่คุณส่งคืน (และคำอธิบายสั้น ๆ ) อ้างอิงจริงหรือไม่ หรือเปลี่ยนอาร์กิวเมนต์รูปแบบสตริงเป็นobj.book.reviews?
Carl G

1
@AnatoliyArkhipov มีวิธี (ขึ้นอยู่กับคำตอบ Terr ) ฉันได้อัปเดตรหัสในคำตอบนี้แล้ว
Denilson Sá Maia

ทำไมคุณauthor = ForeignKey(Author)ถึงไม่มีโมเดลของหนังสือเล่มนี้list_display = ('author')ไม่ได้แล้วล่ะ?
alias51

3
สิ่งนี้ทำให้เกิดการค้นหาหนึ่งรายการต่อแถวในผู้ดูแลระบบ :(
marcelm

1
@marcelm นั่นคือสิ่งที่select_relatedมีไว้เพื่อ get_queryset()ของUserAdminจะต้องเขียนทับ
interDist

142

แม้จะมีคำตอบที่ดีทั้งหมดข้างต้นและเนื่องจากฉันยังใหม่กับ Django ฉันยังคงติดอยู่ นี่คือคำอธิบายของฉันจากมุมมองมือใหม่มาก

models.py

class Author(models.Model):
    name = models.CharField(max_length=255)

class Book(models.Model):
    author = models.ForeignKey(Author)
    title = models.CharField(max_length=255)

admin.py (วิธีที่ไม่ถูกต้อง) - คุณคิดว่ามันจะใช้งานได้โดยใช้ 'model__field' เพื่อการอ้างอิง แต่ไม่ได้ผล

class BookAdmin(admin.ModelAdmin):
    model = Book
    list_display = ['title', 'author__name', ]

admin.site.register(Book, BookAdmin)

admin.py (วิธีที่ถูกต้อง) - นี่คือวิธีที่คุณอ้างอิงชื่อต่างประเทศด้วยวิธี Django

class BookAdmin(admin.ModelAdmin):
    model = Book
    list_display = ['title', 'get_name', ]

    def get_name(self, obj):
        return obj.author.name
    get_name.admin_order_field  = 'author'  #Allows column order sorting
    get_name.short_description = 'Author Name'  #Renames column head

    #Filtering on side - for some reason, this works
    #list_filter = ['title', 'author__name']

admin.site.register(Book, BookAdmin)

สำหรับการอ้างอิงเพิ่มเติมดูลิงค์รุ่น Django ที่นี่


3
สำหรับฟิลด์คำสั่งซื้อไม่ควรเป็น = 'author__name' หรือ
Yunti

2
ทำงานได้อย่างสมบูรณ์แบบ แต่ฉันไม่แน่ใจว่าทำไม objคือBookAdminอะไร
Steven Church

ว้าว. ใช้เวลาหนึ่งชั่วโมงบนเว็บเพื่อค้นหาสิ่งนี้ นี้ควรจะทำมากชัดเจนในเอกสาร Django
Sevenearths

67

เช่นเดียวกับส่วนที่เหลือฉันก็มี callables ด้วย แต่มีข้อเสียเดียว: โดยค่าเริ่มต้นคุณไม่สามารถสั่งซื้อได้ โชคดีที่มีวิธีแก้ปัญหาคือ:

Django> = 1.8

def author(self, obj):
    return obj.book.author
author.admin_order_field  = 'book__author'

Django <1.8

def author(self):
    return self.book.author
author.admin_order_field  = 'book__author'

ลายเซ็นวิธีการควรเป็นdef author(self, obj):
sheats

ย้อนกลับไปเมื่อฉันแสดงความคิดเห็นมันไม่ใช่กรณี แต่ปรากฏว่าตั้งแต่รุ่น 1.8 วิธีการได้รับวัตถุผ่านไป ฉันอัพเดตคำตอบแล้ว
Arjen

46

โปรดทราบว่าการเพิ่มget_authorฟังก์ชั่นจะทำให้ list_display ช้าลงในผู้ดูแลระบบเนื่องจากการแสดงแต่ละคนจะทำให้คิวรี SQL

เพื่อหลีกเลี่ยงปัญหานี้คุณต้องแก้ไขget_querysetวิธีใน PersonAdmin ตัวอย่างเช่น:

def get_queryset(self, request):
    return super(PersonAdmin,self).get_queryset(request).select_related('book')

ก่อน: 73 คำค้นหาใน 36.02ms (คำค้นหาซ้ำซ้อน 67 รายการในผู้ดูแลระบบ)

หลังจาก: 6 ข้อความค้นหาใน 10.81ms


3
สิ่งนี้สำคัญมากและควรนำไปใช้เสมอ
xleon

นี่เป็นสิ่งสำคัญอย่างแน่นอน หรือถ้าคนใดคนหนึ่งที่จะไปลง__str__เส้นทางเพียงแค่เพิ่ม ForeignKey ไปlist_displayและlist_select_related
Scratch'N'Purr

22

ตามเอกสารคุณสามารถแสดงการ__unicode__เป็นตัวแทนของ ForeignKey:

http://docs.djangoproject.com/en/dev/ref/contrib/admin/#list-display

ดูเหมือนแปลกที่ไม่รองรับ'book__author'รูปแบบสไตล์ที่ใช้ในที่อื่นใน DB API

ปรากฎว่ามีตั๋วสำหรับคุณสมบัตินี้ซึ่งถูกทำเครื่องหมายว่าจะไม่แก้ไข


11
@Mermoz จริงเหรอ? ดูเหมือนว่าตั๋วจะถูกตั้งค่าเป็น wontfix ไม่ปรากฏว่าใช้งานได้ (Django 1.3)
Dave

1.11 ยังคงไม่มีอยู่ รับทำ django เป็นเวลาหลายสิบปีและฉันไม่เคยจำอันนี้ :(
Aaron McMillin

12

ฉันเพิ่งโพสต์ตัวอย่างที่ทำให้ admin.ModelAdmin รองรับไวยากรณ์ '__':

http://djangosnippets.org/snippets/2887/

ดังนั้นคุณสามารถทำได้:

class PersonAdmin(RelatedFieldAdmin):
    list_display = ['book__author',]

นี่เป็นเพียงการทำสิ่งเดียวกันที่อธิบายไว้ในคำตอบอื่น ๆ แต่จะดูแล (1) การตั้งค่า admin_order_field (2) การตั้งค่า short_description และ (3) การปรับเปลี่ยนชุดแบบสอบถามเพื่อหลีกเลี่ยงการตีฐานข้อมูลสำหรับแต่ละแถว


ฉันชอบความคิดนี้มาก แต่ดูเหมือนจะไม่ทำงานอีกต่อไปกับ verango django เมื่อเร็ว ๆ นี้:AttributeError: type object 'BaseModel' has no attribute '__metaclass__'
Vincent van Leeuwen

10

คุณสามารถแสดงสิ่งที่คุณต้องการในการแสดงรายการโดยใช้ callable มันจะมีลักษณะเช่นนี้:

def book_author (วัตถุ):
  ส่งคืน object.book.author

คลาส PersonAdmin (admin.ModelAdmin):
  list_display = [book_author,]

อันนี้ดีสำหรับสถานการณ์ที่หลายรุ่นที่แตกต่างกันมักจะเรียกคุณลักษณะที่เดียวกัน; รองรับ 1.3+ ไหม
kagali-san

3
ปัญหาเกี่ยวกับเรื่องนี้คือจำนวนของแบบสอบถาม SQL ที่ทำในท้ายที่สุด สำหรับแต่ละวัตถุในรายการมันจะสร้างแบบสอบถาม นี่คือเหตุผลที่ 'field__attribute' นั้นมีประโยชน์มากเพราะแน่นอนว่า Django จะขยายไปยังแบบสอบถาม SQL หนึ่งรายการเท่านั้น แปลกที่ไม่มีการสนับสนุนนี้แล้ว
emyller

7

คนนี้ได้รับการยอมรับแล้ว แต่ถ้ามีหุ่นอื่น ๆ ออกไปที่นั่น (เช่นฉัน) ที่ไม่ได้รับมันทันทีจากคำตอบที่ยอมรับในปัจจุบันนี่คือรายละเอียดอีกเล็กน้อย

คลาสโมเดลอ้างอิงโดยForeignKeyความต้องการให้มี__unicode__เมธอดอยู่ภายในเช่นเดียวกับที่นี่:

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

    def __unicode__(self):
        return self.name

นั่นทำให้ความแตกต่างสำหรับฉันและควรนำไปใช้กับสถานการณ์ข้างต้น ใช้งานได้กับ Django 1.0.2


4
บนหลาม 3 def __str__(self):นี้จะเป็น
Martlark

5

หากคุณมีฟิลด์แอตทริบิวต์ความสัมพันธ์จำนวนมากที่จะใช้ในlist_displayและไม่ต้องการสร้างฟังก์ชั่น (และเป็นคุณสมบัติ) สำหรับแต่ละอันโซลูชันที่เรียบง่าย แต่เรียบง่ายจะแทนที่เมธอดModelAdmininstace __getattr__สร้าง callables ได้ทันที:

class DynamicLookupMixin(object):
    '''
    a mixin to add dynamic callable attributes like 'book__author' which
    return a function that return the instance.book.author value
    '''

    def __getattr__(self, attr):
        if ('__' in attr
            and not attr.startswith('_')
            and not attr.endswith('_boolean')
            and not attr.endswith('_short_description')):

            def dyn_lookup(instance):
                # traverse all __ lookups
                return reduce(lambda parent, child: getattr(parent, child),
                              attr.split('__'),
                              instance)

            # get admin_order_field, boolean and short_description
            dyn_lookup.admin_order_field = attr
            dyn_lookup.boolean = getattr(self, '{}_boolean'.format(attr), False)
            dyn_lookup.short_description = getattr(
                self, '{}_short_description'.format(attr),
                attr.replace('_', ' ').capitalize())

            return dyn_lookup

        # not dynamic lookup, default behaviour
        return self.__getattribute__(attr)


# use examples    

@admin.register(models.Person)
class PersonAdmin(admin.ModelAdmin, DynamicLookupMixin):
    list_display = ['book__author', 'book__publisher__name',
                    'book__publisher__country']

    # custom short description
    book__publisher__country_short_description = 'Publisher Country'


@admin.register(models.Product)
class ProductAdmin(admin.ModelAdmin, DynamicLookupMixin):
    list_display = ('name', 'category__is_new')

    # to show as boolean field
    category__is_new_boolean = True

เป็นส่วนสำคัญที่นี่

คุณลักษณะเฉพาะ Callable ชอบbooleanและshort_descriptionต้องได้รับการกำหนดให้เป็นModelAdminคุณลักษณะเช่นและbook__author_verbose_name = 'Author name'category__is_new_boolean = True

admin_order_fieldแอตทริบิวต์callable ถูกกำหนดโดยอัตโนมัติ

อย่าลืมใช้แอตทริบิวต์list_select_relatedในของคุณModelAdminเพื่อทำให้ Django หลีกเลี่ยงข้อความค้นหาเพิ่มเติม


1
เพิ่งลองใช้งานด้วยการติดตั้ง Django 2.2 และมันใช้งานได้ดีสำหรับฉันในขณะที่วิธีการอื่นไม่ได้ด้วยเหตุผลใดก็ตาม โปรดทราบว่าทุกวันนี้คุณจำเป็นต้องนำเข้าการลดลงจาก functools หรือที่อื่น ...
Paul Brackin

5

มีเป็นเรื่องง่ายมากที่จะใช้แพคเกจที่มีอยู่ใน PyPI ที่จับตรงที่: Django เกี่ยวข้องกับผู้ดูแลระบบ นอกจากนี้คุณยังสามารถดูรหัสใน GitHub

การใช้สิ่งนี้สิ่งที่คุณต้องการบรรลุนั้นง่ายพอ ๆ กับ:

class PersonAdmin(RelatedFieldAdmin):
    list_display = ['book__author',]

ลิงก์ทั้งสองนี้มีรายละเอียดทั้งหมดเกี่ยวกับการติดตั้งและการใช้งานดังนั้นฉันจะไม่วางลิงก์เหล่านี้ในกรณีที่มีการเปลี่ยนแปลง

เช่นเดียวกับที่ทราบด้านถ้าคุณกำลังใช้สิ่งอื่น ๆ กว่าmodel.Admin(เช่นผมใช้SimpleHistoryAdminแทน) class MyAdmin(SimpleHistoryAdmin, RelatedFieldAdmin)คุณสามารถทำสิ่งนี้:


getter_for_related_field ไม่ทำงานใน 1.9 ดังนั้นจึงไม่ใช่ทางเลือกที่ดีที่สุดสำหรับผู้ที่ชอบปรับแต่ง
GriMel

4

หากคุณลองใช้ Inline คุณจะไม่ประสบความสำเร็จเว้นแต่:

ในแบบอินไลน์ของคุณ:

class AddInline(admin.TabularInline):
    readonly_fields = ['localname',]
    model = MyModel
    fields = ('localname',)

ในโมเดลของคุณ (MyModel):

class MyModel(models.Model):
    localization = models.ForeignKey(Localizations)

    def localname(self):
        return self.localization.name

-1

คำตอบของ AlexRobbins ทำงานให้ฉันยกเว้นว่าสองบรรทัดแรกต้องอยู่ในรูปแบบ (อาจเป็นแบบนี้หรือไม่) และควรอ้างอิงตนเอง:

def book_author(self):
  return self.book.author

จากนั้นส่วนผู้ดูแลระบบทำงานได้ดี


-5

ฉันชอบสิ่งนี้:

class CoolAdmin(admin.ModelAdmin):
    list_display = ('pk', 'submodel__field')

    @staticmethod
    def submodel__field(obj):
        return obj.submodel.field
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.