# Path: dynamic_rest/meta.py
# def get_model_field_and_type(model, field_name):
#     field = get_model_field(model, field_name)
# 
#     # Django 1.7 (and 1.8?)
#     if isinstance(field, RelatedObject):
#         if isinstance(field.field, OneToOneField):
#             return field, 'o2or'
#         elif isinstance(field.field, ManyToManyField):
#             return field, 'm2m'
#         elif isinstance(field.field, ForeignKey):
#             return field, 'm2o'
#         else:
#             raise RuntimeError("Unexpected field type")
# 
#     # Django 1.9
#     type_map = [
#         (OneToOneField, 'o2o'),
#         (OneToOneRel, 'o2or'),  # is subclass of m2o so check first
#         (ManyToManyField, 'm2m'),
#         (ManyToOneRel, 'm2o'),
#         (ManyToManyRel, 'm2m'),
#         (ForeignKey, 'fk'),  # check last
#     ]
#     for cls, type_str in type_map:
#         if isinstance(field, cls):
#             return field, type_str,
# 
#     return field, '',
# 
# def get_remote_model(field):
#     try:
#         # Django 1.9
#         return field.remote_field.model
#     except BaseException:
#         # Django 1.7
#         if hasattr(field, 'field'):
#             return field.field.model
#         elif hasattr(field, 'rel'):
#             return field.rel.to
#         elif field.__class__.__name__ == 'GenericForeignKey':
#             return None
#         else:
#             raise
# 
# def reverse_m2m_field_name(m2m_field):
#     try:
#         # Django 1.9
#         return m2m_field.remote_field.name
#     except BaseException:
#         # Django 1.7
#         if hasattr(m2m_field, 'rel'):
#             return m2m_field.rel.related_name
#         elif hasattr(m2m_field, 'field'):
#             return m2m_field.field.name
#         elif m2m_field.__class__.__name__ == 'GenericForeignKey':
#             return None
#         else:
#             raise
# 
# def reverse_o2o_field_name(o2or_field):
#     try:
#         # Django 1.9
#         return o2or_field.remote_field.attname
#     except BaseException:
#         # Django 1.7
#         return o2or_field.field.attname


from collections import defaultdict
from django.db import models
from django.db.models import Prefetch, QuerySet
from dynamic_rest.meta import (
    get_model_field_and_type,
    get_remote_model,
    reverse_m2m_field_name,
    reverse_o2o_field_name
)
import copy
import traceback
    def merge_fk(self, data, field, prefetch):
        # Strategy: pull out field_id values from each row, pass to
        #           prefetch queryset using `pk__in`.

        id_field = field.attname
        ids = set([
            row[id_field] for row in data if id_field in row
        ])
        prefetched_data = prefetch.query.get_ids(ids).execute()
        id_map = self._make_id_map(prefetched_data)

        for row in data:
            row[field.name] = id_map.get(row[id_field], None)

        return data

    def merge_o2o(self, data, field, prefetch):
        # Same as FK.
        return self.merge_fk(data, field, prefetch)

    def merge_o2or(self, data, field, prefetch, m2o_mode=False):
        # Strategy: get my IDs, filter remote model for rows pointing at
        #           my IDs.
        #           For m2o_mode, account for there many objects, while
        #           for o2or only support one reverse object.

        my_ids = self._get_my_ids(data)

        # If prefetching User.profile, construct filter like:
        #   Profile.objects.filter(user__in=<user_ids>)

# Path: dynamic_rest/meta.py
# def get_model_field_and_type(model, field_name):
#     field = get_model_field(model, field_name)
# 
#     # Django 1.7 (and 1.8?)
#     if isinstance(field, RelatedObject):
#         if isinstance(field.field, OneToOneField):
#             return field, 'o2or'
#         elif isinstance(field.field, ManyToManyField):
#             return field, 'm2m'
#         elif isinstance(field.field, ForeignKey):
#             return field, 'm2o'
#         else:
#             raise RuntimeError("Unexpected field type")
# 
#     # Django 1.9
#     type_map = [
#         (OneToOneField, 'o2o'),
#         (OneToOneRel, 'o2or'),  # is subclass of m2o so check first
#         (ManyToManyField, 'm2m'),
#         (ManyToOneRel, 'm2o'),
#         (ManyToManyRel, 'm2m'),
#         (ForeignKey, 'fk'),  # check last
#     ]
#     for cls, type_str in type_map:
#         if isinstance(field, cls):
#             return field, type_str,
# 
#     return field, '',
# 
# def get_remote_model(field):
#     try:
#         # Django 1.9
#         return field.remote_field.model
#     except BaseException:
#         # Django 1.7
#         if hasattr(field, 'field'):
#             return field.field.model
#         elif hasattr(field, 'rel'):
#             return field.rel.to
#         elif field.__class__.__name__ == 'GenericForeignKey':
#             return None
#         else:
#             raise
# 
# def reverse_m2m_field_name(m2m_field):
#     try:
#         # Django 1.9
#         return m2m_field.remote_field.name
#     except BaseException:
#         # Django 1.7
#         if hasattr(m2m_field, 'rel'):
#             return m2m_field.rel.related_name
#         elif hasattr(m2m_field, 'field'):
#             return m2m_field.field.name
#         elif m2m_field.__class__.__name__ == 'GenericForeignKey':
#             return None
#         else:
#             raise
# 
# def reverse_o2o_field_name(o2or_field):
#     try:
#         # Django 1.9
#         return o2or_field.remote_field.attname
#     except BaseException:
#         # Django 1.7
#         return o2or_field.field.attname


# Path: dynamic_rest/prefetch.py
from collections import defaultdict
from django.db import models
from django.db.models import Prefetch, QuerySet
from dynamic_rest.meta import (
    get_model_field_and_type,
    get_remote_model,
    reverse_m2m_field_name,
    reverse_o2o_field_name
)
import copy
import traceback

    def merge_fk(self, data, field, prefetch):
        # Strategy: pull out field_id values from each row, pass to
        #           prefetch queryset using `pk__in`.

        id_field = field.attname
        ids = set([
            row[id_field] for row in data if id_field in row
        ])
        prefetched_data = prefetch.query.get_ids(ids).execute()
        id_map = self._make_id_map(prefetched_data)

        for row in data:
            row[field.name] = id_map.get(row[id_field], None)

        return data

    def merge_o2o(self, data, field, prefetch):
        # Same as FK.
        return self.merge_fk(data, field, prefetch)

    def merge_o2or(self, data, field, prefetch, m2o_mode=False):
        # Strategy: get my IDs, filter remote model for rows pointing at
        #           my IDs.
        #           For m2o_mode, account for there many objects, while
        #           for o2or only support one reverse object.

        my_ids = self._get_my_ids(data)

        # If prefetching User.profile, construct filter like:
        #   Profile.objects.filter(user__in=<user_ids>)

        remote_field = reverse_o2o_field_name(field)