I'd like to be able to automatically insert an entity with a reference t another entity directly from a message, using Google Endpoints.
To transmit ReferenceProperty in message, I use the encoded string value of the Key. That is fine for sending message, but when receiving message, and creating an entity of it, I cannot just pass the encoded string as a parameter to the constructor.
For instance, say I have two classes that inherits from BaseModel which itself inherits from db.models
class TestModel2(models.BaseModel):
test_string = db.StringProperty(required=True)
class TestModel(models.BaseModel):
test2 = db.ReferenceProperty(TestModel2)
test2_id = property(models.BaseModel._get_attr_id_builder('test2'),
models.BaseModel._set_attr_id_builder('test2'))
And a message class
class TestModelMessage(messages.Message):
test2_id = messages.StringField(4)
I want to be able to create an Entity TestModel directly of the TestModelMessage.
I managed to do it in the other way (from to entity to message) using a property. But in the other way it doesn't work since I have the feeling that the constructor for db.models will only set the attributes that inherits db.Property. Thus the setter for the property won't be called...
How could I do this?
I thought of overriding the __init__ in BaseModel but then when calling the __init__ of db.models it will probably override the ReferenceProperty.
So, I added a _ref_properties field to the BaseModel class.
For the previous example, it would be _ref_properties = {'test2': 'test2_id'}
Then I added this class method
#classmethod
def from_message(cls, message, *args):
attributes = {attr: getattr(message, attr) for attr in args}
for attribute, property in cls._ref_properties.items():
attributes[attribute] = db.Key(encoded=getattr(message, property))
entity = cls(**attributes)
return entity
And it seems to work. Probably not the best. Any remarks or better solution?
Related
I want to separate some logic from model and group it in a property, just like Django does with model managers (object property). I fact, something like ForeignKey but without database representation.
I have something like this:
class Remote(object):
def __init(self, *args, **kwargs)
self.post = ... # how to get to post instance?
def synchronize(self):
# this function requires Post object access
print(self.post.name)
class Post(models.Model):
name = models.CharField(max_length=100)
remote = Remote()
...
for post in Post.objects.all():
post.remote.synchronize()
Question
How to modify above code to get access to Post object in Remote object?
Additional question
Is it possible to determine if Remote object has been called from Post instance (post.remote... – like above) or Post class (Post.remote...)?
What you want here can be achieved with descriptors.
In order for it to work, you need to define a __get__ method in your class that you want to be accessible as an attribute of another class.
A simple example for your case will look like this:
class Remote:
def __init__(self, post)
self.post = post
def synchronize(self):
print(self.post.name)
class RemoteDescriptor:
def __get__(self, obj):
if not obj:
return self
remote = getattr(obj, '_remote', None)
if not remote:
remote = Remote(obj)
obj._remote = remote
return remote
class Post(models.Model):
name = models.CharField(max_length=100)
remote = RemoteDescriptor()
Explanation:
In the above code, every time you call an attribute remote of your Post model, __get__ method of the RemoteDescriptor will be invoked. First check for obj is to make sure that descriptor is called from other object, not directly. Two classes Remote and RemoteDescriptor are needed here in order for you to be able to add custom methods inside your descriptor accessible using dot (e.g. post.remote.calculate())
Note also that I am placing the instance of Remote to the dict of Post on first invokation and on all subsequent calls, object will be returned from there.
You should also check a great article on descriptors on RealPython.
I'm building an HTTP API and I factored out a lot of code into a superclass that handles requests to a collection of objects. In my subclass, I specify what database models the operation should work on and the superclass takes care of the rest.
This means that I don't need to re-implement the get, post, etc. methods from the superclass, however, I want to change their docstrings in the subclass so that I can have some documentation more specific to the actual model the endpoint is operating on.
What is the cleanest way to inherit the parent class's functionality but change the docstrings?
Example:
class CollectionApi(Resource):
"""Operate on a collection of something.
"""
class Meta(object):
model = None
schema = None
def get(self):
"""Return a list of collections.
"""
# snip
def post(self):
"""Create a new item in this collection.
"""
# snip
class ActivityListApi(CollectionApi):
"""Operations on the collection of Activities.
"""
class Meta(object):
model = models.Activity
schema = schemas.ActivitySchema
Specifically, I need ActivityListApi to have get and post run like in CollectionApi, but I want different docstrings (for automatic documentation's sake).
I can do this:
def get(self):
"""More detailed docs
"""
return super(ActivityListApi, self).get()
But this seems messy.
class CollectionApi(Resource):
"""Operate on a collection of something.
"""
def _get(self):
"""actual work... lotsa techy doc here!
the get methods only serve to have something to hang
their user docstrings onto
"""
pass
def get(self):
"""user-intended doc for CollectionApi"""
return self._get()
class ActivityListApi(CollectionApi):
def get(self):
"""user-intended doc for ActivityListApi"""
return self._get()
I want to have an endpoint which gets a certain type of object and returns a different type of object , instead of having them the same type.
for example:
class SomeClass(EndpointsModel):
name = ndb.StringProperty()
class OtherClass(EndpointsModel):
otherName = ndb.StringProperty()
#SomeClass.method(path='mymodel', http_method='POST', name='mymodel.insert')
def MyModelInsert(self, my_model):
my_model.put()
otherModel = OtherClass(otherName='someothername')
return otherModel
currently i'm getting:
ServerError (Method MyApi.MyModelInsert expected response type <class '.SomeClass'>, sent <class '.OtherClass'>)
Is there any way to have the input Class different from the return Class ?
You can supply a response_message parameter in the method decorator, but that parameter has to be a ProtoRPC message class and not an EndpointsModel.
You can get the message class from the EndpointsModel via the ProtoModel classmethod.
And you have to return a ProtoRPC message and not an EndpointsModel from your method since the library doesn't do the conversation automatically for custom response classes. You can do this using the ToMessage method of the model.
To summarize you would have this code (untested):
#SomeClass.method(path='mymodel',
http_method='POST',
name='mymodel.insert'
response_message=OtherClass.ProtoModel())
def MyModelInsert(self, my_model):
my_model.put()
otherModel = OtherClass(otherName='someothername')
return otherModel.ToMessage()
Given the following models:
class Module(models.Model):
pass
class Content(models.Model):
module = models.ForeignKey(Module, related_name='contents')
class Blog(Module):
pass
class Post(Content):
pass
I would like to be able to get all the "post" objects owned by blog doing something like:
b = Blog.objects.get(pk=1)
b.posts.all()
However, I haven't figured out a good way of doing this. I can't use b.contents.all() as I need Post instances and not Content instances. I won't ever have a root content object, every content object is going to be subclassed, but I can't use abstract classes as I want a central table with all my content in it and then there will be content_blog etc tables for all the unique inherited pieces of content.
I also tried doing this
class Content(models.Model):
module = models.ForeignKey(Module, related_name='%(class)')
but that failed miserably as far as I could tell.
The simplest way might add a method to Blog model to return a Post queryset, like this:
class Blog(Module):
def _get_posts(self):
return Post.objects.filter(module=self)
posts = property(_get_posts)
The problem is you have to add method for every sub-model. The related_name seems only works for abstract base class.
This solution comes to my mind:
# ...
class Blog(Module):
#property
def posts(self):
return self.contents
class Post(Content):
pass
This way, doing blog.posts is the same as doing blog.contents:
>>> blog = Blog.objects.get(pk=1)
>>> blog.posts.all()
# [ ... ]
By default collection.find or collection.findone() functions results in a dictionary types and if you pass paramater as_class=SomeUserClass than it will try to parse the result into this class format.
but it seems this class should also be derived class of dictionary (as it required __setitem__ function to be defined and i can add keys in the class ).
Here i want to set the properties of the class. how can i do achieve this?
Also, my collection class contains some child classes as properties .So how can i set the properties of child classes also.
It sounds like you want something like an object-relational mapper. I am the primary author of one Ming , but there exist several others for Python as well. In Ming, you might do the following to set up your mapping:
from ming import schema, Field
from ming.orm import (mapper, Mapper, RelationProperty,
ForeignIdProperty)
WikiDoc = collection(‘wiki_page', session,
Field('_id', schema.ObjectId()),
Field('title', str, index=True),
Field('text', str))
CommentDoc = collection(‘comment', session,
Field('_id', schema.ObjectId()),
Field('page_id', schema.ObjectId(), index=True),
Field('text', str))
class WikiPage(object): pass
class Comment(object): pass
ormsession.mapper(WikiPage, WikiDoc, properties=dict(
comments=RelationProperty('WikiComment')))
ormsession.mapper(Comment, CommentDoc, properties=dict(
page_id=ForeignIdProperty('WikiPage'),
page=RelationProperty('WikiPage')))
Mapper.compile_all()
Then you can query for some particular page via:
pg = WikiPage.query.get(title='MyPage')
pg.comments # loads comments via a second query from MongoDB
The various ODMs I know of for MongoDB in Python are listed below.
Ming
MongoKit
MongoEngine
I have solved this by adding __setitem__ in class.
than i do
result = as_class()
for key,value in dict_expr.items():
result.__setitem__(key,value)
and in my class __setitem__ is like
def __setitem__(self,key,value):
try:
attr = getattr(class_obj,key)
if(attr!=None):
if(isinstance(value,dict)):
for child_key,child_value in value.items():
attr.__setitem__(child_key,child_value)
setattr(class_obj,key,attr)
else:
setattr(class_obj,key,value)
except AttributeError:
pass