Django ModelAdmin'deki “list_display”, ForeignKey alanlarının özelliklerini görüntüleyebilir mi?


297

PersonYabancı anahtar ilişkisi Bookolan, bir dizi alanı olan bir modelim var , ama en çok endişeleniyorum author(standart bir CharField).

Bununla birlikte, modelimde şunu kullanarak PersonAdmingörüntülemek istiyorum :book.authorlist_display

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

Bunu yapmak için tüm bariz yöntemleri denedim, ancak hiçbir şey işe yaramıyor.

Baska öneri?

Yanıtlar:


473

Başka bir seçenek olarak, aşağıdaki gibi yukarı bakabilirsiniz:

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'

Her ikisi de olmamalı get_author, çünkü geri döndürdüğünüz dize (ve kısa açıklama) aslında referansta bu mu? Veya dize biçimi bağımsız değişkenini obj.book.reviews?
Carl G

1
@AnatoliyArkhipov, bir yol var ( Terr cevabına dayanarak ). Bu cevaptaki kodu zaten güncelledim.
Denilson Sá Maia

neden sadece author = ForeignKey(Author)kitap modeline sahip olamıyorsun list_display = ('author')?
alias51

3
Bu,
admin'de

1
@marcelm bunun select_relatediçin var. get_queryset()arasında UserAdminyazılır gerekecektir.
interDist

142

Yukarıdaki tüm harika cevaplara rağmen ve Django'da yeni olduğum için hala takılıp kaldım. İşte acemi bir bakış açısından açıklamam.

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 (Hatalı Yol) - başvurmak için 'model__field' kullanarak işe yarayacağını düşünüyorsunuz, ancak çalışmıyor

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

admin.site.register(Book, BookAdmin)

admin.py (Doğru Yol) - Django yolunda yabancı bir anahtar adına bu şekilde başvurursunuz

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)

Ek Başvuru için, Django modeli bağlantıya bakın burada


3
Sipariş alanı için = = author__name 'olmamalı mı?
Yunti

2
Bu mükemmel çalışıyor, ancak neden olduğundan emin değilim. objnedir BookAdmin?
Steven Kilisesi

Vay. Bunu bulmak için bana web'de bir saat sürdü. Bu Django belgelerinde çok daha açık hale getirilmelidir
Sevenearths

67

Diğerleri gibi ben de callables ile gittim. Ama bir dezavantajı var: varsayılan olarak, onlara sipariş veremezsiniz. Neyse ki, bunun için bir çözüm var:

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'

yöntem imza olmalıdırdef author(self, obj):
sheats

Yorum yaptığımda durum böyle değildi ama 1.8 sürümünden beri yöntemin nesneye geçtiği anlaşılıyor. Cevabımı güncelledim.
Arjen

46

get_authorHer kişinin gösterilmesi bir SQL sorgusu yapacağından , işlev eklemenin admin'deki list_display'i yavaşlatacağını lütfen unutmayın .

Bundan kaçınmak için get_queryset, PersonAdmin'de yöntemi değiştirmeniz gerekir , örneğin:

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

Önceki: 36.02ms'de 73 sorgu (admin'de 67 yinelenen sorgu)

Sonra: 10.81 ms içinde 6 sorgu


3
Bu gerçekten önemli ve her zaman uygulanmalıdır
xleon

Bu gerçekten önemlidir. Alternatif olarak, biri __str__rotadan aşağı list_displaylist_select_related
inecek olsaydı, yabancı anahtarı

22

Belgelere göre, yalnızca __unicode__bir ForeignKey temsilini görüntüleyebilirsiniz :

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

Desteklemediği tuhaf görünüyor 'book__author'DB API'sinde başka her yerde kullanılan stil biçimini .

Dönüşler daha varmış bu özellik için bir bilet Will Fix olarak işaretlenmiş.


11
@Mermoz gerçekten mi? Bilet alışkanlık olarak ayarlanmış görünüyor. Bu da işe yaramıyor (Django 1.3)
Dave

1.11 hala mevcut değil. Bir düzine yıldır django yapıyorum ve bunu hiç hatırlamıyorum :(
Aaron McMillin

12

Ben sadece admin.ModelAdmin '__' sözdizimini destek yapan bir pasaj gönderdim:

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

Böylece şunları yapabilirsiniz:

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

Bu temelde sadece diğer cevaplarda anlatılanla aynı şeyleri yapmakla birlikte, otomatik olarak (1) admin_order_field (2) ayarının short_description ayarını ve (3) her satır için bir veritabanı isabetinden kaçınmak için queryset'i değiştirmekle ilgilenir.


Bu fikri çok beğendim, ancak son django sürümleriyle artık çalışmıyor gibi görünüyor:AttributeError: type object 'BaseModel' has no attribute '__metaclass__'
Vincent van Leeuwen

10

Çağrılabilir bir liste kullanarak liste ekranında istediğinizi gösterebilirsiniz. Şöyle görünecektir:

def book_author (nesne):
  return object.book.author

Sınıf PersonAdmin (admin.ModelAdmin):
  list_display = [kitap_yazarı,]

Bu, birçok farklı modelin genellikle aynı özelliğe çağırdığı durumlar için iyidir; 1.3+ sürümünde destekleniyor mu?
Kagali-san

3
Bu sorun, sonunda yapılan SQL sorgularının miktarıdır. Listedeki her nesne için bir sorgu oluşturur. Bu yüzden 'field__attribute' çok kullanışlı olurdu, çünkü kesinlikle Django bunu sadece bir SQL sorgusuna yayıyor. Bunu zaten desteklemediği garip.
emyller

7

Bu zaten kabul edildi, ama dışarıda (benim gibi) şu anda kabul edilen cevaptan hemen alamayan başka aptallar varsa , burada biraz daha detay var.

ForeignKeyİhtiyaçların referans aldığı model sınıfının __unicode__, burada olduğu gibi bir yöntemi olması gerekir:

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

    def __unicode__(self):
        return self.name

Bu benim için bir fark yarattı ve yukarıdaki senaryoya uygulanmalıdır. Bu Django 1.0.2 üzerinde çalışır.


4
Python 3'te bu olurdu def __str__(self):.
Martlark

5

Kullanılacak çok fazla ilişki özniteliği alanınız varsa list_displayve her biri için bir işlev (ve onun öznitelikleri) oluşturmak istemiyorsanız, kir ancak basit bir çözüm , anında callables oluşturarak ModelAdmininstace __getattr__yöntemini geçersiz kılar :

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

Burada öz olarak

Çağrılabilir özel nitelikler booleanve short_descriptiongibi ModelAdminnitelikler olarak tanımlanmalıdır , örn. book__author_verbose_name = 'Author name'Ve category__is_new_boolean = True.

Çağrılabilir admin_order_fieldöznitelik otomatik olarak tanımlanır.

Django'nun ek sorgulardan kaçınması için list_select_related özniteliğini kullanmayı unutmayın ModelAdmin.


1
Sadece bir Django 2.2 kurulumu ile denedim & diğer yaklaşımlar ne olursa olsun, benim için harika çalıştı. Bugünlerde ithal etmek gerekir functools veya başka bir yerde azaltmak ...
Paul Brackin

5

PyPI'de tam olarak işleyen, kullanımı kolay bir paket var: django ile ilgili-admin . Kodu GitHub'da da görebilirsiniz .

Bunu kullanarak, elde etmek istediğiniz şey şu kadar basittir:

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

Her iki bağlantı da kurulum ve kullanımla ilgili tüm ayrıntıları içerir, bu yüzden değişmeleri durumunda buraya yapıştırmam.

Sadece bir yan not olarak zaten başka bir şey kullanıyorsanız, model.Admin(örneğin ben kullanıyordum SimpleHistoryAdminyerine), bunu yapabilirsiniz: class MyAdmin(SimpleHistoryAdmin, RelatedFieldAdmin).


getter_for_related_field 1.9'da çalışmıyor, bu yüzden özelleştirmeyi sevenler için en iyi seçim değil gibi görünüyor.
GriMel

4

Inline'da denerseniz, başarılı olmazsınız:

satır içi:

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

modelinizde (MyModel):

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

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

-1

AlexRobbins'in cevabı benim için çalıştı, ancak ilk iki satırın modelde olması gerekiyordu (belki de bu varsayıldı?) Ve kendine referans vermelidir:

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

Sonra yönetici kısmı güzel çalışıyor.


-5

Bunu tercih ederim:

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

    @staticmethod
    def submodel__field(obj):
        return obj.submodel.field
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.