Google Code disponible en: English - Español - 日本語 - 한국어 - Português - Pусский - 中文(简体) - 中文(繁體)
Almacenar datos en una aplicación web escalable puede ser complicado. Un usuario podría estar interactuando con cualquiera de una docena de servidores web en un momento dado y la siguiente solicitud de ese usuario podría dirigirse a un servidor web distinto del servidor que haya gestionado la solicitud anterior. Todos los servidores web deben interactuar con datos que también están distribuidos por docenas de equipos y que posiblemente se encuentran en distintas partes del mundo.
Google App Engine soluciona todos estos problemas. La infraestructura de App Engine se encarga de todas las tareas de distribución, replicación y balanceo de carga de los datos de un API sencilla, además de ofrecer un potente motor de consulta y transacciones.
A continuación se muestra una nueva versión de helloworld/helloworld.py que almacena saludos en el almacén de datos. El resto de esta página describe los nuevos fragmentos.
import cgi
from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext import db
class Greeting(db.Model):
author = db.UserProperty()
content = db.StringProperty(multiline=True)
date = db.DateTimeProperty(auto_now_add=True)
class MainPage(webapp.RequestHandler):
def get(self):
self.response.out.write('<html><body>')
greetings = db.GqlQuery("SELECT * FROM Greeting ORDER BY date DESC LIMIT 10")
for greeting in greetings:
if greeting.author:
self.response.out.write('<b>%s</b> wrote:' % greeting.author.nickname())
else:
self.response.out.write('An anonymous person wrote:')
self.response.out.write('<blockquote>%s</blockquote>' %
cgi.escape(greeting.content))
# Write the submission form and the footer of the page
self.response.out.write("""
<form action="/sign" method="post">
<div><textarea name="content" rows="3" cols="60"></textarea></div>
<div><input type="submit" value="Sign Guestbook"></div>
</form>
</body>
</html>""")
class Guestbook(webapp.RequestHandler):
def post(self):
greeting = Greeting()
if users.get_current_user():
greeting.author = users.get_current_user()
greeting.content = self.request.get('content')
greeting.put()
self.redirect('/')
application = webapp.WSGIApplication(
[('/', MainPage),
('/sign', Guestbook)],
debug=True)
def main():
run_wsgi_app(application)
if __name__ == "__main__":
main()
Reemplaza helloworld/helloworld.py por esto y, a continuación, vuelve a cargar http://localhost:8080/ en tu navegador. Publica algunos mensajes para comprobar que se almacenan y se muestran correctamente.
App Engine incluye un API de modelado de datos para Python. Es parecida al API de modelado de datos de Django, pero utiliza el almacén de datos escalable en segundo plano.
Para la aplicación de libro de invitados, queremos almacenar los saludos publicados por los usuarios. Cada saludo incluye el nombre del autor, el contenido del mensaje y la fecha y hora de publicación de los distintos mensajes para que éstos puedan aparecer en orden cronológico.
Para utilizar el API de modelado de datos, importa el módulo google.appengine.ext.db:
from google.appengine.ext import db
A continuación se define un modelo de datos para un saludo:
class Greeting(db.Model): author = db.UserProperty() content = db.StringProperty(multiline=True) date = db.DateTimeProperty(auto_now_add=True)
Esta clase define un modelo Greeting con tres propiedades: author (cuyo valor es un objeto User), content (cuyo valor es una cadena) y date (cuyo valor es una fecha y hora datetime.datetime).
Algunos constructores de propiedades utilizan parámetros para ampliar la configuración de su comportamiento. Si se asigna al constructor db.StringProperty el parámetro multiline=True, los valores de esta propiedad podrán contener caracteres de salto de línea. Si se asigna al constructor db.DateTimeProperty un parámetro auto_now_add=True, el modelo se configurará para asignar automáticamente a los nuevos objetos un valor de date correspondiente a la hora de creación del objeto si la aplicación no especifica otro valor. Para obtener una lista completa de los tipos de propiedades y de sus opciones, consulta la referencia del almacén de datos.
Una vez que tenemos un modelo de datos para saludos, la aplicación puede utilizar ese modelo para crear nuevos objetos Greeting y guardarlos en el almacén de datos. La nueva versión del controladorGuestbook que se muestra a continuación crea nuevos saludos y los guarda en el almacén de datos:
class Guestbook(webapp.RequestHandler):
def post(self):
greeting = Greeting()
if users.get_current_user():
greeting.author = users.get_current_user()
greeting.content = self.request.get('content')
greeting.put()
self.redirect('/')
Este nuevo controlador Guestbook crea un nuevo objeto Greeting y después establece su autor (author) y las propiedades del contenido (content) con los datos publicados por el usuario. No establece la propiedad date, por lo que date se establece automáticamente en "now" de acuerdo con la configuración establecida para el modelo.
Por último, greeting.put() guarda el nuevo objeto en el almacén de datos. Si este objeto se obtuviera a partir de una consulta, put() actualizaría el objeto existente. Como el objeto se ha creado con el constructor de modelos, put() añadirá el nuevo objeto al almacén de datos.
El almacén de datos de App Engine dispone de un sofisticado motor de consulta para modelos de datos. El almacén de datos de App Engine no es una base de datos relacional tradicional, por lo que las consultas no se especifican con el lenguaje SQL. Las consultas se pueden preparar con un lenguaje de consulta parecido a SQL que se conoce como GQL. GQL ofrece acceso a las funciones del motor de consulta del almacén de datos de App Engine mediante una sintaxis común.
La versión del controlador MainPage que se muestra a continuación solicita saludos al almacén de datos:
class MainPage(webapp.RequestHandler):
def get(self):
self.response.out.write('<html><body>')
greetings = db.GqlQuery("SELECT * FROM Greeting ORDER BY date DESC LIMIT 10")
for greeting in greetings:
if greeting.author:
self.response.out.write('<b>%s</b> wrote:' % greeting.author.nickname())
else:
self.response.out.write('An anonymous person wrote:')
self.response.out.write('<blockquote>%s</blockquote>' %
cgi.escape(greeting.content))
# Write the submission form and the footer of the page
self.response.out.write("""
<form action="/sign" method="post">
<div><textarea name="content" rows="3" cols="60"></textarea></div>
<div><input type="submit" value="Sign Guestbook"></div>
</form>
</body>
</html>""")
La consulta aparece en esta línea:
greetings = db.GqlQuery("SELECT * FROM Greeting ORDER BY date DESC LIMIT 10")
También puedes ejecutar el método gql(...) en la clase Greeting y omitir SELECT * FROM Greeting en la consulta:
greetings = Greeting.gql("ORDER BY date DESC LIMIT 10")
Al igual que sucede con SQL, en las palabras clave (como SELECT) no se distingue entre mayúsculas y minúsculas. No ocurre lo mismo con los nombres.
La consulta devuelve objetos de datos completos, por lo que no tiene sentido seleccionar propiedades específicas del modelo. Todas las consultas GQL empiezan por SELECT * FROM model (o están establecidas así por el método gql(...) del modelo) para parecerse a sus equivalentes SQL.
Una consulta GQL puede tener una cláusula WHERE que filtre el conjunto de resultados por una o varias condiciones basadas en los valores de las propiedades. A diferencia de SQL, las consultas GQL no pueden contener constantes de valores: GQL utiliza los enlaces de parámetros para todos los valores de las consultas. Por ejemplo, para obtener sólo los saludos publicados por el usuario actual:
if users.get_current_user():
greetings = Greeting.gql("WHERE author = :1 ORDER BY date DESC",
users.get_current_user())
También se pueden utilizar parámetros con nombre en lugar de parámetros posicionales:
greetings = Greeting.gql("WHERE author = :author ORDER BY date DESC",
author=users.get_current_user())
Además de GQL, el API del almacén de datos ofrece otro mecanismo para la creación de objetos de consulta con métodos. La consulta anterior también se podría preparar del siguiente modo:
greetings = Greeting.all()
greetings.filter("author =", users.get_current_user())
greetings.order("-date")
Puedes obtener una descripción completa de GQL y de las API de consulta en la referencia del almacén de datos.
El servidor web de desarrollo utiliza una versión local del almacén de datos para probar las aplicaciones con archivos temporales. Los datos permanecen mientras existen los archivos temporales y el servidor web no restablece estos archivos a menos que se le pida que lo haga.
Si quieres que el servidor de desarrollo borre su almacén de datos antes de iniciarse, utiliza la opción --clear_datastore al iniciar el servidor:
dev_appserver.py --clear_datastore helloworld/
Actualmente existe una aplicación de libro de invitados que autentica a los usuarios con cuentas de Google, les permite enviar mensajes y muestra mensajes que han dejado otros usuarios. App Engine gestiona la escalabilidad de forma automática, por lo que no tendremos que revisar este código cuando se extienda el uso de esta aplicación.
En esta última versión se combina contenido HTML con el código del controlador MainPage. Esto dificultará el cambio de la apariencia de la aplicación, especialmente cuando ésta vaya aumentando en tamaño y complejidad. Utilizaremos plantillas para administrar la apariencia de la aplicación e introduciremos archivos estáticos para una hoja de estilo CSS.
Para continuar, consulta la sección Uso de plantillas.