Display foreign key columns as link to detail object in Django admin

后端 未结 3 733
小鲜肉
小鲜肉 2021-02-02 04:18

As explained in link-in-django-admin-to-foreign-key-object, one can display a ForeignKey field as a link to the admin detail page.

To summarize,

class Fo         


        
相关标签:
3条回答
  • 2021-02-02 04:49

    A good start would be looking at the source of BaseModelAdmin and ModelAdmin. Try to find out how the ModelAdmin generates the default links. Extend ModelAdmin, add a method to generate links to arbitrary foreign keys and look at how ChangeList generates the change list.

    I would also suggest you use format_html to render the links, which makes link_to_bar.allow_tags = True unnecessary:

    from django.utils.html import format_html
    
    class FooAdmin(ModelAdmin):
        list_display = ('link_to_bar', )
        def link_to_bar(self, obj):
            link = urlresolvers.reverse('admin:app_bar_change', args=[obj.bar_id])
            return format_html('<a href="{}">{}</a>', link, obj.bar) if obj.bar else None
    
    0 讨论(0)
  • 2021-02-02 04:52

    A slight respin on the accepted answer. It is not necessarily better, but implements some of the advice in the comments:

    from django.contrib.contenttypes.models import ContentType
    from django.urls import reverse
    from django.utils.html import format_html
    
    
    def linkify(field_name):
        def _linkify(obj):
            content_type = ContentType.objects.get_for_model(obj)
            app_label = content_type.app_label
            linked_obj = getattr(obj, field_name)
            linked_content_type = ContentType.objects.get_for_model(linked_obj)
            model_name = linked_content_type.model
            view_name = f"admin:{app_label}_{model_name}_change"
            link_url = reverse(view_name, args=[linked_obj.pk])
            return format_html('<a href="{}">{}</a>', link_url, linked_obj)
    
        _linkify.short_description = field_name.replace("_", " ").capitalize()
        return _linkify
    
    0 讨论(0)
  • 2021-02-02 04:55

    The solution below uses this answer but makes it reusable by all models, avoiding the need to add methods to each admin class.

    Example Models

    # models.py
    from django.db import models
    
    class Country(models.Model):
        name = models.CharField(max_length=200)
        population = models.IntegerField()
    
    class Career(models.Model):
        name = models.CharField(max_length=200)
        average_salary = models.IntegerField()
    
    class Person(models.Model):
        name = models.CharField(max_length=200)
        age = models.IntegerField()
        country = models.ForeignKey(Country, on_delete=models.CASCADE)
        career = models.ForeignKey(Career, on_delete=models.CASCADE)
    

    Example Admin

    # admin.py
    from django.utils.html import format_html
    from django.urls import reverse
    
    from .models import Person
    
    
    def linkify(field_name):
        """
        Converts a foreign key value into clickable links.
        
        If field_name is 'parent', link text will be str(obj.parent)
        Link will be admin url for the admin url for obj.parent.id:change
        """
        def _linkify(obj):
            linked_obj = getattr(obj, field_name)
            if linked_obj is None:
                return '-'
            app_label = linked_obj._meta.app_label
            model_name = linked_obj._meta.model_name
            view_name = f'admin:{app_label}_{model_name}_change'
            link_url = reverse(view_name, args=[linked_obj.pk])
            return format_html('<a href="{}">{}</a>', link_url, linked_obj)
    
        _linkify.short_description = field_name  # Sets column name
        return _linkify
    
    
    
    @admin.register(Person)
    class PersonAdmin(admin.ModelAdmin):
        list_display = [
            "name",
            "age",
            linkify(field_name="country"),
            linkify(field_name="career"),
        ]
    

    Results

    Given an App named app, and a Person instance Person(name='Adam' age=20) with country and carreer foreign key values with ids 123 and 456, the list result will be:

    | Name | Age |                          Country                          |...|
    |------|-----|-----------------------------------------------------------|...|
    | Adam |  20 | <a href="/admin/app/country/123">Country object(123)</a>  |...|
    

    (Continues)

    |...|                          Career                         |
    |---|---------------------------------------------------------|
    |...| <a href="/admin/app/career/456">Career object(456)</a>  |
    
    0 讨论(0)
提交回复
热议问题