Code

Ticket #3982: lazy_attribute.diff

File lazy_attribute.diff, 2.1 KB (added by Marty Alchin <gulopine@…>, 7 years ago)

An attempt at generic lazily-instantiated attributes

Line 
1Index: django/db/models/fields/__init__.py
2===================================================================
3--- django/db/models/fields/__init__.py (revision 5370)
4+++ django/db/models/fields/__init__.py (working copy)
5@@ -357,6 +357,46 @@
6         "Returns the value of this field in the given model instance."
7         return getattr(obj, self.attname)
8 
9+    def lazy_attribute(self, cls, creator=None, init=None):
10+        return AttributeProxy(self, cls, creator, init)
11+
12+class AttributeProxy(object):
13+    def __init__(self, field, cls, creator=None, init=None):
14+        self.field, self.cls = field, cls
15+        self.creator = creator or self.cls
16+        self.init = init or (lambda x: None)
17+
18+    def create(self, value):
19+        # Creates the attribute and makes sure it's a valid type
20+        value = self.creator(value)
21+        assert isinstance(value, self.cls), \
22+            "'%s.%s' did not return an object of type '%s'" % (
23+                self.field.model.object_name,
24+                self.field.attname,
25+                self.cls.__name__,
26+            )
27+        return value
28+       
29+    def __get__(self, obj, type=None):
30+        value = obj.__dict__[self.field.attname]
31+        if not getattr(obj, self.field.get_cache_name()):
32+            setattr(obj, self.field.attname, value)
33+        return obj.__dict__[self.field.attname]
34+
35+    def __set__(self, obj, value):
36+        if hasattr(obj, self.field.get_cache_name()):
37+            setattr(obj, self.field.get_cache_name(), False)
38+            if value is not None:
39+                if not isinstance(value, self.cls):
40+                    value = self.create(value)
41+                if not getattr(obj, self.field.get_cache_name()):
42+                    self.init(value)
43+                    setattr(obj, self.field.get_cache_name(), True)
44+        else:
45+            # This cache indicates whether the attribute has been instantiated
46+            setattr(obj, self.field.get_cache_name(), False)
47+        obj.__dict__[self.field.attname] = value
48+
49 class AutoField(Field):
50     empty_strings_allowed = False
51     def __init__(self, *args, **kwargs):