I'm offering services to companies (many-to-many between Service and Company). To structure my services, I use a tree-like structure using Django's many-to-many field. So, a service can contain multiple other services.
The question: how can I retrieve all services a company has? Using company.services, I only get the services directly related to that company. I need the directly related ones + included_services (in a recursive way).
class Company(models.Model):
services = models.ManyToManyField(Service)
class Service(models.Model):
name = models.CharField(max_length=255)
included_services = models.ManyToManyField("self", blank=True, symmetrical=False)
I don't think you can do this using Django's ORM framework or writing a plain raw SQL statement. You might want to take a look at 3rd party libraries like django-mptt that enables you to store/retrieve models represented in tree-like structures more efficiently.
However, if you want to do it via Python, here is what you can do:
def get_services(company):
services = list(company.services.all())
result = []
while services:
service = services.pop(0)
result.append(service)
services.extend(list(service.included_services.all()))
return result
The above idea is very similar to performing a breadth-first search in a tree.
I forgot to mention that I needed a QuerySet as return value, so I modified @ozgur's exellent answer a bit. Just in case someone also needs this:
def get_services(company):
services = list(company.services.all())
pks = []
while services:
service = services.pop(0)
if service.pk not in pks:
pks.append(service.pk)
services.extend(list(service.included_services.all()))
return Service.objects.filter(pk__in=pks)
EDIT: I also added a check for potential loops.