Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django admin changelist filtering / link to other models

I have the models set up like this:

class ParentModel(models.Model):
    some_col = models.IntegerField()
    some_other = models.CharField()

class ChildModel(models.Model)
    parent = models.ForeignKey(ParentModel, related_name='children')

class ToyModel(models.Model)
    child_owner = models.ForeignKey(ChildModel, related_name='toys')

Now in my admin panel when I open the changelist for ParentModel I want a new field/column in the list_display with a link to open the changelist of the ChildModel but with an applied filter to show only the children from the selected parent. For now I realized it with this method, but I think there is a cleaner way to do it, I just don't know how:

class ParentAdmin(admin.ModelAdmin)
    list_display = ('id', 'some_col', 'some_other', 'list_children')
    def list_children(self, obj):
        url = urlresolvers.reverse('admin:appname_childmodel_changelist')
        return '<a href="{0}?parent__id__exact={1}">List children</a>'.format(url, obj.id)
    list_children.allow_tags = True
    list_children.short_description = 'Children'        

admin.site.register(Parent, ParentAdmin)

So my question is, is it possible to achieve the same without this "link hacking"? Also is it possible to indicate in a separate column in the ParentModel changelist, if any of its children has toys?

like image 383
Gordon Freeman Avatar asked Dec 18 '11 11:12

Gordon Freeman


1 Answers

I think your approach to display the list_children column is correct. Don't worry about the 'link hacking', it's fine.

To display a column for indicate whether any of the object's children has toys, just define another method on the ParentAdmin class, and add it to list_display as before.

class ParentAdmin(admin.ModelAdmin):
    list_display = ('id', 'some_col', 'some_other', 'list_children', 'children_has_toys')
    ...
    def children_has_toys(self, obj):
        """
        Returns 'yes' if any of the object's children has toys, otherwise 'no'
        """
        return ToyModel.objects.filter(child_owner__parent=obj).exists()
    children_has_toys.boolean = True

Setting boolean=True means Django will render the 'on' or 'off' icons as it does for boolean fields. Note that this approach requires one query per parent (i.e. O(n)). You'll have to test to see whether you get acceptable performance in production.

like image 174
Alasdair Avatar answered Sep 30 '22 18:09

Alasdair