3 # Copyright 2007 Ken Tidwell
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 """Simple property for storing ordered lists of Model objects.
19 It should be noted that larger lists are going to be VERY inefficient
20 to load (one get per object).
22 Currently I have no idea where that upper bound might lie, though.
24 A quick usage example:
26 name = db.StringProperty(required=True)
27 class Holder(db.Model):
28 bits = reflistprop.ReferenceListProperty(Bit, default=None)
29 b1 = Bit(name="first")
30 b1.put() # need to put it so that it is a valid reference object
38 This throws a db.BadValueError because a string is not an instance of
40 h1.bits = ["nasty evil string"]
42 This is not good but gets no complaint at assignment time (same behaviour
43 as ListProperty) but we will throw a db.BadValueError if you try to put it
44 into the datastore. (Maybe ListProperty wants to do the same? Or should I be
45 waiting for the datastore internal entity construction to notice the problem
47 h1.bits.append("nasty evil string")
49 Yes, of course you can query them. The important bit to understand is
50 that the list is stored as a list of keys in the datastore. So you use
51 the key of the entity in question in your query. (Seems like it would be
52 nice if the property could get involved and do that coercion for you but
53 I don't think it can right now...).
55 Here's a little example:
56 class Thang(db.Model):
57 name = db.StringProperty(required=True)
58 class Holder(db.Model):
59 thangs = langutils.ReferenceListProperty(Thang, default=None)
62 foo = Thang(name="foo")
64 holder1.thangs.append(foo)
66 hq = db.GqlQuery("SELECT * FROM Holder where thangs = :1", foo.key())
67 holders = hq.fetch(10)
68 print "Holders =", holders
71 http://groups.google.com/group/google-appengine/msg/d203cc1b93ee22d7
75 from google
.appengine
.ext
import db
78 class ReferenceListProperty(db
.Property
):
79 """A property that stores a list of models.
81 This is a parameterized property; the parameter must be a valid
82 Model type, and all items must conform to this type.
84 def __init__(self
, item_type
, verbose_name
=None, default
=None, **kwds
):
85 """Construct ReferenceListProperty.
88 item_type: Type for the list items; must be a subclass of Model.
89 verbose_name: Optional verbose name.
90 default: Optional default value; if omitted, an empty list is used.
91 **kwds: Optional additional keyword arguments, passed to base class.
93 if not issubclass(item_type
, db
.Model
):
94 raise TypeError('Item type should be a subclass of Model')
97 self
.item_type
= item_type
98 super(ReferenceListProperty
, self
).__init
__(verbose_name
,
102 def validate(self
, value
):
105 Note that validation here is just as broken as for ListProperty.
106 The values in the list are only validated if the entire list is
107 swapped out. If the list is directly modified, there is no attempt
108 to validate the new items.
114 BadValueError if property is not a list whose items are
115 instances of the item_type given to the constructor.
117 value
= super(ReferenceListProperty
, self
).validate(value
)
118 if value
is not None:
119 if not isinstance(value
, list):
120 raise db
.BadValueError('Property %s must be a list' %
123 if not isinstance(item
, self
.item_type
):
124 raise db
.BadValueError(
125 'Items in the %s list must all be %s instances' %
126 (self
.name
, self
.item_type
.__name
__))
129 def empty(self
, value
):
130 """Is list property empty.
132 [] is not an empty value.
135 True if value is None, else False.
141 def default_value(self
):
142 """Default value for list.
144 Because the property supplied to 'default' is a static value,
145 that value must be shallow copied to prevent all fields with
146 default values from sharing the same instance.
149 Copy of the default value.
151 return list(super(ReferenceListProperty
, self
).default_value())
153 def get_value_for_datastore(self
, model_instance
):
154 """A list of key values is stored.
156 Prior to storage, we validate the items in the list.
157 This check seems to be missing from ListProperty.
160 model_instance: Instance to fetch datastore value from.
163 A list of the keys for all Models in the value list.
165 value
= self
.__get
__(model_instance
, model_instance
.__class
__)
170 return [v
.key() for v
in value
]
172 def make_value_from_datastore(self
, value
):
173 """Recreates the list of Models from the list of keys.
176 value: value retrieved from the datastore entity.
179 None or a list of Models.
184 return [db
.get(v
) for v
in value
]