I am using Django 2.1 and I am having a hard time trying to figure out how to show relationships in the Admin.
I have two Models Hospital
and Unit
and a third one is Exam
. They are set up like this:
Hospital
can have many Units and one Unit belongs to one Hospital.Exam
model has the exam attributes like exam name and hospital name.class Hospital(models.Model):
name = models.CharField(max_length=200)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
class Unit(models.Model):
unit_name = models.CharField(max_length=200)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
parent_company = models.ForeignKey(
Hospital, related_name='unit_parent', on_delete=models.CASCADE, verbose_name="Hospital")
class Exam(models.Model):
unit_name = models.ForeignKey(
Hospital, related_name='hospital_pk', on_delete=models.CASCADE)
exam_name = models.CharField(max_length=255, blank=True,
null=True, verbose_name="Exam's name")
date = models.DateField()
time = models.TimeField()
def __str__(self):
return self.exam_name
class CustomExamAdmin(admin.ModelAdmin):
model = Exam
list_display = ('exam_name', 'time', 'alert',
'document', 'notes', 'report', 'patients_name', 'unit_name')
list_filter = ('exam_name', 'time')
list_select_related = ('unit_name',)
fieldsets = (
("Hospital's Name:", {
'fields': ('unit_name',)}),
('Types of Exams:', {
'fields': ('exam_name', 'modality', 'model_used')}),
("Patient's name:", {
'fields': ('patients_name',)}),
)
So, in the Exams page in Django Admin I am able to select the Hospital name, however I am not able to select the Unit that belongs to the hospital. I need to select the hospital as well as the unit that belongs to it.
Should I add another ForeignKey
to Exam, like this:
unit_selected = models.ForeignKey(
Unit, related_name='unit_pk', on_delete=models.CASCADE)
For instance: Hospital: Brookdale University Hospital Unit: Downtown Unit
What I get: enter image description here
In order to select the Unit
in the Admin
, you need to first add it to the Exam
model. You have a field called unit_name
, but you have defined it as a foreign key to the Hospital
model, not the Unit
model. If you have a unit_name
field, you don't really need a separate hospital
field, since you can easily get it from unit_name.parent_company
. So you can do this:
class Exam(models.Model):
unit_name = models.ForeignKey(
Unit, related_name='exams', on_delete=models.CASCADE)
exam_name = models.CharField(max_length=255, blank=True,
null=True, verbose_name="Exam's name")
date = models.DateField()
time = models.TimeField()
def __str__(self):
return self.exam_name
For the admin, rather than selecting the hospital and unit separately, the simplest approach, in my opinion, is to include the hospital name as part of the unit name when you select it in the admin, like Brookdale University Hospital|Downtown Unit
. That way you only need one dropdown field. You can do that like this:
from django import forms
class UnitChoiceField(forms.ModelChoiceField):
def label_from_instance(self, obj):
return "{}|{}".format(obj.parent_company.name, obj.unit_name)
class CustomExamAdmin(admin.ModelAdmin):
model = Exam
list_display = ('exam_name', 'time', 'alert',
'document', 'notes', 'report', 'patients_name', 'get_hospital', 'unit_name')
list_filter = ('exam_name', 'time')
list_select_related = ('unit_name__parent_company',)
fieldsets = (
("Hospital's Name:", {
'fields': ('unit_name',)}),
('Types of Exams:', {
'fields': ('exam_name', 'modality', 'model_used')}),
("Patient's name:", {
'fields': ('patients_name',)}),
)
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == 'unit_name':
return UnitChoiceField(
queryset=Unit.objects.all().select_related('parent_company').order_by(
'parent_company.name', 'unit_name'))
return super().formfield_for_foreignkey(db_field, request, **kwargs)
def get_hospital(self, obj):
# required to add hospital name to list_display
return obj.unit_name.parent_company.name
get_hospital.short_description = 'Hospital'
One other note: the related_name
in a ForeignKeyField
is the relation from the foreign model back to the model you are defining. So in the Unit
model
parent_company = models.ForeignKey(
Hospital, related_name='unit_parent', on_delete=models.CASCADE, verbose_name="Hospital")
should be
parent_company = models.ForeignKey(
Hospital, related_name='units', on_delete=models.CASCADE, verbose_name="Hospital")
Then from the Hospital
model you can refer to the list of units as units
.