Dave Mackey
Dave Mackey

Reputation: 4432

Django: How to navigate multi-level reverse relationships?

I have several models that are related hierarchically:

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.

Upvotes: 0

Views: 204

Answers (1)

preator
preator

Reputation: 1029

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()

Upvotes: 1

Related Questions