I am leveraging ndb's to_dict method to convert an object's properties into a python dict. From everything I can tell, this method does not include the object's key or parent within the dict as per the documentation:
https://developers.google.com/appengine/docs/python/ndb/modelclass#Model_to_dict
However for my situation I need the key to be in the dict. My preference would be to leverage the builtin method and subclass it or something similar rather than create my own to_dict method.
What is the best way to accomplish this or am I missing something obvious? Thanks in advance.
FYI: I am not leveraging django for this project but instead straight python deployed up to gae.
You're not missing anything ;-)
Just add the key to the dictionary after you call to_dict, and yes override the method.
If you have multiple models that don't share the same base class with your custom to_dict, I would implement it as a mixin.
to define to_dict as a method of a Mixin class. you would
class ModelUtils(object):
def to_dict(self):
result = super(ModelUtils,self).to_dict()
result['key'] = self.key.id() #get the key as a string
return result
Then to use it.
class MyModel(ModelUtils,ndb.Model):
# some properties etc...
Another easy way to achieve that (without having to override to_dict) is to add a ComputedProperty that returns the id, like so:
class MyModel(ndb.Model):
# this property always returns the value of self.key.id()
uid = ndb.ComputedProperty(lambda self: self.key.id(), indexed=False)
# other properties ...
A ComputedProperty will be added to the result of to_dict just like any other property.
There are just two constraints:
Apparently the name of the property can not be key (since that would conflict with the actual key) and id doesn't work either.
This won't work if you don't specify a key or id when you create the object.
Also, the computed value will be written to the data store when you call put(), so it consumes some of your storage space.
An advantage is that this supports the include and exclude arguments of to_dict() out of the box.
Related
I wonder if i use class variables as keys in a dictionary can it be considered as good practice?
For example, this is object model in peewee.
class Abc:
name = CharField()
age = IntegerField()
then i declare a configuration dict for it like this
conf = {Abc.name: Config(listable=False, format_on_edit=True), Abc.age: Config()}
I don't want to use string like name, age as dict keys because i'm afraid of mistyping, and want to make sure that Object/ Model field is valid.
I see Sqlalchemy or Peewee are using condition like where(Abc.name=='abc') or filter(User.age == 25) , not where('name', 'abc') like many other orms from Go or PHP since they don't have class variables.
It's quite good for prevent mistyping.
I've tried hash(Abc.name) and it works then class variables are immutable for using as dict keys or not?
You may safely use them. Peewee replaces the field instances you declare as model attributes with special Descriptor objects (which then expose the underlying field instance, which is hashable).
For instance, when performing an insert or update, you can specify the data using the fields as keys:
User.insert({User.username: 'charlie'}).execute()
I'm using MongoEngine and trying to create a field that works like SQLAlchemy's PickleType field. Basically, I just need to pickle objects before they're written to the database, and unpickle them when they're loaded.
However it looks like MongoEngine's fields don't provide proper conversion methods I could override, instead having two coercion methods (to_python and to_mongo). If I understand correctly, these functions can be called anytime, that is, a call to to_python(v) does not guarantee that v comes from the database. I've thought of writing something like this:
class PickleField(fields.BinaryField):
def to_python(self, value):
value = super().to_python(value)
if <<value was pickled by the field>>
return pickle.loads(value)
else:
return value
Unfortunately, if I want to be as general as possible, I don't see a way to check whether the value should be unpickled or not. For instance,
a = pickle.dumps(x)
PickleField().to_python(a) # should return a, will return x
I also don't think I can store any state in the PickleField, since that's shared by all instances.
Is there a way around this?
The below link specifies how to create a list-field in Django.
How to create list field in django
My question; how to add, remove items to this field? if in case that's possible. I want to be able to add to and remove from this field of a Model's instance afterwards. If not possible please someone suggest me an alternative.
in ListField:
to_python function is used to convert database string to python object(here is list)
get_prep_value is used to handle python object to string(to store into database next step).
when you call your obj.listfield, such as a.list, you'll get a python list, so you could use a.list.append(val), a.list.insert(0, val) method to operate, and at last call a.save(), listfield will do the conversion for you between database and python.
class Dummy(models.Model):
mylist = ListField()
def insert_to_mylist(self, index, value):
self.mylist.insert(index, value)
#self.save()
def remove_from_mylist(self, value):
self.mylist.remove(value)
#self.save()
for example:
d = Dummy()
d.mylist.insert(0, 'tu')
d.save()
d.mylist.remove('tu')
d.save()
d.insert_to_mylist(0, "tu")
d.save()
d.remove_from_mylist('tu')
d.save()
if you want to insert or remove without .save(), just add .save() to insert_to_mylist and remove_from_mylist function.
ListField is just used to conversion between database and python, so add a remove or insert to it may be difficult. But you can do it in your Model, if many Models need to use this method, just write an abstract model.
My way of storing lists in models is by converting them to a string and storing them as TextField.
I'm creating an app that I want to have an expandable set of properties (each a RatingProperty) I also want to validate that any dynamic properties are of the RatingProperty type.
In the Expando documentation it says:
Tip: If you want to validate a dynamic property value using a Property class, you can instantiate the Property class and call its validate() method on the value.
So if I want to validate a dynamic property I need to know what the class's non-dynamic properties are. How can I ask my class what it's defined properties are?
I've considered creating a class method that takes a string and returns true if that string is in a list of property names that I create and maintain, but it seems like a hack. I've searched the Google for tips, but haven't had any luck.
Thanks,
Pat
After a bit more research (damn you lazyweb!) I've found a solution that I think is acceptable:
A dynamic property can't be of a db subclassed property type. Thus, there are two distinct steps that must be taken. First you need to create an instance of your property class and validate your value:
test = db.RatingProperty()
if test.validate(valueToSave):
#do your thing
Next you need to check if the property you want to save is a declared property:
if valueToSaveKey not in myObject.properties():
#if not save it as desired
myObject.valueToSaveKey = valueToSave
The down side here is that the value you save isn't stored as the property type you want.
http://code.google.com/appengine/docs/python/datastore/modelclass.html#Model_properties
db.Model has methods to find out all the properties on an instance.
The class exposes a list of Property objects: db.Model.properties()
The instance exposes the dynamic names only: instance.dynamic_properties()
You want to loop through the list and build Property objects, and run p.validate().
for p_name in instance.dynamic_properties():
p = db.RatingProperty()
p.validate() # raises BadValueError, etc.
I may be misunderstanding your question, but if you have a list of properties you expect to find, why not just use a standard db.Model, instead of an Expando? You can add additional properties to a Model class, as long as you either provide a default or don't make them required.
It's actually quite easy!
ExpandoObject implements (IDictionary<String, Object>) so you just need to do this :
dynamic person = new ExpandoObject();
person.FirstName = "Barack";
person.LastName = "Obama"
(((IDictionary<String, Object>)person).Keys
=> { "FirstName", "LastName" }
(((IDictionary<String, Object>)person).ContainsKey("FirstName")
=> true
Note: You need to explicitly cast to (IDictionary<string, object> because ExpandoObject explicitly implements this interface - and the instance itself doesn't have ContainsKey() or Keys.
Don't expect this method to work with all dynamic objects - just ExpandoObject and anything else that implements this interface.
Is there a way to get the key (or id) value of a db.ReferenceProperty, without dereferencing the actual entity it points to? I have been digging around - it looks like the key is stored as the property name preceeded with an _, but I have been unable to get any code working. Examples would be much appreciated. Thanks.
EDIT: Here is what I have unsuccessfully tried:
class Comment(db.Model):
series = db.ReferenceProperty(reference_class=Series);
def series_id(self):
return self._series
And in my template:
more
The result:
more
Actually, the way that you are advocating accessing the key for a ReferenceProperty might well not exist in the future. Attributes that begin with '_' in python are generally accepted to be "protected" in that things that are closely bound and intimate with its implementation can use them, but things that are updated with the implementation must change when it changes.
However, there is a way through the public interface that you can access the key for your reference-property so that it will be safe in the future. I'll revise the above example:
class Comment(db.Model):
series = db.ReferenceProperty(reference_class=Series);
def series_id(self):
return Comment.series.get_value_for_datastore(self)
When you access properties via the class it is associated, you get the property object itself, which has a public method that can get the underlying values.
You're correct - the key is stored as the property name prefixed with '_'. You should just be able to access it directly on the model object. Can you demonstrate what you're trying? I've used this technique in the past with no problems.
Edit: Have you tried calling series_id() directly, or referencing _series in your template directly? I'm not sure whether Django automatically calls methods with no arguments if you specify them in this context. You could also try putting the #property decorator on the method.