在Django admin中获取list_display以显示manytone关系的“many”端

2024-10-01 15:46:29 发布

您现在位置:Python中文网/ 问答频道 /正文

我想显示所有的宠物主人(客户)使用列表显示,并为每个主人的所有宠物(病人)逗号单独的列表。在

外键在病人表中,这样一个主人可以有许多宠物,但是一个宠物只能有一个主人。在

我有以下的工作,但想一些建议,这是否是一个可接受的方法。在

from .models import Client, Patient

class ClientAdmin(admin.ModelAdmin):
    list_display = ('first_name', 'last_name', 'mobile', 'patients')

    def patients(self,obj):
        p = Patient.objects.filter(client_id=obj.pk)
        return list(p)

它看起来是这样的: enter image description here

谢谢你的指导。在

更新: 以下是我目前的情况:

到目前为止,我努力做到的是

^{pr2}$

以下是文档re:following relationships backwards。在

当然,上面的例子将client对象的数量“修复”为一个(pk=1),所以我不确定如何获得所有客户端的结果。在

非常感谢你的密码。我几乎肯定是做错了什么错误。但是所以你知道FK现在

 related_name = 'patient_fk'

这解释了为什么我不使用patient_set(因为FOO_set被覆盖)

所以我得到的是:

class ClientAdmin(admin.ModelAdmin):
    list_display = ('first_name', 'last_name', 'mobile', 'getpatients')

    def get_queryset(self, request):
        qs = super(ClientAdmin, self).get_queryset(request)
        return qs.prefetch_related('patient_fk') 

    def getpatients(self, obj):
        return self.patient_fk.all()

我得到的错误是“'ClientAdmin'对象没有属性'patient\fk'”,并且与上面代码的最后一行有关。在

有什么想法吗?在

谢谢!在

编辑

我试过布莱恩的密码:

class ClientAdmin(admin.ModelAdmin):
    list_display = ('first_name', 'last_name', 'mobile', 'getpatients')

    def getpatients(self, obj):
        p = obj.patient_pk.all()
        return list(p)

…并且出现错误'Client' object has no attribute 'patient_fk'

如果我运行原始代码,它仍然可以正常工作:

class ClientAdmin(admin.ModelAdmin):
    list_display = ('first_name', 'last_name', 'mobile', 'getpatients')

    def getpatients(self, obj):
        p = Patient.objects.filter(client_id=obj.pk)
        return list(p)

以下是我的课程供参考:

class Client(TimeStampedModel):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    ....

class Patient(TimeStampedModel):
    client = models.ForeignKey(Client, on_delete=models.CASCADE, related_name='patient_fk')
    name = models.CharField(max_length=30)
    ....

Tags: nameselfobj宠物returnmodelsdeflist
3条回答

如果有效:+1:!!在

但是需要注意的是:它将为每个客户机执行一个查询,因此,如果在admin上显示100个客户机,django将执行100个查询

您可以通过更改admin上的主查询集(like this)并使用prefetch_related('patients')来改进它

应该是这样的:

class ClientAdmin(admin.ModelAdmin):
    list_display = ('first_name', 'last_name', 'mobile', 'patients')

    def get_queryset(self, request):
        qs = super(ClientAdmin, self).get_queryset(request)
        return qs.prefetch_related('patients')  # do read the doc, maybe 'patients' is not the correct lookup for you

    def patients(self,obj):
        return self.patients_set.all()  # since you have prefetched the patients I think it wont hit the database, to be tested

希望这有帮助

注:

您可以使用related object reference获取与客户相关的所有患者,例如:

^{pr2}$

最后一行类似于:

patients = Patient.objects.get(client=client)

最后,您可以重写patient_set名称并使其更漂亮,读https://docs.djangoproject.com/en/1.9/topics/db/queries/#following-relationships-backward

我还没有测试过它,最好能有一个反馈,看看这是否能阻止n+1 problem

def patients(self,obj):
        p = obj.patients.all()
        return list(p)

这是假设在ForeignKey中设置related_name='patients'

编辑:修正错误 EDIT2:已将反向的“名称”更改为相关的“名称”,并添加了“.all()”

现在可以:

class ClientAdmin(admin.ModelAdmin):
    list_display = ('first_name', 'last_name', 'mobile', 'get_patients')

    def get_queryset(self, obj):
        qs = super(ClientAdmin, self).get_queryset(obj)
        return qs.prefetch_related('patient_fk')

    def get_patients(self, obj):
        return list(obj.patient_fk.all())

这个页面只需要6个查询就可以显示。。。在

enter image description here

…与我的原始代码(如下)相比,该代码运行一个单独的查询来检索每个客户机的患者(每页100个客户机)

^{pr2}$

enter image description here

以下是我对如何以及为什么这样做的理解(请随时指出任何错误):

每个模型都有一个管理器,其默认名称为对象,允许我们访问数据库记录。为了从一个模型中提取所有记录,我们使用SomeModel.objects.all(),实际上它只是Manager类的get QuerySet方法返回的QuerySet。在

因此,如果我们需要调整从模型返回的内容(即查询集),那么我们需要重写获取它的方法,即获取查询集。我们的新方法与要重写的方法同名:

 def get_queryset(self, obj):

现在,上面的方法不知道如何访问模式数据。它不包含代码。为了访问数据,我们需要调用'real'get_queryset方法(我们正在重写的方法),这样我们就可以实际取回数据,调整它(添加一些额外的患者信息),然后返回它。在

要访问'original'get_queryset方法并获取queryset对象(包含所有模型数据,没有患者),那么我们使用super()。在

super()允许我们访问父类上的方法。在

例如:

enter image description here

在我们的例子中,它让我们获取ClientAdmin的get_queryset()方法。在

def get_queryset(self, obj):
    qs = super(ClientAdmin, self).get_queryset(obj)

qs将模型中的所有数据保存在QuerySet对象中。在

要“添加”位于一对多关系末尾的所有Patients对象(一个客户可以有多个患者),我们使用prefetch_related()

return qs.prefetch_related('patient_fk')'

这将对每个客户机执行查找,并通过跟随“Patient_fk”外键返回任何Patient对象。这是由Python(而不是SQL)在后台执行的,因此最终结果是一个新的查询集-由单个数据库查找生成-包含我们需要的所有数据,不仅列出主模型中的所有对象,还包括来自其他模型的相关对象。在

那么,如果我们不重写Manager.get_queryset()方法,会发生什么呢?好吧,然后我们只得到特定表(客户机)中的数据,没有关于患者的信息(…和100个额外的数据库点击):

class ClientAdmin(admin.ModelAdmin):
    list_display = ('first_name', 'last_name', 'mobile', 'get_patients')
    #do not override Manager.get_queryset()
    #def get_queryset(self, obj):
    #    qs = super(ClientAdmin, self).get_queryset(obj)
    #    return qs.prefetch_related('patient_fk')

def get_patients(self, obj):
    return list(obj.patient_fk.all())
    #forces extra per-client query by following patient_fk

我希望这能对其他人有所帮助。如果我的解释有任何错误,请告诉我,我会改正的。在

相关问题 更多 >

    热门问题