Django Admin中外键关联模型的类似内联解决方案

17 投票
4 回答
10518 浏览
提问于 2025-04-16 19:57

我有几个顾客,他们会预约时间。每个预约都有一个顾客,但一个顾客可以预约多个不同时间的预约。

class Customer(model.Model):
    def __unicode__(self):
        return u'%s' % (self.name,)
    name = models.CharField(max_length=30)
    # and about ten other fields I'd like to see from the admin view.

class Appointment(models.Model):
    datetime = models.DateTimeField()
    customer = models.ForeignKey("Customer")
    class Meta:
        ordering = ('datetime',)

现在,当管理员想要查看按时间排序的预约安排时,有时候他们想了解某个预约的顾客信息。目前,他们需要记住顾客的名字,然后从预约页面跳转到顾客的管理页面,找到记住的顾客,才能查看他们的信息。

理想情况下,如果能在管理员界面直接显示顾客信息就好了。不过,我发现只有当顾客有一个指向预约的外键时,才能在预约的管理页面上创建一个CustomerInline。但是Django给我报错,提示顾客没有指向预约的外键。有没有人知道类似的功能,但当预约有一个指向顾客的外键时该怎么做?

注意:我简化了模型;实际上,顾客字段除了名字外还有大约10个其他字段(一些是自由文本),所以把所有信息放在__unicode__里并不实际。

4 个回答

1

在你的预约(Appointments)管理类(ModelAdmin)中,你需要声明以下这个方法:

class MySuperModelAdmin(admin.ModelAdmin):
  def get_form(self, request, obj=None, **kwargs):

    if obj:
      # create your own model admin instance here, because you will have the Customer's
      # id so you know which instance to fetch
      # something like the following
      inline_instance = MyModelAdminInline(self.model, self.admin_site)
      self.inline_instances = [inline_instance]

    return super(MySuperModelAdmin, self).get_form(request, obj, **kwargs)

想了解更多信息,可以查看这个函数的源代码,这样你就能知道你可以使用哪些内容。

https://code.djangoproject.com/browser/django/trunk/django/contrib/admin/options.py#L423

7

在Django中,想要做到这一点并不简单。因为内联(inlines)是为了跟踪关系而设计的,主要是从后往前查找。

一个比较好的替代方法是给用户对象提供一个链接。在列表视图中,这个操作非常简单:

在你的预约模型中添加一个方法,比如:

def customer_admin_link(self):
    return '<a href="%s">Customer</a>' % reverse('admin:app_label_customer_change %s') % self.id
customer_admin_link.allow_tags = True
customer_admin_link.short_description = 'Customer'

然后在你的ModelAdmin中添加:

list_display = (..., 'customer_admin_link', ...)

还有一种解决方案可以让你得到想要的效果,不过会稍微复杂一些,那就是定义一个自定义的管理模板。如果你这样做,你基本上可以实现任何功能。这里有一个我之前用过的指南来解释这个过程:

http://www.unessa.net/en/hoyci/2006/12/custom-admin-templates/

简单来说,就是从Django的源代码中复制更改表单,并添加代码来显示客户信息。

4

补充一下@John的回答,说明一下你希望在变更列表中看到什么:

return '<a href="%s">%s</a>' % (
                     reverse('admin:applabel_customer_change', (self.customer.id,)),
                     self.customer.name # add more stuff here
             )

如果想把这个添加到变更表单中,可以参考:在Django管理后台的变更表单中,在两个模型字段之间添加自定义HTML

撰写回答