Google Code предлагается на следующих языках: English – Español – 日本語 – 한국어 – Português – Pусский – 中文(简体) – 中文(繁體)
Объект хранилища данных обладает ключом и набором свойств. Приложение использует API хранилища данных для определения моделей данных и создания экземпляров этих моделей, которые будут храниться в качестве объектов. Модели представляют общую структуру для объектов, создаваемых API, и могут определять правила проверки значений свойств.
Приложение описывает вид используемых данных с помощью моделей. Модель представляет собой класс Python, наследуемый от класса Model. Класс модели определяет новый тип объекта хранилища данных и свойства, которыми должен обладать данный тип.
Свойства модели определяются с помощью атрибутов класса модели. Каждый атрибут класса является экземпляром подкласса класса Property, обычно одним из предоставленных классов свойств. Экземпляр свойства описывает параметры свойства, такие как необходимость данного свойства для допустимости экземпляра и значение по умолчанию, используемое для экземпляра, если не указано иное.
from google.appengine.ext import db class Pet(db.Model): name = db.StringProperty(required=True) type = db.StringProperty(required=True, choices=set(["cat", "dog", "bird"])) birthdate = db.DateProperty() weight_in_pounds = db.IntegerProperty() spayed_or_neutered = db.BooleanProperty() owner = db.UserProperty(required=True)
API представляет объект определенного типа в виде экземпляра соответствующего класса модели. Приложение может создавать новые объекты посредством вызова конструктора класса. Приложение осуществляет доступ к свойствам объекта и управляет ими с помощью атрибутов экземпляра. Конструктор экземпляра модели принимает начальные значения свойств в виде именованных аргументов.
from google.appengine.api import users
pet = Pet(name="Fluffy",
type="cat",
owner=users.get_current_user())
pet.weight_in_pounds = 24
Примечание. Атрибуты класса модели являются настройками свойств модели, значения которых являются экземплярами класса Property. Атрибуты экземпляра модели являются фактическими значениями свойств, тип значений которых поддерживается классом Property.
Класс Model использует экземпляры класса Property для проверки значений, присвоенных атрибутам экземпляра модели. Проверка значений свойств осуществляется при первом создании экземпляра модели и при присвоении атрибуту экземпляра нового значения. Это устраняет возможность присвоения свойству недопустимого значения.
Поскольку при создании экземпляра проводится проверка, в конструкторе необходимо инициализировать все свойства, заданные как обязательные. В этом примере свойства name, type и owner обязательны, поэтому их начальные значения задаются в конструкторе. Свойство weight_in_pounds не является обязательным для модели, поэтому его значение не присваивается изначально, а задается позже.
Экземпляр модели, созданный с помощью конструктора, не существует в хранилище данных до первого размещения там. См. статью Создание, получение и удаление данных.
Примечание. Как и все атрибуты классов Python, параметры свойств модели инициализируются при первом импорте сценария или модуля. Поскольку App Engine кэширует импортированные модули для выполнения различных запросов, настройки модуля могут быть инициализированы при выполнении запроса одного пользователя, а затем повторно использованы при выполнении запроса другого пользователя. Не инициализируйте параметры свойств модели, например значения по умолчанию, данными, специфичными для запроса текущего пользователя. Подробнее см. статью Кэширование приложений.
Модель, определенная с помощью класса Model, задает фиксированный набор свойств, которыми должен обладать каждый экземпляр класса (иногда вместе со значениями по умолчанию). Это полезно при работе с объектами данных модели, но для хранилища данных не требуется, чтобы все объекты одного типа обладали одинаковым набором свойств.
Иногда полезно, чтобы объект обладал свойствами, отличными от свойств других объектов того же типа. Такой объект представляется в API хранилища данных моделью "expando". Класс модели expando является подклассом суперкласса Expando. Каждое значение, присваиваемое атрибуту экземпляра модели expando, становится свойством объекта хранилища данных, использующим имя этого атрибута. Такие свойства называются динамическими свойствами. Свойства, определенные с помощью экземпляров класса Property в атрибутах класса, являются фиксированными свойствами.
Модель expando может содержать как фиксированные, так и динамические свойства. Класс модели просто задает атрибуты класса с помощью объектов конфигурации Property для фиксированных свойств. Приложение создает динамические свойства, когда присваивает им значения.
class Person(db.Expando): first_name = db.StringProperty() last_name = db.StringProperty() hobbies = db.StringListProperty() p = Person(first_name="Albert", last_name="Johnson") p.hobbies = ["chess", "travel"] p.chess_elo_rating = 1350 p.travel_countries_visited = ["Spain", "Italy", "USA", "Brazil"] p.travel_trip_count = 13
Поскольку в динамических свойствах нет определений свойств модели, динамические свойства не проверяются. Любое динамическое свойство может иметь значение одного из базовых типов хранилища данных, включая None. Два объекта одинакового типа могут иметь значения разных типов для одного динамического свойства, и в одном объекте оно может быть не задано, когда в других определено.
В отличие от фиксированных свойств, динамические свойства необязательно должны существовать. Динамическое свойство со значением None – не то же самое, что несуществующее динамическое свойство. Если у экземпляра модели expando нет атрибута для свойства, у соответствующего объекта данных нет такого свойства. Можно удалить динамическое свойство, удалив атрибут.
del p.chess_elo_rating
Запрос, использующий динамическое свойство в фильтре, возвращает только те объекты, у которых значение свойства имеет тот же тип, что и значение в запросе. Аналогичным образом запрос возвращает только те объекты, для которых это свойство задано.
p1 = Person()
p1.favorite = 42
p1.put()
p2 = Person()
p2.favorite = "blue"
p2.put()
p3 = Person()
p3.put()
people = db.GqlQuery("SELECT * FROM Person WHERE favorite < :1", 50)
# people has p1, but not p2 or p3
people = db.GqlQuery("SELECT * FROM Person WHERE favorite > :1", 50)
# people has no results
Класс Expando является подклассом класса Model и наследует все его методы.
API Python содержит еще один класс для моделирования данных, который позволяет определять иерархии классов и выполнять запросы, возвращающие объекты заданного класса или любого из его подклассов. Такие модели и запросы называются "полиморфическими", поскольку дают возможность экземплярам определенного класса быть результатами запроса к родителю этого класса.
В следующем примере определяется класс Contact и классы Person и Company, являющиеся подклассами Contact:
from google.appengine.ext import db from google.appengine.ext.db import polymodel class Contact(polymodel.PolyModel): phone_number = db.PhoneNumberProperty() address = db.PostalAddressProperty() class Person(Contact): first_name = db.StringProperty() last_name = db.StringProperty() mobile_number = db.PhoneNumberProperty() class Company(Contact): name = db.StringProperty() fax_number = db.PhoneNumberProperty()
Эта модель гарантирует, что у всех объектов Person и Company есть свойства phone_number и address, а запросы к объектам Contact могут возвращать объекты Person или Company. Свойство mobile_number есть только у объектов Person.
Экземпляры подклассов создаются так же, как и экземпляры любых других классов модели:
p = Person(phone_number='1-206-555-9234',
address='123 First Ave., Seattle, WA, 98101',
first_name='Alfred',
last_name='Smith',
mobile_number='1-206-555-0117')
p.put()
c = Company(phone_number='1-503-555-9123',
address='P.O. Box 98765, Salem, OR, 97301',
name='Data Solutions, LLC',
fax_number='1-503-555-6622')
c.put()
Запрос к объектам Contact может вернуть экземпляры Contact, Person или Company. В следующем фрагменте кода выводится информация о созданных выше объектах:
for contact in Contact.all():
print 'Phone: %s\nAddress: %s\n\n'
% (contact.phone,
contact.address))
Запрос к объектам Company возвращает только экземпляры Company:
for company in Company.all() # ...
Подробнее об использовании полиморфических моделей и их реализации рассказано в статье Класс PolyModel.
Хранилище данных поддерживает фиксированный набор типов значений свойств объектов, включая строки Unicode, целые числа, числа с плавающей точкой, даты, ключи объектов, байтовые строки (блобы) и различные типы GData. Каждый из типов значений хранилища данных имеет соответствующий класс Property, который входит в модуль google.appengine.ext.db.
Описание всех поддерживаемых типов значений и соответствующих им классов Property можно найти в статье Типы и классы Property. Несколько специальных типов значений описано ниже.
Хранилище данных поддерживает два типа значений для хранения текста: короткие текстовые строки размером до 500 байт и длинные текстовые строки размером до 1 мегабайта. Короткие строки индексируются и могут использоваться при указании порядка сортировки и условий фильтров запросов. Длинные строки не индексируются и не могут использоваться при указании порядка сортировки и условий фильтров запросов.
Значение короткой строки может являться значением unicode или str. Значение str предполагает кодировку 'ascii'. Чтобы изменить кодировку значения str, можно преобразовать его в значение unicode посредством конструктора типа unicode(), который принимает в качестве аргументов значение str и название кодировки. Короткие строки можно смоделировать с помощью класса StringProperty.
class MyModel(db.Model):
string = db.StringProperty()
obj = MyModel()
# Python Unicode literal syntax fully describes characters in a text string.
obj.string = u"kittens"
# unicode() converts a byte string to a Unicode value using the named codec.
obj.string = unicode("kittens", "latin-1")
# A byte string is assumed to be text encoded as ASCII (the 'ascii' codec).
obj.string = "kittens"
# Short string properties can be used in query filters.
results = db.GqlQuery("SELECT * FROM MyModel WHERE string = :1", u"kittens")
Значение длинной строки представляется экземпляром класса db.Text. Его конструктор принимает значение unicode или str и дополнительно название кодировки, используемой в значении str. Длинные строки можно смоделировать с помощью класса TextProperty.
class MyModel(db.Model):
text = db.TextProperty()
obj = MyModel()
# Text() can take a Unicode value.
obj.text = db.Text(u"lots of kittens")
# Text() can take a byte string and the name of an encoding.
obj.text = db.Text("lots of kittens", "latin-1")
# If no encoding is specified, a byte string is assumed to be ASCII text.
obj.text = db.Text("lots of kittens")
# Text properties can store large values.
obj.text = db.Text(open("a_tale_of_two_cities.txt").read(), "utf-8")
Кроме того, хранилище данных поддерживает два похожих типа нетекстовых байтовых строк: db.ByteString и db.Blob. Значения этих типов представляют собой необработанные строки байтов и не воспринимаются как закодированный текст (например, в формате UTF-8).
Как и значения str и unicode, значения db.ByteString индексируются, а их длина ограничивается 500 символами. Экземпляр класса ByteString представляет собой короткую строку байт и принимает значение str в качестве аргумента своего конструктора. Байтовые строки моделируются с помощью класса ByteStringProperty.
Как и db.Text, значение db.Blob может иметь размер до 1 мегабайта, не индексируется и не может использоваться при указании порядка сортировки и условий фильтров запросов. Класс db.Blob принимает значение str в качестве аргумента своего конструктора. Блобы моделируются с помощью класса BlobProperty.
class MyModel(db.Model):
blob = db.BlobProperty()
obj = MyModel()
obj.blob = db.Blob(open("image.png").read())
У свойства может быть несколько значений, представляемых в API хранилища данных в виде списка list Python. Список может содержать значения любых типов, поддерживаемых хранилищем данных. В одном списковом свойстве могут даже содержаться значения разных типов. Порядок сохраняется, поэтому при возвращении объектов запросами и методом get() значения списковых свойств располагаются в том же порядке, что и при хранении.
Класс ListProperty моделирует список и принудительно назначает всем значениям списка указанный тип. Для удобства в библиотеке также имеется класс StringListProperty, аналогичный ListProperty(basestring).
class MyModel(db.Model): numbers = db.ListProperty(long) obj = MyModel() obj.numbers = [2, 4, 6, 8, 10] obj.numbers = ["hello"] # ERROR: MyModel.numbers must be a list of longs.
Фильтр запроса по списковому свойству проверяет, содержится ли указанное значение в списке. Условие выполняется, если ему удовлетворяет хотя бы один из элементов списка.
# Get all entities where numbers contains a 6.
results = db.GqlQuery("SELECT * FROM MyModel WHERE numbers = 6")
# Get all entities where numbers contains at least one element less than 10.
results = db.GqlQuery("SELECT * FROM MyModel WHERE numbers < 10")
Фильтры запросов работают только с элементами списка. Найти общие элементы двух списков с помощью фильтра запроса невозможно.
Технически хранилище данных представляет значение спискового свойства в виде нескольких значений свойства. Если значением спискового свойства является пустой список, у свойства нет представления в хранилище данных. API хранилища данных обрабатывает такую ситуацию по-разному для статических свойств (с ListProperty) и динамических свойств:
None.list невозможно присвоить значение пустого списка. Однако у него может быть значение None, и его можно удалить (с помощью del).Модель ListProperty проверяет, что в список добавляется значение верного типа, в противном случае возникает исключение BadValueError. Такая проверка осуществляется (с возможным неудачным результатом) даже при получении и загрузке в модель ранее сохраненного объекта. Поскольку перед сохранением значения str преобразуются в значения unicode (как текст ASCII), ListProperty(str) обрабатывается как ListProperty(basestring), тип данных Python, который принимает как значения str, так и unicode. Для этой цели также можно использовать StringListProperty().
Для хранения нетекстовых байтовых строк используйте значения db.Blob. При сохранении и получении байты строки блоба не изменяются. Можно объявить свойство вида ListProperty(db.Blob), представляющее собой список блобов.
Списковые свойства взаимодействуют с порядком сортировки необычным образом. Подробнее об этом рассказано в статье Запросы и индексы: порядки сортировки и свойства с несколькими значениями.
Значение свойства может содержать ключ другого объекта. Это значение является экземпляром класса Key.
Класс ReferenceProperty моделирует значение ключа и требует, чтобы все значения ссылались на объекты определенного типа. Для удобства в библиотеке также имеется класс SelfReferenceProperty, который аналогичен классу ReferenceProperty и ссылается на тот же тип, что и объект со свойством.
При присвоении экземпляра модели свойству ReferenceProperty в качестве значения автоматически используется его ключ.
class FirstModel(db.Model): prop = db.IntegerProperty() class SecondModel(db.Model): reference = db.ReferenceProperty(FirstModel) obj1 = FirstModel() obj1.prop = 42 obj1.put() obj2 = SecondModel() # A reference value is the key of another entity. obj2.reference = obj1.key() # Assigning a model instance to a property uses the entity's key as the value. obj2.reference = obj1 obj2.put()
Значение свойства ReferenceProperty можно использовать, как если бы оно было экземпляром модели объекта, на который ссылается. Если объект, на который указывает ссылка, отсутствует в памяти, при использовании свойства в качестве экземпляра этот объект будет автоматически получен из хранилища данных.
obj2.reference.prop = 999
obj2.reference.put()
results = db.GqlQuery("SELECT * FROM SecondModel")
another_obj = results.fetch(1)[0]
v = another_obj.reference.prop
При удалении объекта, ключ которого является значением свойства-ссылки, само свойство не изменяется. Значением свойства-ссылки может быть ключ, который уже недействителен. При наличии вероятности, что ссылка недействительна, приложение может проверить существование объекта с помощью оператора if:
obj1 = obj2.reference if not obj1: # Referenced entity was deleted.
Класс ReferenceProperty обладает еще одной удобной функцией – обратными ссылками. Если модель содержит ReferenceProperty со ссылкой на другую модель, каждый объект, на который имеется ссылка, получает свойство со значением класса Query, возвращающего все объекты первой модели, которые ссылаются на данный объект.
# To fetch and iterate over every SecondModel entity that refers to the # FirstModel instance obj1: for obj in obj1.secondmodel_set: # ...
Имя свойства обратной ссылки по умолчанию – modelname_set (имя модели строчными буквами, к которому добавляется "_set"). Его можно настроить с помощью аргумента collection_name в конструкторе класса ReferenceProperty.
При наличии нескольких значений ReferenceProperty, которые ссылаются на один и тот же класс модели, используемая по умолчанию конструкция свойства с обратной ссылкой вызовет ошибку:
class FirstModel(db.Model): prop = db.IntegerProperty() # This class raises a DuplicatePropertyError with the message # "Class Firstmodel already has property secondmodel_set" class SecondModel(db.Model): reference_one = db.ReferenceProperty(FirstModel) reference_two = db.ReferenceProperty(FirstModel)
Чтобы избежать этой ошибки, следует задать аргумент collection_name явным образом:
class FirstModel(db.Model):
prop = db.IntegerProperty()
# This class runs fine
class SecondModel(db.Model):
reference_one = db.ReferenceProperty(FirstModel,
collection_name="secondmodel_reference_one_set")
reference_two = db.ReferenceProperty(FirstModel,
collection_name="secondmodel_reference_two_set")
Автоматическое задание и разыменование ссылок экземпляров модели, проверка типа и обратные ссылки доступны только при использовании класса свойств модели ReferenceProperty. Ключи, хранящиеся в качестве значений динамических свойств Expando или значений ListProperty, не обладают данными функциями.
Хранилище данных резервирует все названия свойств, начинающиеся и заканчивающиеся двойным подчеркиванием (__*__). Приложение не может создать свойство с таким названием.
В API Python атрибуты экземпляров модели, названия которых начинаются с подчеркивания (_), игнорируются и не сохраняются в объекте хранилища данных. Это позволяет сохранять значения для временного внутреннего использования в экземпляре модели, не влияя на данные, сохраненные в объекте.
Поскольку API Python использует атрибуты экземпляров модели в качестве названий свойств по умолчанию, ни один из атрибутов, используемых методами экземпляров, нельзя использовать напрямую в качестве названия атрибута свойства. Аналогично нельзя использовать в качестве названий атрибутов свойств названия, используемые именованными аргументами конструкторов модели. См. список зарезервированных названий свойств.
Само хранилище данных допускает использование таких названий. Если приложению необходимо, чтобы объект хранилища данных обладал свойством с названием, совпадающим с зарезервированным словом в API Python, оно может использовать статическое свойство и передать аргумент name в конструктор класса Property. См. конструктор класса Property.
class MyModel(db.Model): obj_key = db.StringProperty(name="key")