Azer
Azer

Reputation: 1288

Django: Query multiple models based on parent model

I'm creating a blog in Django where I have a base model PostType which I then extend in to several subclasses for different types of content on the website. For example CodeSnippet and BlogPost.

The idea is that these content types are mostly the same, they all have an author, a title, a slug, etc, but they also have a few unique fields. For example a blog post has a field for the text content, while a code snippet has a related field for programming language.

Something like this:

class PostType(models.Model):
    author = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE
    )
    title = models.CharField(
        max_length=255,
        unique=True,
    )

    class Meta:
        abstract = True


class BlogPost(PostType):
    content = models.TextField(
        default='',
    )


class GitHubRepo(PostType):
    url = models.URLField(
        unique=True
    )


class CodeSnippet(PostType):
    language = models.ForeignKey(
        to=Language,
        on_delete=models.CASCADE,
    )

Now what I want to know is if there's any good/prefered way to query all objects in the database that are based on the parent class PostType?

For the site's search I am currently querying each of the different content types, and then merging the result. This is the code for the search view:

class Search(View):
    def get(self, request):
        context = {}
        try:
            query = request.GET.get('s')
            blog_list = models.BlogPost.objects.filter(title__icontains=query)
            projects_list = models.Project.objects.filter(title__icontains=query)
            code_list = models.CodeSnippet.objects.filter(title__icontains=query)
            from itertools import chain
            context['result_list'] = list(chain(blog_list, projects_list, code_list))

        except KeyError:
            query = ''

        context['title'] = 'Result for "{}"'.format(query)

        return render(request, 'blog/search.html', context)

This all works fine, but I would like to know if there's any way to query all children of PostType at the same time?

Is Django somehow aware of what child models exist? And can I use that somehow?

Like a PostType.child_objects.get() or something similar. Even a way to programmatically get all the children so that I could loop through them and get all the objects would be fine too.

For the time being I just have a few models, but the number of child models were to increase, it would be great if I could be assured that all the models would be included in the site search automatically based on their relationship to their parent model.

Upvotes: 1

Views: 1232

Answers (2)

gachdavit
gachdavit

Reputation: 1261

PostType is an abstract Model (So, it does not create physical table. It's just to use inheritance feature in Django). As far as i understand you want to generate list of QuerySet's merge it in a single list and iterate over list/QuerySet later.

get_qs_list = [model.objects.filter(title__icontains=query) for model in PostType.__subclasses__()] # This contains QuerySet instances now.
for qs in get_qs_list:
    # qs iterator returns QuerySet instance
    for instance in qs:
        # instance iterator is single Model instance from QuerySet collection
        print(instance.title)

Hope, it helps you.

Upvotes: 3

NS0
NS0

Reputation: 6096

If PostType is not an abstract model then you should be able to query it directly to get all those subclass results

PostType.objects.filter(title__icontains=query)

Otherwise, you cannot really do this with a single query.

Even a way to programmatically get all the children so that I could loop through them and get all the objects would be fine too.

This is possible --- to get the subclasses programmatically, you would do

PostType.__subclasses__()

Upvotes: 2

Related Questions