This article is derived from a Stack Overflow question. The original question can be found here.
Question
I looked into the ndb
GitHub sample code, but I couldn’t find any example
which shows on how to create a ndb
entity with a constructor that contains a StructuredProperty
.
Here is the GitHub example.
What if I want to initialize a Contact
entity with a list of phone numbers and this list of phone number is not a list of PhoneNumber
objects. Instead it is a list of Python dictionaries.
So, given the following Model
classes:
class PhoneNumber(ndb.Model):
"""A model representing a phone number."""
phone_type = ndb.StringProperty(
choices=('home', 'work', 'fax', 'mobile', 'other'))
number = ndb.StringProperty()
class Contact(ndb.Model):
"""A Contact model that uses StructuredProperty for phone numbers."""
# Basic info.
name = ndb.StringProperty()
birth_day = ndb.DateProperty()
# Address info.
address = ndb.StringProperty()
phone_numbers = ndb.StructuredProperty(PhoneNumber, repeated=True)
I want to create a Contact
using the following Python dictionaries:
phone_number_dicts = [{"phone_type" : "home", number = 122}, {"phone_type" : "work", number = 123}]
contact = Contact(name = "some name", birthday = "some day", phone_numbers = phone_number_dicts)
- Am I required to convert a dict to a
ndb
entity explicitly? - Can I override
ndb
constructor which converts a dict to andb
entity and assign? - Any other better approach?
Solution
Simply override the PhoneNumber
constructor, so that you can pass in a dict as kwargs
to its constructor via the Contact
constructor.
class PhoneNumber(ndb.Model):
phone_type = ndb.StringProperty(
choices=('home', 'work', 'fax', 'mobile', 'other'))
number = ndb.StringProperty()
def __init__(self, *args, **kwargs):
super(PhoneNumber, self).__init__(*args, **kwargs)
self.__dict__.update(kwargs)
class Contact(ndb.Model):
name = ndb.StringProperty()
birth_day = ndb.DateProperty()
address = ndb.StringProperty()
phone_numbers = ndb.StructuredProperty(PhoneNumber, repeated=True)
company_title = ndb.StringProperty()
company_name = ndb.StringProperty()
company_description = ndb.TextProperty()
company_address = ndb.StringProperty()
def __init__(self, *args, **kwargs):
super(Contact, self).__init__(*args, **kwargs)
if kwargs:
self.phone_numbers = []
for kwarg in kwargs.pop('phone_numbers'):
if isinstance(kwarg, PhoneNumber):
self.phone_numbers.append(kwarg)
else:
p = PhoneNumber(**kwarg)
self.phone_numbers.append(p)
In this way you can pass a dictionary representation of your PhoneNumber
entities to the Contact
constructor or a dictionary representation of the PhoneNumber
properties to the PhoneNumber
constructor.
Here are a few test cases I tried via the Interactive Console of the dev_appserver.py
:
from google.appengine.ext import ndb
from models import Contact, PhoneNumber
kwargs = {
'phone_numbers': [{
'phone_type': 'home',
'number': '123',
}, {
'phone_type': 'work',
'number': '456',
}, {
'phone_type': 'fax',
'number': '789',
}]
}
c = Contact(**kwargs)
print 'Test Case 1:'
print c
print
kwargs = {
'phone_numbers': [
PhoneNumber(**{'phone_type': 'home','number': '123'}),
PhoneNumber(**{'phone_type': 'work','number': '456'}),
PhoneNumber(**{'phone_type': 'fax', 'number': '789'})
]
}
c = Contact(**kwargs)
print 'Test Case 2:'
print c
print
c = Contact(
phone_numbers=[
PhoneNumber(phone_type='home', number='123'),
PhoneNumber(phone_type='work', number='456'),
PhoneNumber(phone_type='fax', number='789')
]
)
print 'Test Case 3:'
print c
print
Output:
Test Case 1:
Contact(phone_numbers=[PhoneNumber(number='123', phone_type='home'),
PhoneNumber(number='456', phone_type='work'),
PhoneNumber(number='789', phone_type='fax')])
Test Case 2:
Contact(phone_numbers=[PhoneNumber(number='123', phone_type='home'),
PhoneNumber(number='456', phone_type='work'),
PhoneNumber(number='789', phone_type='fax')])
Test Case 3:
Contact(phone_numbers=[PhoneNumber(number='123', phone_type='home'),
PhoneNumber(number='456', phone_type='work'),
PhoneNumber(number='789', phone_type='fax')])
As expected, each case elicits the same Contact
objects.