#!/usr/bin/env bash
set -eu
python -m venv nested_prefetch
cd nested_prefetch
. bin/activate
pip install Django==2.0.4
django-admin startproject nested_prefetch
cd nested_prefetch
./manage.py startapp a1
cat <<END >> nested_prefetch/settings.py
INSTALLED_APPS.append('a1')
END
cat >> a1/models.py <<END
class M1(models.Model):
    name = models.CharField(max_length=255)

class M3(models.Model):
    name = models.CharField(max_length=255)
    m1s = models.ManyToManyField(M1, through='M2', related_name='m3s', related_query_name='m3')

class M2(models.Model):
    m1 = models.ForeignKey(M1, related_name='m2s', related_query_name='m2',
        on_delete=models.CASCADE)
    m3 = models.ForeignKey(M3, related_name='m2s', related_query_name='m2',
        on_delete=models.CASCADE)
    name = models.CharField(max_length=255)

class M4(models.Model):
    name = models.CharField(max_length=255)
    m3 = models.ForeignKey(M3, related_name='m4s', related_query_name='m4',
        on_delete=models.CASCADE)
END
./manage.py makemigrations
./manage.py migrate
cat > 1.py <<END
import os, django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "nested_prefetch.settings")
django.setup()

from django.db.models import Prefetch
from a1.models import M1, M2, M3, M4

m11 = M1.objects.create(name='m11')
m31 = M3.objects.create(name='m31')
m21_11 = M2.objects.create(m1=m11, m3=m31, name='m21_11')
m32 = M3.objects.create(name='m32')
m22_12 = M2.objects.create(m1=m11, m3=m32, name='m22_12')
m411 = M4.objects.create(name='m411', m3=m31)
m412 = M4.objects.create(name='m412', m3=m31)
m421 = M4.objects.create(name='m421', m3=m32)
m422 = M4.objects.create(name='m422', m3=m32)

from django.conf import settings
from django.utils.module_loading import import_string
logging_config_func = import_string(settings.LOGGING_CONFIG)
logging_config_func({
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'django.server': {
            '()': 'django.utils.log.ServerFormatter',
            'format': '[%(server_time)s] %(message)s',
        }
    },
    'handlers': {
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'level': 'DEBUG',
            'propagate': False,
        },
    }
})

m1s = M1.objects.all() \
    .prefetch_related(
        Prefetch('m2s', queryset=M2.objects.prefetch_related('m3__m4s'))
    )

# m1s = M1.objects.all() \
#     .prefetch_related('m2s', 'm2s__m3__m4s')

indent = '  '
for m1 in m1s:
    print(m1.name)
    for m2 in m1.m2s.all():
        print('{}{} (through {})'.format(indent * 2, m2.m3.name, m2.name))
        for m4 in m2.m3.m4s.all():
            print('{}{}'.format(indent * 3, m4.name))
END

echo Now run:
echo cd nested_prefetch/nested_prefetch
echo . ../bin/activate
echo ./manage.py migrate a1 zero \&\& ./manage.py migrate a1 \&\& python 1.py
