# Path: dynamic_rest/meta.py
# def get_model_field(model, field_name):
#     """Return a field given a model and field name.
# 
#     Arguments:
#         model: a Django model
#         field_name: the name of a field
# 
#     Returns:
#         A Django field if `field_name` is a valid field for `model`,
#             None otherwise.
#     """
#     meta = model._meta
#     try:
#         return meta.get_field(field_name)
#     except BaseException:
#         related_objs = (
#             f for f in meta.get_fields()
#             if (f.one_to_many or f.one_to_one)
#             and f.auto_created and not f.concrete
#         )
#         related_m2m_objs = (
#             f for f in meta.get_fields(include_hidden=True)
#             if f.many_to_many and f.auto_created
#         )
# 
#         related_objects = {
#             o.get_accessor_name(): o
#             for o in chain(related_objs, related_m2m_objs)
#         }
#         if field_name in related_objects:
#             return related_objects[field_name]
#         else:
#             # check virtual fields (1.7)
#             if hasattr(meta, 'virtual_fields'):
#                 for field in meta.virtual_fields:
#                     if field.name == field_name:
#                         return field
# 
#             raise AttributeError(
#                 '%s is not a valid field for %s' % (field_name, model)
#             )
# 
# 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
# 
# Path: tests/models.py
# class Group(models.Model):
#     name = models.TextField(unique=True)
#     permissions = models.ManyToManyField('Permission', related_name='groups')
# 
# class Location(models.Model):
#     name = models.TextField()
#     blob = models.TextField()
# 
# class Profile(models.Model):
#     user = models.OneToOneField(User, on_delete=models.CASCADE)
#     display_name = models.TextField()
#     thumbnail_url = models.TextField(null=True, blank=True)
# 
# class User(models.Model):
#     name = models.TextField()
#     last_name = models.TextField()
#     groups = models.ManyToManyField('Group', related_name='users')
#     permissions = models.ManyToManyField('Permission', related_name='users')
#     date_of_birth = models.DateField(null=True, blank=True)
#     # 'related_name' intentionally left unset in location field below:
#     location = models.ForeignKey(
#         'Location',
#         null=True,
#         blank=True,
#         on_delete=models.CASCADE
#     )
#     favorite_pet_type = models.ForeignKey(
#         ContentType,
#         null=True,
#         blank=True,
#         on_delete=models.CASCADE
#     )
#     favorite_pet_id = models.TextField(null=True, blank=True)
#     favorite_pet = GenericForeignKey(
#         'favorite_pet_type',
#         'favorite_pet_id',
#     )
#     is_dead = models.BooleanField(null=True, default=False)


from django.test import TestCase
from dynamic_rest.meta import (
    get_model_field,
    get_model_field_and_type,
    get_remote_model,
    reverse_m2m_field_name
)
from tests.models import Group, Location, Profile, User



class TestMeta(TestCase):

    def test_get_remote_model(self):
        tests = [
            (Location, 'user_set', User),
            (User, 'location', Location),
            (User, 'profile', Profile),
            (User, 'groups', Group),
            (Group, 'users', User),
            (Profile, 'user', User),
        ]

        for model, field_name, expected in tests:

# Path: dynamic_rest/meta.py
# def get_model_field(model, field_name):
#     """Return a field given a model and field name.
# 
#     Arguments:
#         model: a Django model
#         field_name: the name of a field
# 
#     Returns:
#         A Django field if `field_name` is a valid field for `model`,
#             None otherwise.
#     """
#     meta = model._meta
#     try:
#         return meta.get_field(field_name)
#     except BaseException:
#         related_objs = (
#             f for f in meta.get_fields()
#             if (f.one_to_many or f.one_to_one)
#             and f.auto_created and not f.concrete
#         )
#         related_m2m_objs = (
#             f for f in meta.get_fields(include_hidden=True)
#             if f.many_to_many and f.auto_created
#         )
# 
#         related_objects = {
#             o.get_accessor_name(): o
#             for o in chain(related_objs, related_m2m_objs)
#         }
#         if field_name in related_objects:
#             return related_objects[field_name]
#         else:
#             # check virtual fields (1.7)
#             if hasattr(meta, 'virtual_fields'):
#                 for field in meta.virtual_fields:
#                     if field.name == field_name:
#                         return field
# 
#             raise AttributeError(
#                 '%s is not a valid field for %s' % (field_name, model)
#             )
# 
# 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
# 
# Path: tests/models.py
# class Group(models.Model):
#     name = models.TextField(unique=True)
#     permissions = models.ManyToManyField('Permission', related_name='groups')
# 
# class Location(models.Model):
#     name = models.TextField()
#     blob = models.TextField()
# 
# class Profile(models.Model):
#     user = models.OneToOneField(User, on_delete=models.CASCADE)
#     display_name = models.TextField()
#     thumbnail_url = models.TextField(null=True, blank=True)
# 
# class User(models.Model):
#     name = models.TextField()
#     last_name = models.TextField()
#     groups = models.ManyToManyField('Group', related_name='users')
#     permissions = models.ManyToManyField('Permission', related_name='users')
#     date_of_birth = models.DateField(null=True, blank=True)
#     # 'related_name' intentionally left unset in location field below:
#     location = models.ForeignKey(
#         'Location',
#         null=True,
#         blank=True,
#         on_delete=models.CASCADE
#     )
#     favorite_pet_type = models.ForeignKey(
#         ContentType,
#         null=True,
#         blank=True,
#         on_delete=models.CASCADE
#     )
#     favorite_pet_id = models.TextField(null=True, blank=True)
#     favorite_pet = GenericForeignKey(
#         'favorite_pet_type',
#         'favorite_pet_id',
#     )
#     is_dead = models.BooleanField(null=True, default=False)


# Path: tests/test_meta.py
from django.test import TestCase
from dynamic_rest.meta import (
    get_model_field,
    get_model_field_and_type,
    get_remote_model,
    reverse_m2m_field_name
)
from tests.models import Group, Location, Profile, User




class TestMeta(TestCase):

    def test_get_remote_model(self):
        tests = [
            (Location, 'user_set', User),
            (User, 'location', Location),
            (User, 'profile', Profile),
            (User, 'groups', Group),
            (Group, 'users', User),
            (Profile, 'user', User),
        ]

        for model, field_name, expected in tests:

            remote_model = get_remote_model(