Refs #30657 -- Made DeferredAttribute.__init__() to take a field instance instead of a field name.

This commit is contained in:
Jon Dufresne 2019-07-23 05:04:06 -07:00 committed by Mariusz Felisiak
parent fc75694257
commit 93ffa81bc5
3 changed files with 19 additions and 20 deletions

View File

@ -14,10 +14,9 @@ class SpatialProxy(DeferredAttribute):
Initialize on the given Geometry or Raster class (not an instance) Initialize on the given Geometry or Raster class (not an instance)
and the corresponding field. and the corresponding field.
""" """
self._field = field
self._klass = klass self._klass = klass
self._load_func = load_func or klass self._load_func = load_func or klass
super().__init__(field.attname) super().__init__(field)
def __get__(self, instance, cls=None): def __get__(self, instance, cls=None):
""" """
@ -32,7 +31,7 @@ class SpatialProxy(DeferredAttribute):
# Getting the value of the field. # Getting the value of the field.
try: try:
geo_value = instance.__dict__[self._field.attname] geo_value = instance.__dict__[self.field.attname]
except KeyError: except KeyError:
geo_value = super().__get__(instance, cls) geo_value = super().__get__(instance, cls)
@ -44,7 +43,7 @@ class SpatialProxy(DeferredAttribute):
# Otherwise, a geometry or raster object is built using the field's # Otherwise, a geometry or raster object is built using the field's
# contents, and the model's corresponding attribute is set. # contents, and the model's corresponding attribute is set.
geo_obj = self._load_func(geo_value) geo_obj = self._load_func(geo_value)
setattr(instance, self._field.attname, geo_obj) setattr(instance, self.field.attname, geo_obj)
return geo_obj return geo_obj
def __set__(self, instance, value): def __set__(self, instance, value):
@ -56,7 +55,7 @@ class SpatialProxy(DeferredAttribute):
To set rasters, use JSON or dict values. To set rasters, use JSON or dict values.
""" """
# The geographic type of the field. # The geographic type of the field.
gtype = self._field.geom_type gtype = self.field.geom_type
if gtype == 'RASTER' and (value is None or isinstance(value, (str, dict, self._klass))): if gtype == 'RASTER' and (value is None or isinstance(value, (str, dict, self._klass))):
# For raster fields, assure input is None or a string, dict, or # For raster fields, assure input is None or a string, dict, or
@ -67,7 +66,7 @@ class SpatialProxy(DeferredAttribute):
# general GeometryField is used. # general GeometryField is used.
if value.srid is None: if value.srid is None:
# Assigning the field SRID if the geometry has no SRID. # Assigning the field SRID if the geometry has no SRID.
value.srid = self._field.srid value.srid = self.field.srid
elif value is None or isinstance(value, (str, memoryview)): elif value is None or isinstance(value, (str, memoryview)):
# Set geometries with None, WKT, HEX, or WKB # Set geometries with None, WKT, HEX, or WKB
pass pass
@ -76,5 +75,5 @@ class SpatialProxy(DeferredAttribute):
instance.__class__.__name__, gtype, type(value))) instance.__class__.__name__, gtype, type(value)))
# Setting the objects dictionary with the value, and returning. # Setting the objects dictionary with the value, and returning.
instance.__dict__[self._field.attname] = value instance.__dict__[self.field.attname] = value
return value return value

View File

@ -738,7 +738,7 @@ class Field(RegisterLookupMixin):
# if you have a classmethod and a field with the same name, then # if you have a classmethod and a field with the same name, then
# such fields can't be deferred (we don't have a check for this). # such fields can't be deferred (we don't have a check for this).
if not getattr(cls, self.attname, None): if not getattr(cls, self.attname, None):
setattr(cls, self.attname, DeferredAttribute(self.attname)) setattr(cls, self.attname, DeferredAttribute(self))
if self.choices is not None: if self.choices is not None:
setattr(cls, 'get_%s_display' % self.name, setattr(cls, 'get_%s_display' % self.name,
partialmethod(cls._get_FIELD_display, field=self)) partialmethod(cls._get_FIELD_display, field=self))

View File

@ -116,8 +116,8 @@ class DeferredAttribute:
A wrapper for a deferred-loading field. When the value is read from this A wrapper for a deferred-loading field. When the value is read from this
object the first time, the query is executed. object the first time, the query is executed.
""" """
def __init__(self, field_name): def __init__(self, field):
self.field_name = field_name self.field = field
def __get__(self, instance, cls=None): def __get__(self, instance, cls=None):
""" """
@ -127,26 +127,26 @@ class DeferredAttribute:
if instance is None: if instance is None:
return self return self
data = instance.__dict__ data = instance.__dict__
if data.get(self.field_name, self) is self: field_name = self.field.attname
if data.get(field_name, self) is self:
# Let's see if the field is part of the parent chain. If so we # Let's see if the field is part of the parent chain. If so we
# might be able to reuse the already loaded value. Refs #18343. # might be able to reuse the already loaded value. Refs #18343.
val = self._check_parent_chain(instance, self.field_name) val = self._check_parent_chain(instance)
if val is None: if val is None:
instance.refresh_from_db(fields=[self.field_name]) instance.refresh_from_db(fields=[field_name])
val = getattr(instance, self.field_name) val = getattr(instance, field_name)
data[self.field_name] = val data[field_name] = val
return data[self.field_name] return data[field_name]
def _check_parent_chain(self, instance, name): def _check_parent_chain(self, instance):
""" """
Check if the field value can be fetched from a parent field already Check if the field value can be fetched from a parent field already
loaded in the instance. This can be done if the to-be fetched loaded in the instance. This can be done if the to-be fetched
field is a primary key field. field is a primary key field.
""" """
opts = instance._meta opts = instance._meta
f = opts.get_field(name) link_field = opts.get_ancestor_link(self.field.model)
link_field = opts.get_ancestor_link(f.model) if self.field.primary_key and self.field != link_field:
if f.primary_key and f != link_field:
return getattr(instance, link_field.attname) return getattr(instance, link_field.attname)
return None return None