Python/Django ORM filter by ManyToMany relationship where one object matches

409 Views Asked by At

I can't wrap my head around how to filter using Django ORM where ForeignKeys and ManyToMany fields are involved. I've done a ton of googling but can't find a similar case. Django documentation isn't helping much either.

Here's the situation. I have apps. An app can have multiple releases. These releases have metadata, such as the platform versions they're compatible with. A release can be compatible with multiple platform versions. I want to find the latest release that's compatible with a specific platform version.

So in Django, I have an App model and I have a Release model, with App being a ForeignKey. I also have a PlatformVersion model. On the Release model I have PlatformVersion as a ManyToManyField.

I want to create a method on the App model that gives me the latest Release that is compatible with a specific PlatformVersion. It can be compatible with other PlatformVersions too, I don't care. But it needs to at least be compatible with that one PlatformVersion.

Here's some code:

class App(models.Model):

    def get_latest_filtered_release(self, **kwargs):
        platform_version = PlatformVersion.objects.get(name=kwargs.get('platform_version', None)

        return self.releases.filter(platform_versions__in=platform_version).latest()
        # When I try to run this I get "TypeError: 'PlatformVersion' object is not iterable" as expected 


class Release(models.Model):

    class Meta:
        ordering = ['-created_datetime']
        get_latest_by = 'created_datetime'

    app = models.ForeignKey(App, related_name='releases')
    platform_versions = models.ManyToManyField('PlatformVersion', related_name="platform_versions")
    created_datetime = models.DateTimeField(auto_now_add=True)


class PlatformVersion(models.Model):

    name = models.CharField(unique=True)

Release has multiple PlatformVersions so I can't filter using the = operator (platform_versions=a_single_platform_version).

I can't use __in either (platform_versions__in=a_single_platform_version) because that means "give me releases where the value of release.platform_versions is in a_single_platform_version (a list)". I want the reverse: "give me releases where release.platform_versions (a list) contains the value of a_single_platform_version".

__contains is unfortunately just for strings, not for evaluating if a value exists in a list. I get a Related Field got invalid lookup: contains error when I try to use __contains.

What do I do? Subqueries? Exclude? Is there a filter I'm missing? Something else? Thanks!

1

There are 1 best solutions below

2
On

Release has multiple PlatformVersions so I can't filter using the = operator (platform_versions=a_single_platform_version).

This is in fact the right way to do it, equality comparisons on a many-to-many field mean that the specified value has to be present but allows other values.