Django: several ForeignKey relationships in models
Tags: django
Published: 2024-06-28 09:43:34

I have multiple models in my project, namely: `Home`, `Payment`, and `PaymentPlan`. The `Payment` model includes a `ForeignKey` relationship both to the `Home` model and to the `PaymentPlan` model. I need to find a way to access the `name` attribute of the `PaymentPlan` model, starting from an instance of the `Home` model.


```python
from django.db import models

class Home(models.Model):
    # Fields for Home
    address = models.CharField(max_length=255)

class PaymentPlan(models.Model):
    # Fields for PaymentPlan
    name = models.CharField(max_length=255)

class Payment(models.Model):
    # Fields for Payment
    home = models.ForeignKey(Home, related_name='payments', on_delete=models.CASCADE)
    payment_plan = models.ForeignKey(PaymentPlan, related_name='payments', on_delete=models.CASCADE)
    amount = models.DecimalField(max_digits=10, decimal_places=2)
```


```python
# Get the Home instance (modify the filter to match your needs)
home_instance = Home.objects.get(id=1)

# Access related payments
payments = home_instance.payments.all()

# Get the payment plans through payments
for payment in payments:
    payment_plan_name = payment.payment_plan.name
    print(f"Payment Plan Name: {payment_plan_name}")
```



### Explanation:


1. **Get the `Home` instance**:

   - `home_instance = Home.objects.get(id=1)`: Retrieves a specific `Home` instance using its primary key.


2. **Access related `Payment` instances**:

   - `payments = home_instance.payments.all()`: Uses the `related_name` (`payments`) defined in the `Payment` model’s `ForeignKey` to `Home` to retrieve all associated `Payment` instances.


3. **Access the `PaymentPlan` through `Payment`**:

   - For each `Payment` instance, access the related `PaymentPlan` and print its `name`.


### Using Django Shell for Ad-hoc Queries:


You can also work interactively in the Django shell:


```bash
python manage.py shell
```


Inside the shell:


```python
from yourapp.models import Home

# Get the Home instance
home_instance = Home.objects.get(id=1)

# Access related payments and their plans
for payment in home_instance.payments.all():
    payment_plan_name = payment.payment_plan.name
    print(f"Payment Plan Name: {payment_plan_name}")

```


### Utilizing Django Queryset Across Relationships:


If you prefer using queryset chaining to fetch and display payment plan names:


```python
from yourapp.models import Home

# Get the Home instance 
home_instance = Home.objects.get(id=1)

# Fetch payment plans directly
payment_plans = PaymentPlan.objects.filter(payments__home=home_instance).distinct()

# Print payment plan names
for plan in payment_plans:
    print(f"Payment Plan Name: {plan.name}")

```


### Explanation:


- `payments__home=home_instance`: This filters `PaymentPlan` instances through the relationship chain `Payment` -> related to `Home`.

- `.distinct()`: Ensures unique `PaymentPlan` instances, avoiding duplicates if there are multiple `Payments` for the same `PaymentPlan`.


By following this method, you can easily navigate the relationships and extract the `PaymentPlan.name` attribute starting from a `Home` instance using Django's powerful ORM capabilities.