smwilsonau
smwilsonau

Reputation: 199

How can I restrict the choices to related object in django?

I am trying to use django to organize and manage my clinical research data. I think I can get by with just the admin interface, since this is a behind-the-scenes database that does not need to go public. I can program, but never in python before, so I am getting stuck on simple things.

I have Patients who make multiple Visits. Each Visit involves multiple Scans. In the admin interface, I can see all the Visits and all the Scans, as inlines on each Patient's page. However, when adding a new Scan, how can I make it so that the choice of which Visit to associate it with is limited to Visits for the Patient in question, not other Patients?

Models (simplified):

class Patient(models.Model):
  patient_id = models.CharField(max_length=16, primary_key=True)
  first_name = models.CharField(max_length=64)
  last_name = models.CharField(max_length=64)

class Visit(models.Model):
  patient = models.ForeignKey(Patient)
  visit_date = models.DateTimeField()

class Scan(models.Model):
  patient = models.ForeignKey(Patient)
  visit = models.ForeignKey(Visit)
  scan_type = models.CharField(max_length=32)

Thanks a lot for any help...

Upvotes: 2

Views: 1498

Answers (2)

mtnpaul
mtnpaul

Reputation: 704

Two examples only slightly different:

First:

class ScanInlineAdmin(admin.TabularAdmin):
    model = Scan
    formset = ScanInlineFormset

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == "visit":
            patient = self.get_object(kwargs['request'], Patient)
            kwargs["queryset"] = Visit.objects.filter(patient=patient)
        return super(ScanInlineAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)   

    def get_object(self, request, model):
        object_id = request.META['PATH_INFO'].strip('/').split('/')[-1]
        try:
            object_id = int(object_id)
        except ValueError:
            return None
        return model.objects.get(pk=object_id)

Second:

class ScanInline(admin.TabularInline):
    model = Scan
    formset = ScanInlineFormset

    def formfield_for_dbfield(self, field, **kwargs):
        if field.name == 'visit':
            # Note - get_object hasn't been defined yet
            patient = self.get_object(kwargs['request'], Patient)
            vists = Visit.objects.filter(patient=patient)
            return forms.ModelChoiceField(queryset=visits)
        return super(ScanInline, self).formfield_for_dbfield(field, **kwargs)

    def get_object(self, request, model):
        object_id = request.META['PATH_INFO'].strip('/').split('/')[-1]
        try:
            object_id = int(object_id)
        except ValueError:
            return None
        return model.objects.get(pk=object_id)

You may find this article helpful. I assume your in the patient page admin edit/view where you want to enter the new scan. Is that correct?

Upvotes: 1

zaan
zaan

Reputation: 897

I think that formfield_for_foreignkey method will be helpfull for you. You can implement it in admin class. See docs.

Upvotes: 1

Related Questions