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!
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.