From 20b621eb3c3dd947a4fe77a2a11264b9072ef25c Mon Sep 17 00:00:00 2001 From: Loic Bistuer Date: Sat, 14 Feb 2015 01:55:36 +0700 Subject: [PATCH] [1.8.x] Fixed #24289 -- Reversed usage of Field.many_to_one and one_to_many. Thanks Carl Meyer and Tim Graham for the reviews and to all involved in the discussion. Backport of 18c0aaa9123579375294fcc4a8ee7e3530176b88 from master --- django/contrib/admin/utils.py | 2 +- django/contrib/contenttypes/fields.py | 8 ++++---- django/db/models/deletion.py | 2 +- django/db/models/fields/related.py | 8 ++++---- django/db/models/options.py | 6 +++--- docs/ref/models/fields.txt | 20 ++++++++++---------- docs/ref/models/meta.txt | 10 +++++----- tests/model_fields/test_field_flags.py | 12 ++++++------ 8 files changed, 34 insertions(+), 34 deletions(-) diff --git a/django/contrib/admin/utils.py b/django/contrib/admin/utils.py index 00d8b41129..5de0f09a48 100644 --- a/django/contrib/admin/utils.py +++ b/django/contrib/admin/utils.py @@ -296,7 +296,7 @@ def _get_non_gfk_field(opts, name): "not found" by get_field(). This could likely be cleaned up. """ field = opts.get_field(name) - if field.is_relation and field.one_to_many and not field.related_model: + if field.is_relation and field.many_to_one and not field.related_model: raise FieldDoesNotExist() return field diff --git a/django/contrib/contenttypes/fields.py b/django/contrib/contenttypes/fields.py index 7bd9b79415..199abc165a 100644 --- a/django/contrib/contenttypes/fields.py +++ b/django/contrib/contenttypes/fields.py @@ -27,8 +27,8 @@ class GenericForeignKey(object): is_relation = True many_to_many = False - many_to_one = False - one_to_many = True + many_to_one = True + one_to_many = False one_to_one = False related_model = None @@ -258,8 +258,8 @@ class GenericRelation(ForeignObject): auto_created = False many_to_many = False - many_to_one = True - one_to_many = False + many_to_one = False + one_to_many = True one_to_one = False def __init__(self, to, **kwargs): diff --git a/django/db/models/deletion.py b/django/db/models/deletion.py index 431b15b0ef..02b8cf3c27 100644 --- a/django/db/models/deletion.py +++ b/django/db/models/deletion.py @@ -65,7 +65,7 @@ def get_candidate_relations_to_delete(opts): # N-N (i.e., many-to-many) relations aren't candidates for deletion. return ( f for f in candidate_model_fields - if f.auto_created and not f.concrete and (f.one_to_one or f.many_to_one) + if f.auto_created and not f.concrete and (f.one_to_one or f.one_to_many) ) diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index 922d8b4bd0..a8e4a3156d 100644 --- a/django/db/models/fields/related.py +++ b/django/db/models/fields/related.py @@ -1475,8 +1475,8 @@ class ManyToManyRel(ForeignObjectRel): class ForeignObject(RelatedField): # Field flags many_to_many = False - many_to_one = False - one_to_many = True + many_to_one = True + one_to_many = False one_to_one = False requires_unique_target = True @@ -1785,8 +1785,8 @@ class ForeignObject(RelatedField): class ForeignKey(ForeignObject): # Field flags many_to_many = False - many_to_one = False - one_to_many = True + many_to_one = True + one_to_many = False one_to_one = False empty_strings_allowed = False diff --git a/django/db/models/options.py b/django/db/models/options.py index 0b669de121..8920b5d86c 100644 --- a/django/db/models/options.py +++ b/django/db/models/options.py @@ -388,9 +388,9 @@ class Options(object): # and all the models may not have been loaded yet; we don't want to cache # the string reference to the related_model. is_not_an_m2m_field = lambda f: not (f.is_relation and f.many_to_many) - is_not_a_generic_relation = lambda f: not (f.is_relation and f.many_to_one) + is_not_a_generic_relation = lambda f: not (f.is_relation and f.one_to_many) is_not_a_generic_foreign_key = lambda f: not ( - f.is_relation and f.one_to_many and not (hasattr(f.rel, 'to') and f.rel.to) + f.is_relation and f.many_to_one and not (hasattr(f.rel, 'to') and f.rel.to) ) return make_immutable_fields_list( "fields", @@ -564,7 +564,7 @@ class Options(object): for field in fields: # For backwards compatibility GenericForeignKey should not be # included in the results. - if field.is_relation and field.one_to_many and field.related_model is None: + if field.is_relation and field.many_to_one and field.related_model is None: continue names.add(field.name) diff --git a/docs/ref/models/fields.txt b/docs/ref/models/fields.txt index f4fe52a737..ee82c315a1 100644 --- a/docs/ref/models/fields.txt +++ b/docs/ref/models/fields.txt @@ -1863,16 +1863,6 @@ relation. These attribute are present on all fields; however, they will only have meaningful values if the field is a relation type (:attr:`Field.is_relation=True `). -.. attribute:: Field.one_to_many - - Boolean flag that is ``True`` if the field has a one-to-many relation, such - as a ``ForeignKey``; ``False`` otherwise. - -.. attribute:: Field.one_to_one - - Boolean flag that is ``True`` if the field has a one-to-one relation, such - as a ``OneToOneField``; ``False`` otherwise. - .. attribute:: Field.many_to_many Boolean flag that is ``True`` if the field has a many-to-many relation; @@ -1882,9 +1872,19 @@ have meaningful values if the field is a relation type .. attribute:: Field.many_to_one Boolean flag that is ``True`` if the field has a many-to-one relation, such + as a ``ForeignKey``; ``False`` otherwise. + +.. attribute:: Field.one_to_many + + Boolean flag that is ``True`` if the field has a one-to-many relation, such as a ``GenericRelation`` or the reverse of a ``ForeignKey``; ``False`` otherwise. +.. attribute:: Field.one_to_one + + Boolean flag that is ``True`` if the field has a one-to-one relation, such + as a ``OneToOneField``; ``False`` otherwise. + .. attribute:: Field.related_model Points to the model the field relates to. For example, ``Author`` in diff --git a/docs/ref/models/meta.txt b/docs/ref/models/meta.txt index fdad7b2ad0..ec088e64fa 100644 --- a/docs/ref/models/meta.txt +++ b/docs/ref/models/meta.txt @@ -213,7 +213,7 @@ can be made to convert your code to the new API: for f in MyModel._meta.get_fields() if not f.is_relation or f.one_to_one - or (f.one_to_many and f.related_model) + or (f.many_to_one and f.related_model) ] * ``MyModel._meta.get_concrete_fields_with_model()``:: @@ -224,7 +224,7 @@ can be made to convert your code to the new API: if f.concrete and ( not f.is_relation or f.one_to_one - or (f.one_to_many and f.related_model) + or (f.many_to_one and f.related_model) ) ] @@ -240,7 +240,7 @@ can be made to convert your code to the new API: [ f for f in MyModel._meta.get_fields() - if f.many_to_one and f.auto_created + if f.one_to_many and f.auto_created ] * ``MyModel._meta.get_all_related_objects_with_model()``:: @@ -248,7 +248,7 @@ can be made to convert your code to the new API: [ (f, f.model if f.model != MyModel else None) for f in MyModel._meta.get_fields() - if f.many_to_one and f.auto_created + if f.one_to_many and f.auto_created ] * ``MyModel._meta.get_all_related_many_to_many_objects()``:: @@ -274,7 +274,7 @@ can be made to convert your code to the new API: for field in MyModel._meta.get_fields() # For complete backwards compatibility, you may want to exclude # GenericForeignKey from the results. - if not (field.one_to_many and field.related_model is None) + if not (field.many_to_one and field.related_model is None) ))) This provides a 100% backwards compatible replacement, ensuring that both diff --git a/tests/model_fields/test_field_flags.py b/tests/model_fields/test_field_flags.py index 3749e55452..e86ee3a528 100644 --- a/tests/model_fields/test_field_flags.py +++ b/tests/model_fields/test_field_flags.py @@ -31,22 +31,22 @@ RELATION_FIELDS = ( GenericRelation, ) -ONE_TO_MANY_CLASSES = { +MANY_TO_MANY_CLASSES = { + ManyToManyField, +} + +MANY_TO_ONE_CLASSES = { ForeignObject, ForeignKey, GenericForeignKey, } -MANY_TO_ONE_CLASSES = { +ONE_TO_MANY_CLASSES = { ForeignObjectRel, ManyToOneRel, GenericRelation, } -MANY_TO_MANY_CLASSES = { - ManyToManyField, -} - ONE_TO_ONE_CLASSES = { OneToOneField, }