python - ¿Cómo unir múltiples aplicaciones reutilizables de Django?
django-1.4 (4)
Hago lo mejor que puedo para escribir aplicaciones reutilizables de Django. Ahora estoy desconcertado sobre cómo reunirlos todos para obtener el proyecto final.
Este es un ejemplo de lo que quiero decir: tengo una aplicación de imágenes que almacena, redimensiona y muestra imágenes. También tengo una aplicación de weblog que almacena, edita y muestra textos. Ahora quiero combinar estos dos para mostrar publicaciones de blog con imágenes.
Para hacer eso, podría poner campos de clave externa en el blog para apuntar a las imágenes. Pero entonces el blog no podría ser utilizado sin la aplicación de imagen. También podría crear una tercera aplicación, que se encarga de conectar ambas.
¿Cuál es la forma de ''mejor práctica'' de hacerlo?
EDIT: Gracias por sus muy buenas respuestas, pero sigo buscando un ejemplo más práctico de cómo resolver este problema. Para completar mi ejemplo: a veces sería bueno usar la aplicación de blog sin la aplicación de imagen. Pero si código la dependencia ya no es posible. Entonces, ¿qué hay de la tercera aplicación para combinar ambos?
Esta es una buena pregunta, y algo que también encuentro bastante difícil de manejar. Pero, ¿imagina que estas aplicaciones se lanzan públicamente o solo las utiliza usted mismo? Si no estás lanzando, no me preocuparía demasiado por eso.
La otra cosa es que las dependencias están bien tener. La aplicación de imágenes en su ejemplo suena como un buen candidato para ser una aplicación ''reutilizable''. Es simple, hace una cosa y puede ser usado por otras aplicaciones.
Una aplicación de blog, por otro lado, generalmente necesita consumir otras aplicaciones como una aplicación de imagen o una aplicación de etiquetado. Encuentro esta dependencia bien para tener. Puede intentar abstraerlo un poco, simplemente vinculando a un recurso de medios que fue puesto allí por su aplicación de imagen.
Es solo un poco de sentido común. ¿Puedes adelgazar tus aplicaciones? En caso afirmativo, intente crearlos para que puedan reutilizarse. Pero no tengas miedo de tomar dependencias cuando tienen sentido. Además, intente permitir puntos de extensión para que pueda intercambiar dependencias por otras. Una clave externa directa no va a ayudar aquí, pero tal vez algo como señales o APIs de Restful puedan.
Introducción charla en la parte inferior de la respuesta (más directamente a la respuesta). Asumiré que tiene una aplicación para el manejo de texto llamada Texto y una aplicación para el manejo de imágenes llamada Imágenes y una tercera aplicación para blogs llamada Blog.
Cuadro grande
Tendrá que estudiar el manual sobre el lenguaje de plantillas para programadores de Python . La idea es que cada cosa esté en su propia aplicación y que tenga una tercera aplicación que lo conecte todo. Luego, las aplicaciones deben proporcionar sus modelos y vistas a su gusto (solo recuerde que debe concentrarse en lo que la aplicación debería hacer) y también proporcionar un conjunto de etiquetas de plantilla.
Cómo hacer etiquetas de inclusión.
Haz etiquetas de inclusión y es realmente fácil! Te recordará escribir vistas normales.
Cree un directorio en su carpeta de aplicaciones. También cree un archivo __init__.py
en estas __init__.py
de plantilla (para que el directorio se convierta en un paquete de Python).
Luego crea un archivo python. El nombre es importante, lo usarás en {% load xyz %}
en las plantillas que usarán tu aplicación. Por ejemplo, si llama al archivo picturestags.py
, llamará
{% load picturestags %}
en todas las plantillas que lo usarán.
Primero, en el archivo, agregue algo de política sobre la que no deba pensar, solo incluya esto antes que nada:
from django.template import Library
register = Library()
Luego agregue las etiquetas definiendo funciones con el mismo nombre que su etiqueta. Lo llamaré display_picture en el ejemplo y tomará un argumento url. La función debe crear un diccionario que usará en una plantilla. Mi ejemplo solo mostrará la imagen a la que apunta la url.
@register.inclusion_tag(''pictures/display_picture.html'')
def display_picture(url):
return {''picture'': url}
Cree las plantillas / imágenes de ruta en su aplicación y cree el archivo display_picture.html que contiene:
<img src="{{ picture }}" />
Como probablemente entiendas, @register lo convierte en una etiqueta, lo que se encuentra dentro del diccionario display_picture devuelve lo que puedes usar en display_picture.html. Muy parecido a sus funciones de vista normales.
Al final terminarás con estos archivos:
pictures/
__init__.py
models.py
views.py
tests.py
templates/
pictures/
display_picture.html
templatetags/
picturetags.py
Eso es todo lo que necesitas para agregar a tu aplicación de imágenes. Para usar esto en su aplicación de Blog, debe agregar Imágenes a su INSTALLED_APPS. Luego, en las plantillas, donde necesita usar su propia etiqueta recién horneada en casa, primero cárguela: {% load picturestags %}
luego simplemente agregue la etiqueta {% display_picture https://www.google.com/intl/sv_ALL/images/logos/images_logo_lg.gif %}
esta manera:
{% load picturestags %}
<html>
<body>
{% display_picture https://www.google.com/intl/sv_ALL/images/logos/images_logo_lg.gif %}
</body>
</html>
Resultados
Este es solo un pequeño ejemplo, pero puedes ver que es muy fácil expandirlo. Tu blog podría conectar la aplicación de Texto e Imágenes importando sus modelos y la clave externa. Hay conexión de texto e imágenes para una determinada publicación de blog. Tu blog_post.html-template podría verse (simplificado):
{% load picturestags %}
{% load texttags %}
<html>
<body>
<h1>{{ subject }}</h1>
<div class="article">{% article_to_html articleid %}</div>
<div class="article_picture">{% display_picture %}</div>
</body>
</html>
Tenga en cuenta que solo el blog tiene dependencias y las dependencias que debe tener (ningún blog sin texto e imágenes ... pero las imágenes pueden vivir sin texto). La apariencia y la ubicación deben ser y pueden ser controladas por las etiquetas CSS y DIV / SPAN. De esta manera, puede tomar su aplicación de imagen y dársela a alguien que no tenga idea de la aplicación de texto y usarla, ¡mostrando imágenes de diferentes formas probablemente sin siquiera tocar su código!
Las etiquetas de inclusión es lo único que sé desde que aprendí esto ayer. Creo que es una conveniencia provista por Django para simplificar la vida. En la página de documentación hay mucho más (incluso cómo hacer que las etiquetas "reales" sean difíciles sin los "accesos directos"). Entonces, si encuentra este método limitado, lea la documentación ... tiene muchos ejemplos. También se explica cómo hacer filtros, etiquetas simples, consideraciones de subprocesos y otras cosas avanzadas.
Introducción
Tuve este problema exactamente ya que tú y yo también queríamos algo diferente a las respuestas que leí (no digo que las respuestas hayan sido malas, me ayudaron a aprender mucho y me dieron ideas, pero quería esto que estoy escribiendo ahora) . Me las arreglé para descubrir algo que no es muy obvio, gracias a tu pregunta y definitivamente gracias a , ¡así que esta es mi contribución incluso a una pregunta de medio año que probablemente esté abandonada (podría ayudar a un googler o dos)!
También me inspiré mucho en las aplicaciones reutilizables de Google Tech Talk . Al final (43 minutos) menciona algunos buenos ejemplos, como django-tagging que es lo que dice como un modelo para escribir aplicaciones reutilizables. Eso me dio la idea de todo esto, porque así es como el etiquetado django resuelve este problema que tuvimos.
Ahora, después de que escribí todo esto (tomó una hora), sentí por primera vez que podría contribuir en lugar de solo buscar en Google y seguir lo que hacen los demás o quejarme de que otros no escriben cómo debo hacer las cosas. Por primera vez, asumo la responsabilidad de escribir mi opinión para que otros puedan buscar en Google (solo tuve que escribir este párrafo :-) porque se siente realmente bien, incluso si se puede destruir en pedazos o ignorar y olvidar).
Piénselo de la misma manera que usaría cualquier aplicación de terceros en su proyecto. "Reutilizable" no significa "sin dependencias". Por el contrario, sería difícil encontrar una aplicación que no tenga al menos una dependencia, incluso si solo depende de Django o de las bibliotecas principales de Python. (Si bien las bibliotecas centrales de Python generalmente se consideran dependencias "seguras", es decir, todos lo tendrán, las cosas a veces cambian entre las versiones de Python, por lo que aún está bloqueando su aplicación en un momento específico).
El objetivo de la reutilización es el mismo que el de DRY: no desea escribir el mismo código una y otra vez. Como resultado, tiene sentido dividir la funcionalidad como una aplicación de imagen, porque luego puede usarla una y otra vez en otras aplicaciones y proyectos, pero su aplicación de imagen tendrá dependencias y otros paquetes dependerán de ella, siempre que no hay dependencias circulares, eres bueno (una dependencia circular significaría que en realidad no has separado la funcionalidad).
Soy un novato de Django y enfrenté el mismo problema. Es vergonzoso cómo hay tanto alboroto acerca de las aplicaciones reutilizables en la comunidad de Django, pero no hay una sola referencia autorizada sobre cómo conectarlas en un proyecto sin dependencias codificadas.
He estado trabajando en un nuevo proyecto de Django y quería configurar mis modelos para evitar la codificación más dura posible. Este es el patrón que utilicé.
Diseño
project_root/
core/
models/
mixinmodels1.py
mixinmodels2.py
...
utils.py
...
app1/
models/
__init__.py
base.py
basemixins.py
mixins.py
concrete.py
/signals
__init__.py
handlers.py
utils.py
...
app2/
...
...
Modelos de aplicaciones
base.py
: este módulo implementa solo la ''razón de la existencia'' de esa aplicación en clases abstractas. Las reglas son:
Este módulo normalmente solo importa desde la aplicación
core
. Por lo general, no importa nada de otras aplicaciones en el mismo proyecto.Una excepción a la regla anterior es cuando el ''motivo de existencia'' asume la existencia de otra aplicación. Por ejemplo, una aplicación de
group
asume que hay una aplicación deuser
algún lugar. En este caso, la forma de vincularlos es:# project_root/settings.py AUTH_USER_MODEL = ''app_label.UserModel'' # project_root/groups/models/base.py from django.conf import settings
y luego usando la configuración. AUTH_USER_MODEL para referirse al modelo de
user
Utilice este patrón para todas las aplicaciones, no solo la aplicación de
user
. Por ejemplo, también deberías hacer# project_root/settings.py GROUP_MODEL = ''app_label.GroupModel''
Si utiliza el patrón anterior, solo asuma la funcionalidad proporcionada por
base.py
de la otra aplicación a la que se está vinculando. No asuma la funcionalidad de las clases concretas elaboradas (discutiré dónde colocar las clases concretas en breve)Por supuesto, se permite la importación desde django, aplicaciones de terceros y paquetes de Python.
Asegúrese de que las suposiciones que realice en
base.py
de cualquier aplicación sean sólidas y no cambien mucho en el futuro. Un buen ejemplo esdjango-registration
de James Bennett. Es una aplicación antigua, pero su atractivo no se desvaneció porque hizo suposiciones sólidas. Dado que las buenas aplicaciones reutilizables hacen una cosa bien, no es difícil encontrar ese conjunto de suposiciones.
basemixins.py
: este módulo debe implementar enchufes a los modelos concretos de esa aplicación. Un ''enchufe'' a un modelo M es cualquier modelo que contiene una clave externa al modelo M. Por ejemplo:
# project_root/groups/models/basemixins.py
from django.conf import settings
from django.db import models
class BaseOwnedByGroup(models.Model):
"""
This is a plug to the group model. Use this
to implement ownership like relations with
the group model
"""
owner = models.ForeignKey(settings.GROUP_MODEL,
related_name = ''%(app_label)s_%(class)s_owner'',
verbose_name = ''owner'')
# functionality and manager definitions go here.
class Meta:
abstract = True
app_label = ''groups''
BaseOwnedByGroup
es un ''plug'' para el modelo de group
. Las reglas aquí son las mismas que ''base.py`
- Al definir ''plugs'' en
basemixins.py
, solo asuma la funcionalidad provista porbase.py
- Importe solo desde
core
, django, aplicaciones de terceros y paquetes de python.
mixins.py
: este módulo debe usarse para dos propósitos
Para definir ''enchufes'' elaborados, que asumen la funcionalidad de las clases concretas elaboradas pero no las relaciones con otras aplicaciones. Los
basemixins.py
elaborados deberían heredar idealmente uno de los ''enchufes básicos'' definidos enbasemixins.py
.Para definir modelos mixin (que no sean ''enchufes'') que pueden ser utilizados por las clases concretas de esa aplicación.
concrete.py
: este módulo se debe usar para definir (usted lo adivinó) las clases concretas de esas aplicaciones y para establecer relaciones con otras aplicaciones. En resumen, este módulo asume su proyecto y toda la funcionalidad que desea proporcionar en él.
Las relaciones con otras aplicaciones deben configurarse de la siguiente manera:
Para establecer una relación
one to one
omany to one
con el modelo M de la aplicaciónanother_app
, haga lo siguiente:# project_root/another_app/utils.py def plug_to_M_factory(version_label): """ This is a factory method which returns the plug to model M specified by version_label """ if version_label == ''first_version'': from another_app.models.basemixins import BasePlugToM return BasePlugToM if version_label == ''second_version'': from another_app.models.mixins import PlugToM return PlugToM ... # project_root/groups/models/concrete.py from groups.models.base import BaseGroup from another_app.utils import plug_to_M_factory PlugToMClass = plug_to_M_factory(version_label = ''second_version'') class ConcreteGroup(BaseGroup, PlugToMClass): # define your concrete model class Meta: app_label = ''groups''
Para establecer una relación de
many to many
, la forma recomendada es usar un modelo athrough
. Herede el enchufe correcto en el modelothrough
de la misma manera (como hicimos en el modeloConcreteGroup
)
signals
: al configurar relaciones, a menudo tenemos que realizar operaciones en un modelo M de la aplicación app1
, cuando el modelo N de la aplicación app2
cambia. Puedes usar señales para manejar eso. Sus manejadores pueden asumir la funcionalidad del remitente concreto, pero a menudo no es necesario. La suposición de la versión base del remitente en base.py
es suficiente. Por esta razón, es una buena idea usar siempre la settings.ModelName
. Nombre de modelo para referirse a un modelo concreto. Puede extraer la clase del modelo de la cadena settings.ModelName
usando ContentType
o usando la función get_model_for_settings
todo el proyecto de la siguiente manera:
# project_root/project/utils.py
from django.db.models import get_model
from django.core.exceptions import ImproperlyConfigured
def get_model_from_settings(model_string):
"""
Takes a string of the form ''app_label.model'' as input, returns the
appropriate model class if it can find it.
"""
try:
app_label, model_name = model_string.split(''.'')
except ValueError:
raise ImproperlyConfigured("function argument must be of the "
"form ''app_label.model_name'', got ''%s''" % model_string)
model = get_model(app_label, model_name)
if model is None:
raise ImproperlyConfigured("function argument refers to model "
"''%s'' that has not been installed" % model_string)
return model
core
: La aplicación core es una aplicación especial que almacena funciones de mezcla amplia de proyectos.
Estos mixins no deben asumir nada sobre ninguna otra aplicación. La única excepción a esta regla son los mixins que dependen de la funcionalidad básica de la configuración. AUTH_USER_MODEL. Esto se debe a que puede asumir con seguridad que la mayoría de los proyectos tendrán un modelo de
user
.Por supuesto, las importaciones desde Django, terceros y paquetes de Python todavía están permitidas.
Recuerde que todos los módulos
base.py
ybasemixins.py
pueden importar desde elcore
.
Finalmente, para que todo funcione según lo previsto, importe sus modelos en los models/__init__.py
de cada aplicación.
Las ventajas que encuentro al seguir este esquema son:
Los modelos son reutilizables . Cualquiera puede usar los modelos base abstractos y las combinaciones para diseñar su propio modelo concreto.
base.py
,basemixins.py
y los métodos de fábrica relacionados se pueden agrupar junto con un modelo concretobasemixins.py
y enviarse en una aplicación reutilizable.Las aplicaciones son extensibles . Todas las mezclas están versionadas y hay un esquema de herencia claro.
Las aplicaciones están acopladas libremente . Se accede a los mixins externos a través de los métodos de fábrica y se hace referencia a los modelos externos utilizando django.conf.settings.
Las aplicaciones son autónomas . Cualquier cambio en una aplicación probablemente romperá esa aplicación y esa aplicación solamente. Otras aplicaciones probablemente permanecerán ilesas. Incluso si las aplicaciones externas se rompen, el lugar donde esto podría ocurrir está claramente marcado.
He estado usando este esquema para reducir el acoplamiento entre mis aplicaciones. No soy un programador experimentado de Django, y tengo mucho que aprender, por lo que agradecemos cualquier comentario.