Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to exclude instances with empty querysets in prefetch_related?

I have two models – Project and Contract.
They have one-to-many relationship.

class Contract(models.Model):
    project = models.ForeignKey(Project)

I get a qs of Project instances with relevant Contract instance(s).

projects = Project.objects.filter(active=True).prefetch_related(
        Prefetch('contract_set', queryset=Contract.objects.filter(**filters), to_attr='contracts')
    )

But now I need to get Project instances with exact Contract instance by putting Contract guid to filters.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

filters: Dict = {
  'guid': some_guid,
}

But I receive all Project instances where only one has not empty contracts attr and others Project instances have empty contracts attr.

I found such issues:
How to exclude rows with empty prefetch_related field
Filter prefetch_related empty in django
but they do not help me.

I tried such options:
a) using OuterRef and Exist:

projects = Project.objects.filter(**filters_projects).annotate(
    has_contracts=Exists(
        Contract.objects.filter(project_id=OuterRef('pk'), **filters_contracts)
    )
)

projects = projects.filter(has_contracts=True)

b) using annotate:

projects = (
        Project.objects.filter(**filters_projects)
        .annotate(num_contracts=Count('contract'))
        .exclude(num_contracts=0)
        .prefetch_related(
            Prefetch('contract_set', queryset=Contract.objects.filter(**filters_contracts), to_attr='contracts')
        )
    )

They do not work for me…

How can I implement the required functionality?

>Solution :

You will have to filter both in the Prefetch object and the Project, so:

projects = (
    Project.objects.filter(
        Exists(
            Contract.objects.filter(
                project_id=OuterRef('pk'), **filters_contracts
            )
        ),
        **filters_projects
    )
    .prefetch_related(
        Prefetch(
            'contract_set',
            queryset=Contract.objects.filter(**filters_contracts),
            to_attr='contracts',
        )
    )
    .distinct()
)

So you use the **filters_contracts twice: once to only retrieve these Projects once, and once for the prefetched object.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading