From 5a72847b766e3e58b8002d478040274503d5c8a5 Mon Sep 17 00:00:00 2001 From: Pierre Dulac Date: Fri, 16 Oct 2015 15:24:40 +0200 Subject: [PATCH 1/5] Use backward compatible implementation for the meta api calls --- rest_framework/utils/model_meta.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/rest_framework/utils/model_meta.py b/rest_framework/utils/model_meta.py index d16630ed67..4027f422f4 100644 --- a/rest_framework/utils/model_meta.py +++ b/rest_framework/utils/model_meta.py @@ -126,7 +126,15 @@ def _get_reverse_relationships(opts): # See: https://code.djangoproject.com/ticket/24208 reverse_relations = OrderedDict() - for relation in opts.get_all_related_objects(): + + # The backward implementation can be found in the Django Documentation + # See: https://docs.djangoproject.com/en/1.9/ref/models/meta/#migrating-from-the-old-api + related_objects = [ + f for f in opts.get_fields() + if (f.one_to_many or f.one_to_one) and f.auto_created + ] + + for relation in related_objects: accessor_name = relation.get_accessor_name() related = getattr(relation, 'related_model', relation.model) reverse_relations[accessor_name] = RelationInfo( @@ -136,8 +144,15 @@ def _get_reverse_relationships(opts): has_through_model=False ) + # The backward implementation can be found in the Django Documentation + # See: https://docs.djangoproject.com/en/1.9/ref/models/meta/#migrating-from-the-old-api + all_related_to_many_objects = [ + f for f in opts.get_fields(include_hidden=True) + if f.many_to_many and f.auto_created + ] + # Deal with reverse many-to-many relationships. - for relation in opts.get_all_related_many_to_many_objects(): + for relation in all_related_to_many_objects: accessor_name = relation.get_accessor_name() related = getattr(relation, 'related_model', relation.model) reverse_relations[accessor_name] = RelationInfo( From 2cc3b417fae33cdd2f9c8ccbd00e22651a0c09ee Mon Sep 17 00:00:00 2001 From: Pierre Dulac Date: Fri, 16 Oct 2015 15:29:07 +0200 Subject: [PATCH 2/5] Fix the indentation --- rest_framework/utils/model_meta.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rest_framework/utils/model_meta.py b/rest_framework/utils/model_meta.py index 4027f422f4..e51b57b37c 100644 --- a/rest_framework/utils/model_meta.py +++ b/rest_framework/utils/model_meta.py @@ -147,7 +147,7 @@ def _get_reverse_relationships(opts): # The backward implementation can be found in the Django Documentation # See: https://docs.djangoproject.com/en/1.9/ref/models/meta/#migrating-from-the-old-api all_related_to_many_objects = [ - f for f in opts.get_fields(include_hidden=True) + f for f in opts.get_fields(include_hidden=True) if f.many_to_many and f.auto_created ] From 0a89478972867d1d5527bfd41d2919d5e753b02c Mon Sep 17 00:00:00 2001 From: Pierre Dulac Date: Fri, 16 Oct 2015 15:37:26 +0200 Subject: [PATCH 3/5] Use compat implementations for the old meta api methods --- rest_framework/compat.py | 24 ++++++++++++++++++++++++ rest_framework/utils/model_meta.py | 23 ++++++----------------- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 1ef3e1751e..43ee2a1f04 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -71,6 +71,30 @@ def distinct(queryset, base): JSONField = None +# Models Options old meta api +# Django 1.8 introduced the `Options.get_fields` method that can be used to +# implement *old* meta api methods +# See: https://docs.djangoproject.com/en/1.9/ref/models/meta/#migrating-from-the-old-api +if django.VERSION >= (1, 8): + def get_all_related_objects(opts): + return [ + f for f in opts.get_fields() + if (f.one_to_many or f.one_to_one) and f.auto_created + ] + + def get_all_related_many_to_many_objects(opts): + return [ + f for f in opts.get_fields(include_hidden=True) + if f.many_to_many and f.auto_created + ] +else: + def get_all_related_objects(opts): + return opts.get_all_related_objects() + + def get_all_related_many_to_many_objects(opts): + return opts.get_all_related_many_to_many_objects() + + # django-filter is optional try: import django_filters diff --git a/rest_framework/utils/model_meta.py b/rest_framework/utils/model_meta.py index e51b57b37c..ae0df2fb6d 100644 --- a/rest_framework/utils/model_meta.py +++ b/rest_framework/utils/model_meta.py @@ -13,6 +13,10 @@ from django.db import models from django.utils import six +from rest_framework.compat import ( + get_all_related_objects, get_all_related_many_to_many_objects +) + FieldInfo = namedtuple('FieldResult', [ 'pk', # Model field instance 'fields', # Dict of field name -> model field instance @@ -126,15 +130,7 @@ def _get_reverse_relationships(opts): # See: https://code.djangoproject.com/ticket/24208 reverse_relations = OrderedDict() - - # The backward implementation can be found in the Django Documentation - # See: https://docs.djangoproject.com/en/1.9/ref/models/meta/#migrating-from-the-old-api - related_objects = [ - f for f in opts.get_fields() - if (f.one_to_many or f.one_to_one) and f.auto_created - ] - - for relation in related_objects: + for relation in get_all_related_objects(opts): accessor_name = relation.get_accessor_name() related = getattr(relation, 'related_model', relation.model) reverse_relations[accessor_name] = RelationInfo( @@ -144,15 +140,8 @@ def _get_reverse_relationships(opts): has_through_model=False ) - # The backward implementation can be found in the Django Documentation - # See: https://docs.djangoproject.com/en/1.9/ref/models/meta/#migrating-from-the-old-api - all_related_to_many_objects = [ - f for f in opts.get_fields(include_hidden=True) - if f.many_to_many and f.auto_created - ] - # Deal with reverse many-to-many relationships. - for relation in all_related_to_many_objects: + for relation in get_all_related_many_to_many_objects(opts): accessor_name = relation.get_accessor_name() related = getattr(relation, 'related_model', relation.model) reverse_relations[accessor_name] = RelationInfo( From f172f0d43f49001698de0fb0bb3a7a0cd750bee0 Mon Sep 17 00:00:00 2001 From: Pierre Dulac Date: Fri, 16 Oct 2015 16:21:59 +0200 Subject: [PATCH 4/5] Fix the compatibility with the OneToOneField class --- rest_framework/compat.py | 15 +++++++++++++++ rest_framework/utils/model_meta.py | 17 +++++++++++------ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 43ee2a1f04..178339a365 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -95,6 +95,21 @@ def get_all_related_many_to_many_objects(opts): return opts.get_all_related_many_to_many_objects() +# Compatibility for the *field* instance returned by either +# the old `Options.get_all_related_objects` or our own implementation above +def get_relation_accessor_name(relation): + if not hasattr(relation, 'get_accessor_name'): + # special case for the `OneToOneField` instances + return relation.name + return relation.get_accessor_name() + +def get_relation_field(relation): + if not hasattr(relation, 'get_accessor_name'): + # special case for the `OneToOneField` instances + return relation + return relation.field + + # django-filter is optional try: import django_filters diff --git a/rest_framework/utils/model_meta.py b/rest_framework/utils/model_meta.py index ae0df2fb6d..ae770392aa 100644 --- a/rest_framework/utils/model_meta.py +++ b/rest_framework/utils/model_meta.py @@ -14,7 +14,10 @@ from django.utils import six from rest_framework.compat import ( - get_all_related_objects, get_all_related_many_to_many_objects + get_all_related_objects, + get_all_related_many_to_many_objects, + get_relation_accessor_name, + get_relation_field ) FieldInfo = namedtuple('FieldResult', [ @@ -131,26 +134,28 @@ def _get_reverse_relationships(opts): reverse_relations = OrderedDict() for relation in get_all_related_objects(opts): - accessor_name = relation.get_accessor_name() + accessor_name = get_relation_accessor_name(relation) + field = get_relation_field(relation) related = getattr(relation, 'related_model', relation.model) reverse_relations[accessor_name] = RelationInfo( model_field=None, related_model=related, - to_many=relation.field.rel.multiple, + to_many=field.rel.multiple, has_through_model=False ) # Deal with reverse many-to-many relationships. for relation in get_all_related_many_to_many_objects(opts): - accessor_name = relation.get_accessor_name() + accessor_name = get_relation_accessor_name(relation) + field = get_relation_field(relation) related = getattr(relation, 'related_model', relation.model) reverse_relations[accessor_name] = RelationInfo( model_field=None, related_model=related, to_many=True, has_through_model=( - (getattr(relation.field.rel, 'through', None) is not None) and - not relation.field.rel.through._meta.auto_created + (getattr(field.rel, 'through', None) is not None) and + not field.rel.through._meta.auto_created ) ) From 597b947c79850aaa9e6048ebb71183a8e6f20634 Mon Sep 17 00:00:00 2001 From: Pierre Dulac Date: Fri, 16 Oct 2015 16:24:42 +0200 Subject: [PATCH 5/5] Fix the import sorting --- rest_framework/utils/model_meta.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/rest_framework/utils/model_meta.py b/rest_framework/utils/model_meta.py index ae770392aa..161eb30976 100644 --- a/rest_framework/utils/model_meta.py +++ b/rest_framework/utils/model_meta.py @@ -14,10 +14,8 @@ from django.utils import six from rest_framework.compat import ( - get_all_related_objects, - get_all_related_many_to_many_objects, - get_relation_accessor_name, - get_relation_field + get_all_related_many_to_many_objects, get_all_related_objects, + get_relation_accessor_name, get_relation_field ) FieldInfo = namedtuple('FieldResult', [