0

I have several models that are related hierarchically:

  • Projects have one or more Experiments
  • Experiments have one or more Scans
  • Scans have one or more Scan Decisions

Simplified Models:

class Project(models.Model):
  id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
  name = models.CharField(max_length=255)

class Experiment(models.Model):
  id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
  name = models.CharField(max_length=255, blank=False)
  project = models.ForeignKey('Project', related_name='experiments', on_delete=models.CASCADE)

class Scan(models.Model):
  id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
  name = models.CharField(max_length=127, blank=False)
  experiment = models.ForeignKey('Experiment', related_name='scans', on_delete=models.CASCADE)

class ScanDecision(models.Model):
  id = models.UUIDField(primary_key=true, default=uuid4, editable=False)
  scan = models.ForeignKey('Scan', related_name='decisions', on_delete=models.CASCADE)

If I have a specific Scan Decision and I want to get the Project associated with that decision, how can I do so?

The Django documentation speaks of backward relationships but the examples seem limited to a single level of relationship.

For example, to get the ScanDecision associated with a Scan I could do something like:

sd = ScanDecision.objects.get(id=1)
sd.scan_set.all() # Returns all scan objects related to the specific ScanDecision

But what if I want to get the Project that is associated with the ScanDecision indirectly through Scan and Experiment?

e.g., something like this but without all the steps?

sd = ScanDecision.objects.get(id=1) # The desired ScanDecision object
s = sd.scan_set.all() # Gets the related Scan object
e = s.experiment_set.all() # Gets the Experiment related to the selected Scan object
p = e.project_set.all() # Gets the Project related to the selected Experiment object

Ideally I'd like something like:

sd = ScanDecision.objects.get(id=1)
p = sd.project_set.all()

Note: Each object only knows about it's immediately antecedent object, e.g. there is an ForeignKey relationship setup only between the child and parent objects, not any other levels. Thus ScanDecision has an FK to Scan but not to Experiment or Project. Similarly, Scan has an FK to Experiment but not to Project.

Dave Mackey
  • 4,306
  • 21
  • 78
  • 136
  • 1
    Can you add your models to the question, just with the relevant relationship fields? If `ScanDecision` has a foreign key to `Scan` then use that field on a `ScanDecision` instance to access the related `Scan`, same with all the other FKs, `sd.scan.experiment.project` for example – Iain Shelvington Aug 22 '22 at 15:50
  • I've updated the the question to include simplified models. So, since there are FK's between the various models I can use `sd.scan.experiment.project` and it should return the correct project? – Dave Mackey Aug 22 '22 at 16:02
  • 1
    yes, exactly `sd.scan.experiment.project` – preator Aug 22 '22 at 16:03
  • 1
    @DaveMackey yes, you are following multiple foreign key relationships forwards. You might want to look at [`select_related`](https://docs.djangoproject.com/en/4.1/ref/models/querysets/#select-related) to fetch all the related objects in a single query to improve performance, otherwise a query will execute every time you access a foreign key – Iain Shelvington Aug 22 '22 at 16:06

1 Answers1

1

Based on your update you can use forward relationship sd.scan.experiment.project. To answer the question though to access backward relationship you could do decisions = project.experiments.scans.decisions.all()

preator
  • 984
  • 5
  • 6