-
Notifications
You must be signed in to change notification settings - Fork 0
/
fields.py
95 lines (75 loc) · 3.21 KB
/
fields.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
from django.db.models import AutoField
from google.appengine.api.datastore import Key
from google.appengine.ext import db
from django.db.models.sql.where import Constraint
class AncestorNode(Constraint):
def __init__(self, instance):
self.instance = instance
class PossibleDescendent(object):
def __init__(self, *args, **kwargs):
self._parent_key = None
super(PossibleDescendent, self).__init__(*args, **kwargs)
@classmethod
def descendents_of(cls, instance):
qs = cls.objects.all()
#Add our own custom constraint type to mark this as an ancestor query
#this is used in GAEQuery._decode_child and then switched for a custom filter
#which is passed to GAEQuery.add_filter where we set the ancestor_key
qs.query.where.add(
(Constraint(None, '__ancestor', instance._meta.pk), 'exact', instance),
'AND'
)
return qs
def parent(self):
if not isinstance(self.pk, AncestorKey):
return None
ak = self.pk
if ak._parent_cache is None:
ak._parent_cache = ak._parent_model.objects.get(pk=ak._parent_key.id_or_name())
return ak._parent_cache
class AncestorKey(object):
def __init__(self, ancestor=None, key_id=None, ancestor_pk=None, ancestor_model=None):
if ancestor is not None:
assert ancestor.pk is not None, "You must provide a parent with a key"
self._parent_model = type(ancestor)
ancestor_pk = ancestor.pk
else:
assert ancestor_pk is not None and ancestor_model is not None, "You must provide an ancestor_model and ancestor_pk or an ancestor instance"
self._parent_model = ancestor_model
self._parent_key = Key.from_path(self._parent_model._meta.db_table, ancestor_pk)
self._parent_cache = ancestor
self.key_id = key_id
def __eq__(self, other):
return self._parent_key == other._parent_key and self.key_id == other.key_id
def parent(self):
return self._parent_key
def id(self):
return self.key_id
def name(self):
return None
class GAEKeyField(AutoField):
#Make sure to_python is called on assignments
def __init__(self, ancestor_model, *args, **kwargs):
self.ancestor_model = ancestor_model
self._parent_key = None
self._id = None
kwargs["primary_key"] = True
super(GAEKeyField, self).__init__(*args, **kwargs)
def to_python(self, value):
if isinstance(value, Key):
return AncestorKey(
ancestor=self.ancestor_model.objects.get(pk=value.parent().id_or_name()),
key_id=value.id_or_name()
)
return value
def get_db_prep_value(self, value, connection, prepared=False):
if isinstance(value, AncestorKey):
# If the key isn't fully initialized pass it through, the InserCompiler will handle it
if value.key_id is None:
return value
return Key.from_path(
self.model._meta.db_table,
value.key_id,
parent=value._parent_key
)
return super(GAEKeyField, self).get_db_prep_value(value, connection)