vts
vts

Reputation: 911

django get queryset of a object with chained foreign key

I have a project which looks like:

mysite/
|-- books
|   |-- __init__.py
|   `-- models.py
`-- org
    |-- __init__.py
    `-- models.py

books/models.py:

from django.db import models

from mysite.org.models import Team

class BookSet(models.Model):
    name = models.CharField(max_length=10)
    team = models.ForeignKey(Team, related_name='booksets')

class Book(models.Model):
    name = models.CharField(max_length=10)
    book_set = models.ForeignKey(BookSet, related_name='books')

org/models.py:

from django.db import models

class Department(models.Model):
    name = models.CharField(max_length=20)

class Team(models.Model):
    name = models.CharField(max_length=20)
    department = models.ForeignKey(Department, related_name='teams')

What I want to achieve is getting all books for a Department, something similar to:

class Department(models.Model):
    name = models.CharField(max_length=20)

    @property
    def books(self):
        # this won't work, is there any possible do it like this?
        return self.teams.booksets.books()

I know one possible method is doing it via:

from mysite.books.models import Book

class Department(models.Model):
    name = models.CharField(max_length=20)

    @property
    def books(self):
        return Book.objects.filter(book_set__team__department=self)

But with this method, I need to do something tricky to allow them (books.models and org.models) to import each other cyclically.

So I'm wondering whether it's possible to find out all the Books for a Department by querying with the foreign key, which is something like:

    @property
    def books(self):
        # this won't work, is there any possible do it like this?
        return self.teams.booksets.books()

Upvotes: 2

Views: 1946

Answers (3)

domino
domino

Reputation: 2165

You could use the same mechanism that Django uses to load Foreign Keys like

user = models.ForeignKey('User')

How do to that:

from django.db.models.loading import get_model
Book = get_model('books', 'Book')

# which would achieve the same thing as
# from books.models import Book

get_model is defined as:

def get_model(self, app_label, model_name, seed_cache=True): ...

Upvotes: 1

Eric Amorde
Eric Amorde

Reputation: 1068

I think something like this would work:

class Department(models.Model):
    name = models.CharField(max_length=20)

    @property
    def books(self):
        booksets = list(team.booksets.all() for team in self.teams.all())
        return list(bookset.books.all() for bookset in booksets)

But I don't have an easy way to test it.

Edit: An alternative I forgot to add:

You can use strings to refer to models in ForeignKey definitions. like so:

class BookSet(models.Model):
    name = models.CharField(max_length=10)
    team = models.ForeignKey('org.Team', related_name='booksets')

Then in you're other application you can freely import books.models

See the docs for more info.

Upvotes: 2

Hamish
Hamish

Reputation: 23316

An easy way to avoid the cyclical import is by making your your Department model like so:

class Department(models.Model):
    name = models.CharField(max_length=20)

    @property
    def books(self):
        from mysite.books.models import Book
        return Book.objects.filter(book_set__team__department=self)

Upvotes: 3

Related Questions