Categorías
Python

La guía del principiante a la tortuga Python

 

Tabla de Contenidos

  • los artículos de esta serie:
  • Lo que estamos construyendo
  • partir offFirst principlesA nota en la estructura del repositorio
  • primeros principios
  • Una nota sobre la estructura del repositorio
  • Inicio del projectDependencies
  • Dependencias
  • superando pequeña
  • Un lugar para everythingDomain modelsData setupConfiguration layerViewsApplication traducción y línea de comandos corredor modelos
  • dominio
  • datos capa de traducción
  • Más Vistas configuración
  • Aplicación

  • configuración
  • y corredor de línea de comandos

  • y todo en su lugar
  • Embalaje para arriba de
  • primeros principios
  • Una nota sobre la estructura del repositorio modelos
  • dependencias
  • dominio
  • datos capa de traducción
  • Vistas de configuración de aplicaciones
  • Configura ción y la línea de comandos corredor

Por favor nota: Esta es una pieza colaboración entre Michael Herman, procedente del Real Python, y Sean Vieira, un desarrollador de Python De Deo diseños.

los artículos de esta serie:

Actualizado: November 17th, 2013

Recientemente, gracias a un concierto impresionante, yo mismo re-introducidos en el frasco, que es “un microframework para Python”. Es un gran marco de poco; Desafortunadamente, como Sinatra (que inspiró), haciendo la transición desde aplicaciones pequeñas “autónomos” para aplicaciones más grandes es difícil. Hay una serie de boilerplates por ahí (incluyendo mi propia matraz de la plancha de caldera) para ayudar a hacer la transición más fácil. Sin embargo, el uso de un texto modelo no ayuda a entender el “por qué” de las convenciones de la plancha de caldera.

Después de revisar los tutoriales disponibles por ahí pensé, “tiene que haber una manera mejor” – entonces vi mega-tutorial de Miguel Grinberg. El enfoque que la construcción de una aplicación en lugar de un texto modelo dio a sus artículos me impresionó. Así pues, en esta serie de tutoriales vamos a ser la construcción de una aplicación de tamaño medio de nuestra propia y como un efecto secundario de la generación de un texto modelo. La esperanza es que cuando hayamos terminado vamos a tener una mejor apreciación de lo que ofrece el frasco y lo que podemos construir a su alrededor.

Lo que estamos construyendo

Esta serie estará siguiendo el desarrollo de una solución de análisis web llamado matraz de Seguimiento. El objetivo de esta serie es tener una aplicación de trabajo que permitirá a los usuarios:

  • Registro con la aplicación.
  • agregar sitios a su cuenta.
  • instalar los códigos de seguimiento en su sitio (s) para realizar un seguimiento de varios eventos a medida que ocurren los eventos.
  • Ver informa en su sitio (s)’actividad de eventos.

Al final del día, el objetivo de nuestra (además de tener una aplicación de trabajo que se ajuste a lo anterior servilleta de especificación) es:

  • aprender a desarrollar una aplicación de tamaño medio usando el frasco.
  • obtener experiencia práctica con la refactorización y pruebas.
  • obtener una apreciación más profunda de la construcción de sistemas componibles.
  • Desarrollar un texto modelo que podemos reutilizar para proyectos futuros.

Partiendo

Esta serie se supone que tiene una familiaridad con Python y el frasco ya. Ya debería estar a gusto con todo en el tutorial de Python, estar familiarizado con la línea de comandos (o, al menos, con PIP), y debería haber leído inicio rápido y Tutorial del frasco. Si quieres más práctica, dar un paso más allá y leer mi tutorial de inicio en el frasco aquí. Dicho esto, si usted es un recién llegado a Python y el frasco aún debe ser capaz de seguir adelante.

Nuestro objetivo hoy es implementar el núcleo de nuestra aplicación – el seguimiento de las visitas a un sitio. Vamos a permitir que varios sitios, pero no vamos a preocuparse por los usuarios o el control de acceso todavía.

primeros principios

Antes de sumergirse en Me gustaría tocar en algunos de los principios que guiarán nuestro desarrollo – no vamos a ahondar demasiado profundamente, en los “porqués” de algunas de estas mejores prácticas, pero en lo posible a ofrecer enlaces a artículos que explican lo que estas prácticas nos proporcionan. Por favor considerar todas las opiniones en esta serie para ser fuertes opiniones, débilmente celebradas.

El Zen de Python

Si no supiera ya, Python tiene una “filosofía de desarrollo” llamado el Zen de Python (también conocido como PEP 20). Para darle una lectura, basta con abrir un intérprete de Python y escriba:

>>> import this

y luego se verá lo siguiente:

El Zen de Python, por Tim Peters

Bello es mejor que feo.

Explícito es mejor que implícito.

simple es mejor que complejo.

Complejo es mejor que complicado.

plana es mejor que anidado.

Escaso es mejor que denso.

legibilidad cuenta.

casos

especiales no son suficientes para romper las reglas especiales.

Aunque latidos practicidad pureza. Errores

nunca deben pasar en silencio.

si no está silenciado de forma explícita.

Ante la ambigüedad, rechaza la tentación de adivinar.

No debe ser uno y preferiblemente sólo uno – manera obvia de hacerlo.

A pesar de esa manera puede no ser obvia en un primer momento a menos que seas holandés.

Ahora es mejor que nunca.

Aunque no es a menudo mejor que en este momento.

Si la aplicación es difícil de explicar, es una mala idea.

Si la aplicación es fácil de explicar, puede ser una buena idea.

Los espacios de nombres son una gran idea tocar la bocina – Vamos a hacer más de esos!

Estas son palabras a trabajar por el. notas de lectura Daniel Greenfeld el 19 de charla Pythonic Tesis Richard Jones para una explicación más detallada.

Acrónimo explosión

También vamos a conformar nuestras prácticas de desarrollo a los siguientes principios:

  • Si bien hay muchas herramientas que podríamos construir para hacer nuestra vida más fácil, vamos a limitarnos a la construcción de lo necesario para hacer que nuestra aplicación trabajo, recordando que en muchos casos YAGNI (Usted no va a necesitar It).
  • Una vez dicho esto, cuando nos encontramos con un patrón de código que estamos repitiendo a través de la aplicación estaremos refactorización nuestro código para mantenerlo seco.
  • Nuestra gatillo para cruzar de YAGNI a SECO serán los tres strikes y refactorizar principio. El uso de un patrón en tres lugares se clasificarán para la extracción. Dos usos todavía caerán bajo YAGNI. (En grandes proyectos de muchos sugieren una versión modificada de esta regla – cuando se necesita para volver a utilizarlo, refactorizar.)

Una nota sobre la estructura del repositorio

largo de esta serie va a ser capaz de encontrar el código final para estos ejercicios en el repositorio matraz de seguimiento en Github. Cada parte de este tutorial tiene una rama en el repositorio y un comunicado. El código para esta sección es en la rama parte-1. Si decide descargar el repositorio, se puede trabajar con el código para cualquier artículo de esta serie simplemente ejecutando:

$ git checkout part-N

donde N es el número del artículo del código es para (por lo que para este artículo uso git checkout parcial 1).

Inicio del proyecto

Con ese frente materia de inicio el camino de Let mediante la creación de una nueva carpeta para guardar nuestro proyecto y la activación de un entorno virtual (si no está seguro de cómo configurar un entorno virtual, tome un momento y revisar la guía de inicio rápido en el frasco de):

$ mkdir flask-tracking
$ cd flask-tracking
$ virtualenv --no-site-packages venv
$ source venv/bin/activate

dependencias

Como Alicia en el país de las maravillas que queremos tener en nuestras dependencias “justo”. Demasiadas dependencias y no vamos a ser capaces de trabajar sobre la base de código después de salir durante un mes sin tener que gastar una documentación de la revisión de la semana. Muy Pocos dependencias y vamos a pasar nuestro tiempo desarrollar todo excepto nuestra aplicación. Para asegurar que mantenemos nuestras dependencias “justo” vamos a utilizar la regla (imperfecta) siguiente – cada dependencia debe resolver también al menos un problema difícil . Comenzaremos con dependencias en tres bibliotecas – Frasco sí para la gestión de la solicitud / ciclo de respuesta, matraz de WTF para su protección CSRF y validación de datos y finalmente matraz de SQLAlchemy para la agrupación de conexiones de base de datos y el objeto / asignador relacional. Aquí está nuestro archivo requirements.txt:

Flask==0.10.1
Flask-SQLAlchemy==1.0
Flask-WTF==0.9.3

Ahora podemos ejecutar pip instalar requirements.txt -r para instalar nuestras necesidades en nuestro entorno virtual. aplicaciones

superando pequeña

más Frasco empezar desde abajo y luego se refactorizado como el alcance del proyecto crece. Para aquellos de nosotros que no han escrito y luego refactorizado una pequeña aplicación Frasco tenemos una única versión del módulo de nuestra aplicación de seguimiento en la rama parcial de 0 (que pesa 145 líneas total de ). Siéntase libre para saltar a la siguiente sección si ya ha escrito una sola aplicación de módulo en el frasco.

Para aquellos de ustedes que se quedan, por favor, git checkout parte-0 o descarga Parte 0 de la sección de comunicados del repositorio. Debe haber un solo módulo dentro de la aplicación, tracking.py, y la estructura de eficacia general debería tener este aspecto:

├── flask-tracking.db
├── requirements.txt
├── templates
│   ├── data_list.html
│   ├── helpers
│   │   ├── forms.html
│   │   └── tables.html
│   ├── index.html
│   ├── layout.html
│   └── validation_error.html
└── tracking.py

Si Agriétela abierto, el primero que se ve después de que las importaciones es:

_cwd = dirname(abspath(__file__))

SECRET_KEY = 'flask-session-insecure-secret-key'
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + join(_cwd, 'flask-tracking.db')
SQLALCHEMY_ECHO = True
WTF_CSRF_SECRET_KEY = 'this-is-not-random-but-it-should-be'

app = Flask(__name__)
app.config.from_object(__name__)

db = SQLAlchemy(app)

Hemos creado algunos básicos – Opciones de configuración de la SECRET_KEY se utiliza para firmar sesiones del frasco, la SQLALCHEMY_DATABASE_URI es la ruta de acceso a la base de datos (estamos utilizando SQLite por ahora), y la WTF_CSRF_SECRET_KEY se utiliza para firmar tokens CSRF WTForms’. Inicializamos una nueva aplicación Frasco y le diremos que configurarse a sí mismo (con nuestro llamado app.config.from_object) con todos los símbolos ALL_CAPS en el módulo actual. Entonces inicializamos nuestra extensión matraz de SQLAlchemy con nuestra aplicación.

A partir de ahí, es más o menos un tiro recto – hemos creado nuestros modelos:

class Site(db.Model):
__tablename__ = 'tracking_site'

id = db.Column(db.Integer, primary_key=True)
base_url = db.Column(db.String)
visits = db.relationship('Visit', backref='tracking_site', lazy='select')

def __repr__(self):
return '' % (self.base_url)

def __str__(self):
return self.base_url

class Visit(db.Model):
# ... snip ...

y forma:

class SiteForm(Form):
base_url = fields.StringField()

class VisitForm(Form):
# ... snip ...

y rutas que utilizan estos modelos y formas:

@app.route("/")
def index():
site_form = SiteForm()
visit_form = VisitForm()
return render_template("index.html",
site_form=site_form,
visit_form=visit_form)

@app.route("/site", methods=("POST", ))
def add_site():
form = SiteForm()
if form.validate_on_submit():
site = Site()
form.populate_obj(site)
db.session.add(site)
db.session.commit()
flash("Added site")
return redirect(url_for("index"))
return render_template("validation_error.html", form=form)

También hay un par de funciones de ayuda para activar SQLAlchemy consultar objetos en listas de listas de datos para nuestras plantillas y luego en la parte inferior del módulo hemos creado un bloque principal para crear nuestras tablas si no existen y ejecutar la aplicación en modo de depuración:

if __name__ == "__main__":
app.debug = True
db.create_all()
app.run()

Si ejecuta pitón tracking.py y vaya a localhost: 5000 en su navegador verá la aplicación extremadamente escueto. Es funcional (puede crear sitios y añadir las visitas), pero no tiene ninguna usuarios y control de acceso – todo el mundo lo ve todo.

Sin embargo, como se puede ver, tracking.py ya es bastante una mezcla heterogénea de funcionalidad – controladores, modelos, ayudantes, y la configuración de línea de comandos todos atascado en un solo archivo. Dividirlo en zonas separadas de funcionalidad hará que sea más fácil de mantener. Además, re-envasado de esta aplicación hará que sea más claro dónde agregar todas las otras características que queremos (usuarios, control de acceso, etc.). Cuando haya terminado de jugar un poco con el Código de pieza a 0 para ejecutar git checkout parte-1 y pasar a la siguiente sección.

Un lugar para el inicio de todo lo

Let mediante la creación de un paquete para nuestra aplicación vivir en (vamos a utilizar una forma modificada de la estructura descrita en la documentación del frasco para envases y planos): Mantener

flask-tracking/ # Our working root
flask_tracking/ # The application package
__init__.py
requirements.txt # Meta data needed by our application
README.md # and developers

en cuenta que nuestro actual objetivo es simplemente para realizar un seguimiento de las visitas a un sitio, vamos a evitar la creación de un sub-paquete para nuestro código de seguimiento … todavía (recuerda, YAGNI hasta que lo necesite). Vamos a seguir adelante y añadir modelos separados, formularios y módulos vistas a mantener nuestros modelos de dominio, nuestra capa de traducción de datos y código de nuestra vista, respectivamente. También vamos a crear un subdirectorio de plantillas para mantener los recursos que vamos a utilizar para hacer que el sitio. Por último, vamos a añadir un archivo de configuración y una secuencia de comandos para ejecutar la aplicación. Ahora debemos tener una estructura de directorios que es similar al siguiente: modelos

flask-tracking/
flask_tracking/
templates/ # Holds Jinja templates
__init__.py # General application setup
forms.py # User data to domain data mappers and validators
models.py # Domain models
views.py # well ... controllers, really.
config.py # Configuration, just like it says on the cover
README.md
requirements.txt
run.py # `python run.py` to bring the application up locally.

de dominio de modelos de dominio

Nuestros son simples – un sitio tiene un URL raíz y una visita tiene metadatos sobre el visitante (navegador, la dirección IP, URL, etc.) . Incluimos __repr__ métodos para darnos mejores detalles en la línea de comandos ( es más útil que ). El sitio incluye un método para controlar __str__ lo que mostramos cuando nos mostramos una lista de sitios en un menú desplegable.

from flask.ext.sqlalchemy import SQLAlchemy

db = SQLAlchemy()

class Site(db.Model):
__tablename__ = 'tracking_site'

id = db.Column(db.Integer, primary_key=True)
base_url = db.Column(db.String)
visits = db.relationship('Visit', backref='tracking_site', lazy='select')

def __repr__(self):
return ''.format(self.id, self.base_url)

def __str__(self):
return self.base_url

class Visit(db.Model):
__tablename__ = 'tracking_visit'

id = db.Column(db.Integer, primary_key=True)
browser = db.Column(db.String)
date = db.Column(db.DateTime)
event = db.Column(db.String)
url = db.Column(db.String)
ip_address = db.Column(db.String)
site_id = db.Column(db.Integer, db.ForeignKey('tracking_site.id'))

def __repr__(self):
r = ''
return r.format(self.site_id, self.url, self.date)

Tenga en cuenta que no hemos inicializado nuestro objeto flask.ext.sqlalchemy con cualquier aplicación por lo que estos modelos no están sujetos a esta aplicación particular (esta flexibilidad viene con algunos pequeños costes, que encontraremos en breve). capa de traducción

datos

Nuestro código formas es WTForms estándar, con una excepción. Si nos fijamos en VisitForm:

from .models import Site

# ... snip ...

class VisitForm(Form):
browser = fields.StringField()
date = fields.DateField()
event = fields.StringField()
url = fields.StringField()
ip_address = fields.StringField("IP Address")
site = QuerySelectField(query_factory=lambda: Site.query.all())

se dará cuenta de que tenemos que envolver Site.query.all en una lambda en lugar de pasar en tal cual. Desde nuestra db no está vinculado a una aplicación cuando VisitForm se está construyendo no podemos acceder a Site.query. Creación de una función que se llame Site.query sólo cuando se crea una instancia VisitForm (e. G., En nuestros puntos de vista que llamamos VisitForm) (= forma) asegura que vamos a sólo el acceso Site.query cuando vamos a tener acceso a una instancia de la aplicación del frasco.

Vistas

Nuestros puntos de vista son la parte compleja la mayor parte de nuestra aplicación (testigo cuántas dependencias estamos importando):

from flask import Blueprint, flash, Markup, redirect, render_template, url_for

from .forms import SiteForm, VisitForm
from .models import db, query_to_list, Site, Visit

Empezamos a cabo mediante la creación de un modelo (también podríamos partir flask_tracking aplicación de importación y el uso de la aplicación, pero prefieren cambiar a los planos una vez que mi solicitud ha crecido más allá de un único archivo)

tracking = Blueprint("tracking", __name__)

a continuación, el mapa nuestros puntos de vista a las rutas utilizando la sintaxis de decorador de lo normal – he añadido los comentarios en torno a algunas de las funciones en lo que estamos haciendo puede no ser perfectamente clara (o cuando nos repetimos y vamos a querer refactorizar más adelante):

@tracking.route("/")
def index():
site_form = SiteForm()
visit_form = VisitForm()
return render_template("index.html",
site_form=site_form,
visit_form=visit_form)

@tracking.route("/site", methods=("POST", ))
def add_site():
# The create a form, validate the form,
# map the form to a model, save the model,
# and redirect pattern will be pretty common
# throughout the application. This is an area
# that is ripe for improvement and refactoring.
form = SiteForm()
if form.validate_on_submit():
site = Site()
form.populate_obj(site)
db.session.add(site)
db.session.commit()
flash("Added site")
return redirect(url_for(".index"))

return render_template("validation_error.html", form=form)

@tracking.route("/site/")
def view_site_visits(site_id=None):
site = Site.query.get_or_404(site_id)
query = Visit.query.filter(Visit.site_id == site_id)
data = query_to_list(query)
title = "visits for {}".format(site.base_url)
return render_template("data_list.html", data=data, title=title)

@tracking.route("/visit", methods=("POST", ))
@tracking.route("/site//visit", methods=("POST",))
def add_visit(site_id=None):
if site_id is None:
# This is only used by the visit_form on the index page.
form = VisitForm()
else:
site = Site.query.get_or_404(site_id)
# WTForms does not coerce obj or keyword arguments
# (otherwise, we could just pass in `site=site_id`)
# CSRF is disabled in this case because we will *want*
# users to be able to hit the /site/:id endpoint from other sites.
form = VisitForm(csrf_enabled=False, site=site)

if form.validate_on_submit():
visit = Visit()
form.populate_obj(visit)
visit.site_id = form.site.data.id
db.session.add(visit)
db.session.commit()
flash("Added visit for site {}".format(form.site.data.base_url))
return redirect(url_for(".index"))

return render_template("validation_error.html", form=form)

@tracking.route("/sites")
def view_sites():
query = Site.query.filter(Site.id >= 0)
data = query_to_list(query)

# The header row should not be linked
results = [next(data)]
for row in data:
row = [_make_link(cell) if i == 0 else cell
for i, cell in enumerate(row)]
results.append(row)

return render_template("data_list.html", data=results, title="Sites")

_LINK = Markup('{name}')

def _make_link(site_id):
url = url_for(".view_site_visits", site_id=site_id)
return _LINK.format(url=url, name=site_id)

Esto nos da una aplicación que nos permitirá añadir un sitio o una visita en la página principal, ver una lista de sitios, y ver las visitas de cada sitio . Realmente no hemos registrado el plano con una aplicación todavía, así que …

configuración de aplicaciones

… en adelante a __init__.py:

from flask import Flask

from .models import db
from .views import tracking

app = Flask(__name__)
app.config.from_object('config')

# Add the `constants` variable to all Jinja templates.
@app.context_processor
def provide_constants():
return {"constants": {"TUTORIAL_PART": 1}}

db.init_app(app)

app.register_blueprint(tracking)

Creamos una aplicación, configura, se registra nuestra instancia matraz de SQLAlchemy en nuestra aplicación, y, finalmente, registrar nuestro plan. Ahora Frasco sabe cómo manejar nuestras rutas. Configuración

y corredor de línea de comandos

un nivel por encima nuestra configuración permite que las sesiones y los conjuntos de Frasco nuestra base de datos SQLite:

# config.py
from os.path import abspath, dirname, join

_cwd = dirname(abspath(__file__))

SECRET_KEY = 'flask-session-insecure-secret-key'
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + join(_cwd, 'flask-tracking.db')
SQLALCHEMY_ECHO = True

Y nuestro corredor aplicación crea las tablas de base de datos si no existen y se ejecuta la aplicación en modo de depuración:

# run.py
#!/usr/bin/env python
from flask_tracking import app, db

if __name__ == "__main__":
app.debug = True
# Because we did not initialize Flask-SQLAlchemy with an application
# it will use `current_app` instead. Since we are not in an application
# context right now, we will instead pass in the configured application
# into our `create_all` call.
db.create_all(app=app)
app.run()
Ver

del matraz de SQLAlchemy Introducción a los contextos si quieren tener una mejor idea de por qué necesitamos para pasar nuestra aplicación en la llamada db.create_all.

Y todo en su lugar

Ahora debería ser capaz de ejecutar run.py pitón y ver nuestra aplicación de puesta en marcha. Ir a localhost: 5000 y crear un sitio para probar cosas hacia fuera. Entonces, para verificar que otros puedan añadir las visitas al sitio, intente ejecutar:

$ curl --data 'event=BashVisit&browser=cURL&url=/&ip_address=1.2.3.4&date=2013-11-09' localhost:5000/site/1/visit

desde la línea de comandos. Cuando vuelva a la aplicación, haga clic en “Sitios” y luego haga clic en el “1” para su sitio. Una sola visita se debe mostrar en la página:

Embalaje para arriba de

Eso es todo por este post. Ahora tenemos una solicitud de trabajo donde los sitios se pueden añadir y visitas registradas en contra de ellos. Todavía tenemos que añadir cuentas de usuario, un fácil utilizar el API de cliente para el seguimiento e informes.

En la segunda parte vamos a añadir usuarios, control de acceso, y permitir a los usuarios añadir las visitas de sus propios sitios web. Vamos a explorar las mejores prácticas para la escritura más plantillas, manteniendo nuestros modelos y formas en sincronía, y el manejo de archivos estáticos.

En la Parte III, exploraremos pruebas de escritura para nuestra aplicación, registro y depuración de errores.

En la Parte IV vamos a hacer un poco de Test Driven Desarrollo para permitir que nuestra aplicación para aceptar pagos y visualizar informes sencillos.

En la Parte V que va a escribir una API REST JSON para el consumo de otros.

En la Parte VI que cubrirá las implementaciones de Automatización (en Heroku) con Tela y básica A / B función de prueba.

Por último, en la Parte VII que cubrirá la preservación de su aplicación para el futuro con la documentación, la cobertura de código y herramientas de métricas de calidad.

Gracias por leer y sintonizar la próxima vez!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *