1
0
mirror of https://github.com/django/django.git synced 2025-01-05 16:06:07 +00:00

Fixed #35224 -- Made GenericForeignKey inherit from Field.

This commit is contained in:
Adam Johnson 2024-02-16 22:38:17 +00:00 committed by Mariusz Felisiak
parent 57e6336f52
commit 6002df0671

View File

@ -11,6 +11,7 @@ from django.core.exceptions import FieldDoesNotExist, ObjectDoesNotExist
from django.db import DEFAULT_DB_ALIAS, models, router, transaction from django.db import DEFAULT_DB_ALIAS, models, router, transaction
from django.db.models import DO_NOTHING, ForeignObject, ForeignObjectRel from django.db.models import DO_NOTHING, ForeignObject, ForeignObjectRel
from django.db.models.base import ModelBase, make_foreign_order_accessors from django.db.models.base import ModelBase, make_foreign_order_accessors
from django.db.models.fields import Field
from django.db.models.fields.mixins import FieldCacheMixin from django.db.models.fields.mixins import FieldCacheMixin
from django.db.models.fields.related import ( from django.db.models.fields.related import (
ReverseManyToOneDescriptor, ReverseManyToOneDescriptor,
@ -24,7 +25,7 @@ from django.utils.deprecation import RemovedInDjango60Warning
from django.utils.functional import cached_property from django.utils.functional import cached_property
class GenericForeignKey(FieldCacheMixin): class GenericForeignKey(FieldCacheMixin, Field):
""" """
Provide a generic many-to-one relation through the ``content_type`` and Provide a generic many-to-one relation through the ``content_type`` and
``object_id`` fields. ``object_id`` fields.
@ -33,35 +34,28 @@ class GenericForeignKey(FieldCacheMixin):
ForwardManyToOneDescriptor) by adding itself as a model attribute. ForwardManyToOneDescriptor) by adding itself as a model attribute.
""" """
# Field flags
auto_created = False
concrete = False
editable = False
hidden = False
is_relation = True
many_to_many = False many_to_many = False
many_to_one = True many_to_one = True
one_to_many = False one_to_many = False
one_to_one = False one_to_one = False
related_model = None
remote_field = None
def __init__( def __init__(
self, ct_field="content_type", fk_field="object_id", for_concrete_model=True self, ct_field="content_type", fk_field="object_id", for_concrete_model=True
): ):
super().__init__(editable=False)
self.ct_field = ct_field self.ct_field = ct_field
self.fk_field = fk_field self.fk_field = fk_field
self.for_concrete_model = for_concrete_model self.for_concrete_model = for_concrete_model
self.editable = False self.is_relation = True
self.rel = None
self.column = None
def contribute_to_class(self, cls, name, **kwargs): def contribute_to_class(self, cls, name, **kwargs):
self.name = name super().contribute_to_class(cls, name, private_only=True, **kwargs)
self.model = cls # GenericForeignKey is its own descriptor.
cls._meta.add_field(self, private=True) setattr(cls, self.attname, self)
setattr(cls, name, self)
def get_attname_column(self):
attname, column = super().get_attname_column()
return attname, None
def get_filter_kwargs_for_object(self, obj): def get_filter_kwargs_for_object(self, obj):
"""See corresponding method on Field""" """See corresponding method on Field"""
@ -77,10 +71,6 @@ class GenericForeignKey(FieldCacheMixin):
self.ct_field: ContentType.objects.get_for_model(obj).pk, self.ct_field: ContentType.objects.get_for_model(obj).pk,
} }
def __str__(self):
model = self.model
return "%s.%s" % (model._meta.label, self.name)
def check(self, **kwargs): def check(self, **kwargs):
return [ return [
*self._check_field_name(), *self._check_field_name(),
@ -88,18 +78,6 @@ class GenericForeignKey(FieldCacheMixin):
*self._check_content_type_field(), *self._check_content_type_field(),
] ]
def _check_field_name(self):
if self.name.endswith("_"):
return [
checks.Error(
"Field names must not end with an underscore.",
obj=self,
id="fields.E001",
)
]
else:
return []
def _check_object_id_field(self): def _check_object_id_field(self):
try: try:
self.model._meta.get_field(self.fk_field) self.model._meta.get_field(self.fk_field)