Estoy escribiendo un proyecto en Django y veo que el 80% del código está en el archivo models.py
. Este código es confuso y, después de cierto tiempo, dejo de entender lo que realmente está sucediendo.
Esto es lo que me molesta:
- Me parece feo que mi nivel de modelo (que se suponía que era responsable solo del trabajo con datos de una base de datos) también está enviando correos electrónicos, caminando sobre la API a otros servicios, etc.
- Además, me parece inaceptable colocar la lógica de negocios en la vista, porque de esta manera se vuelve difícil de controlar. Por ejemplo, en mi aplicación hay al menos tres formas de crear nuevas instancias
User
, pero técnicamente debería crearlas de manera uniforme. - No siempre me doy cuenta cuando los métodos y propiedades de mis modelos se vuelven no deterministas y cuando desarrollan efectos secundarios.
Aquí hay un ejemplo simple. Al principio, el User
modelo era así:
class User(db.Models):
def get_present_name(self):
return self.name or 'Anonymous'
def activate(self):
self.status = 'activated'
self.save()
Con el tiempo, se convirtió en esto:
class User(db.Models):
def get_present_name(self):
# property became non-deterministic in terms of database
# data is taken from another service by api
return remote_api.request_user_name(self.uid) or 'Anonymous'
def activate(self):
# method now has a side effect (send message to user)
self.status = 'activated'
self.save()
send_mail('Your account is activated!', '…', [self.email])
Lo que quiero es separar las entidades en mi código:
- Entidades de mi base de datos, nivel de base de datos: ¿Qué contiene mi aplicación?
- Entidades de mi aplicación, nivel de lógica de negocios: ¿Qué puede hacer mi aplicación?
¿Cuáles son las buenas prácticas para implementar un enfoque que se pueda aplicar en Django?
Respuestas:
Parece que está preguntando sobre la diferencia entre el modelo de datos y el modelo de dominio : en este último es donde puede encontrar la lógica comercial y las entidades tal como las percibe su usuario final, el primero es donde realmente almacena sus datos.
Además, he interpretado la tercera parte de su pregunta como: cómo notar la falla para mantener estos modelos separados.
Estos son dos conceptos muy diferentes y siempre es difícil mantenerlos separados. Sin embargo, existen algunos patrones y herramientas comunes que pueden usarse para este propósito.
Sobre el modelo de dominio
Lo primero que debe reconocer es que su modelo de dominio no se trata realmente de datos; se trata de acciones y preguntas como "activar este usuario", "desactivar este usuario", "¿qué usuarios están activados actualmente?" y "¿cuál es el nombre de este usuario?". En términos clásicos: se trata de consultas y comandos .
Pensar en comandos
Comencemos mirando los comandos en su ejemplo: "activar este usuario" y "desactivar este usuario". Lo bueno de los comandos es que pueden expresarse fácilmente mediante pequeños escenarios de cuándo-entonces:
Tales escenarios son útiles para ver cómo diferentes partes de su infraestructura pueden verse afectadas por un solo comando, en este caso, su base de datos (algún tipo de bandera 'activa'), su servidor de correo, el registro de su sistema, etc.
Tal escenario también lo ayuda realmente a configurar un entorno de desarrollo dirigido por pruebas.
Y finalmente, pensar en comandos realmente te ayuda a crear una aplicación orientada a tareas. Sus usuarios apreciarán esto :-)
Expresando comandos
Django proporciona dos formas fáciles de expresar comandos; Ambos son opciones válidas y no es inusual mezclar los dos enfoques.
La capa de servicio
El módulo de servicio ya ha sido descrito por @Hedde . Aquí define un módulo separado y cada comando se representa como una función.
servicios.py
Usando formularios
La otra forma es usar un formulario Django para cada comando. Prefiero este enfoque, porque combina múltiples aspectos estrechamente relacionados:
formas.py
Pensando en consultas
Su ejemplo no contenía ninguna consulta, así que me tomé la libertad de hacer algunas consultas útiles. Prefiero usar el término "pregunta", pero las consultas es la terminología clásica. Las consultas interesantes son: "¿Cuál es el nombre de este usuario?", "¿Puede este usuario iniciar sesión?", "Mostrarme una lista de usuarios desactivados" y "¿Cuál es la distribución geográfica de los usuarios desactivados?"
Antes de embarcarse en responder estas preguntas, siempre debe hacerse dos preguntas: ¿es esta una consulta de presentación solo para mis plantillas, y / o una consulta de lógica de negocios vinculada a la ejecución de mis comandos, y / o una consulta de informes ?
Las consultas de presentación se realizan simplemente para mejorar la interfaz de usuario. Las respuestas a las consultas de lógica de negocios afectan directamente la ejecución de sus comandos. Las consultas de informes son meramente para fines analíticos y tienen limitaciones de tiempo más flexibles. Estas categorías no son mutuamente excluyentes.
La otra pregunta es: "¿Tengo control completo sobre las respuestas?" Por ejemplo, al consultar el nombre del usuario (en este contexto) no tenemos ningún control sobre el resultado, porque confiamos en una API externa.
Haciendo consultas
La consulta más básica en Django es el uso del objeto Manager:
Por supuesto, esto solo funciona si los datos están realmente representados en su modelo de datos. Este no es siempre el caso. En esos casos, puede considerar las siguientes opciones.
Etiquetas y filtros personalizados
La primera alternativa es útil para consultas que son simplemente de presentación: etiquetas personalizadas y filtros de plantilla.
template.html
template_tags.py
Métodos de consulta
Si su consulta no es simplemente de presentación, puede agregar consultas a sus services.py (si está usando eso), o introducir un módulo queries.py :
queries.py
Modelos proxy
Los modelos proxy son muy útiles en el contexto de la lógica empresarial y los informes. Básicamente, define un subconjunto mejorado de su modelo. Puede anular el QuerySet base de un Administrador anulando el
Manager.get_queryset()
método.modelos.py
Modelos de consulta
Para consultas que son inherentemente complejas, pero que se ejecutan con bastante frecuencia, existe la posibilidad de modelos de consulta. Un modelo de consulta es una forma de desnormalización donde los datos relevantes para una sola consulta se almacenan en un modelo separado. El truco, por supuesto, es mantener el modelo desnormalizado sincronizado con el modelo primario. Los modelos de consulta solo se pueden usar si los cambios están completamente bajo su control.
modelos.py
La primera opción es actualizar estos modelos en sus comandos. Esto es muy útil si estos modelos solo se cambian con uno o dos comandos.
formas.py
Una mejor opción sería utilizar señales personalizadas. Estas señales son, por supuesto, emitidas por sus comandos. Las señales tienen la ventaja de que puede mantener múltiples modelos de consulta sincronizados con su modelo original. Además, el procesamiento de la señal se puede descargar a tareas en segundo plano, utilizando Celery o marcos similares.
señales.py
formas.py
modelos.py
Manteniéndolo limpio
Al usar este enfoque, resulta ridículamente fácil determinar si su código se mantiene limpio. Simplemente siga estas pautas:
Lo mismo ocurre con las vistas (porque las vistas a menudo sufren el mismo problema).
Algunas referencias
Documentación de Django: modelos proxy
Documentación de Django: señales
Arquitectura: diseño dirigido por dominios
fuente
User.objects.inactive_users()
. Pero el ejemplo del modelo de proxy aquí IMO conduce a una semántica incorrecta:u = InactiveUser.objects.all()[0]; u.active = True; u.save()
y aúnisinstance(u, InactiveUser) == True
. También mencionaría que una forma efectiva de mantener un modelo de consulta en muchos casos es con una vista db.Por lo general, implemento una capa de servicio entre vistas y modelos. Esto actúa como la API de su proyecto y le brinda una buena vista en helicóptero de lo que está sucediendo. Heredé esta práctica de un colega mío que usa mucho esta técnica de estratificación con proyectos Java (JSF), por ejemplo:
modelos.py
servicios.py
views.py
fuente
En primer lugar, no te repitas .
Entonces, tenga cuidado de no diseñar demasiado, a veces es solo una pérdida de tiempo y hace que alguien pierda el enfoque en lo que es importante. Revise el zen de Python de vez en cuando.
Echa un vistazo a los proyectos activos.
el repositorio de telas también es bueno para mirar.
yourapp/models/logicalgroup.py
User
,Group
y los modelos relacionados pueden ir debajoyourapp/models/users.py
Poll
,Question
,Answer
... podría pasar por debajoyourapp/models/polls.py
__all__
dentro deyourapp/models/__init__.py
Más acerca de MVC
request.GET
/request.POST
... etc.tastypie
opiston
Aproveche middleware / templatetags
Aproveche los gerentes de modelo
User
puede ir en aUserManager(models.Manager)
.models.Model
.queryset
podrían ir en amodels.Manager
.User
uno a la vez, por lo que puede pensar que debería vivir en el modelo en sí, pero al crear el objeto, probablemente no tenga todos los detalles:Ejemplo:
Haga uso de formularios siempre que sea posible
Se puede eliminar una gran cantidad de código repetitivo si tiene formularios que se asignan a un modelo. El
ModelForm documentation
es bastante bueno. Separar el código de los formularios del código del modelo puede ser bueno si tiene mucha personalización (o, a veces, evita errores de importación cíclicos para usos más avanzados).Use comandos de administración cuando sea posible
yourapp/management/commands/createsuperuser.py
yourapp/management/commands/activateinbulk.py
Si tiene lógica empresarial, puede separarla
django.contrib.auth
usa backends , al igual que db tiene un backend ... etc.setting
para su lógica de negocios (por ejemploAUTHENTICATION_BACKENDS
)django.contrib.auth.backends.RemoteUserBackend
yourapp.backends.remote_api.RemoteUserBackend
yourapp.backends.memcached.RemoteUserBackend
ejemplo de backend:
podría convertirse:
Más sobre patrones de diseño
Más información sobre los límites de la interfaz
yourapp.models
yourapp.vendor
yourapp.libs
yourapp.libs.vendor
oyourapp.vendor.libs
En resumen, podrías tener
yourapp/core/backends.py
yourapp/core/models/__init__.py
yourapp/core/models/users.py
yourapp/core/models/questions.py
yourapp/core/backends.py
yourapp/core/forms.py
yourapp/core/handlers.py
yourapp/core/management/commands/__init__.py
yourapp/core/management/commands/closepolls.py
yourapp/core/management/commands/removeduplicates.py
yourapp/core/middleware.py
yourapp/core/signals.py
yourapp/core/templatetags/__init__.py
yourapp/core/templatetags/polls_extras.py
yourapp/core/views/__init__.py
yourapp/core/views/users.py
yourapp/core/views/questions.py
yourapp/core/signals.py
yourapp/lib/utils.py
yourapp/lib/textanalysis.py
yourapp/lib/ratings.py
yourapp/vendor/backends.py
yourapp/vendor/morebusinesslogic.py
yourapp/vendor/handlers.py
yourapp/vendor/middleware.py
yourapp/vendor/signals.py
yourapp/tests/test_polls.py
yourapp/tests/test_questions.py
yourapp/tests/test_duplicates.py
yourapp/tests/test_ratings.py
o cualquier otra cosa que te ayude; Encontrar las interfaces que necesita y los límites lo ayudarán.
fuente
Django emplea un tipo de MVC ligeramente modificado. No hay concepto de un "controlador" en Django. El proxy más cercano es una "vista", que tiende a causar confusión con los conversos de MVC porque en MVC una vista se parece más a la "plantilla" de Django.
En Django, un "modelo" no es simplemente una abstracción de la base de datos. En algunos aspectos, comparte el deber con la "visión" de Django como el controlador de MVC. Contiene la totalidad del comportamiento asociado con una instancia. Si esa instancia necesita interactuar con una API externa como parte de su comportamiento, entonces ese sigue siendo el código del modelo. De hecho, no es necesario que los modelos interactúen con la base de datos, por lo que podría concebir modelos que existan completamente como una capa interactiva para una API externa. Es un concepto mucho más libre de un "modelo".
fuente
En Django, la estructura MVC es, como dijo Chris Pratt, diferente del modelo MVC clásico utilizado en otros marcos, creo que la razón principal para hacerlo es evitar una estructura de aplicación demasiado estricta, como sucede en otros marcos MVC como CakePHP.
En Django, MVC se implementó de la siguiente manera:
La capa de vista se divide en dos. Las vistas solo deben usarse para administrar solicitudes HTTP, se llaman y responden a ellas. Las vistas se comunican con el resto de su aplicación (formularios, formas de modelo, clases personalizadas, o en casos simples directamente con modelos). Para crear la interfaz usamos plantillas. Las plantillas tienen forma de cadena para Django, asigna un contexto en ellas, y la aplicación comunicó este contexto a la vista (cuando la vista pregunta).
La capa de modelo proporciona encapsulación, abstracción, validación, inteligencia y hace que sus datos estén orientados a objetos (dicen que algún día DBMS también lo hará). Esto no significa que deba crear grandes archivos de models.py (de hecho, un muy buen consejo es dividir sus modelos en diferentes archivos, colocarlos en una carpeta llamada 'modelos', hacer un archivo '__init__.py' en este carpeta donde importa todos sus modelos y finalmente usa el atributo 'app_label' de models.Model class). El modelo debe abstraerlo de la operación con datos, hará que su aplicación sea más simple. También debe, si es necesario, crear clases externas, como "herramientas" para sus modelos. También puede usar la herencia en los modelos, estableciendo el atributo 'abstracto' de la clase Meta de su modelo en 'Verdadero'.
¿Donde esta el resto? Bueno, las aplicaciones web pequeñas generalmente son una especie de interfaz para los datos, en algunos casos de programas pequeños usar vistas para consultar o insertar datos sería suficiente. Los casos más comunes utilizarán Formularios o ModelForms, que en realidad son "controladores". Esto no es otra cosa que una solución práctica a un problema común y muy rápido. Es lo que suele hacer un sitio web.
Si los formularios no son suficientes para usted, entonces debe crear sus propias clases para hacer la magia, un muy buen ejemplo de esto es la aplicación de administración: puede leer el código ModelAmin, esto realmente funciona como un controlador. No hay una estructura estándar, le sugiero que examine las aplicaciones existentes de Django, depende de cada caso. Esto es lo que pretendían los desarrolladores de Django, puede agregar una clase de analizador xml, una clase de conector API, agregar Celery para realizar tareas, retorcer para una aplicación basada en reactor, usar solo el ORM, hacer un servicio web, modificar la aplicación de administración y más. .. Es su responsabilidad crear un código de buena calidad, respetar la filosofía MVC o no, hacer que esté basado en módulos y crear sus propias capas de abstracción. Es muy flexible
Mi consejo: lea tanto código como pueda, hay muchas aplicaciones de Django, pero no las tome tan en serio. Cada caso es diferente, los patrones y la teoría ayudan, pero no siempre, esta es una idea imprecisa, django solo le proporciona buenas herramientas que puede usar para aliviar algunos dolores (como interfaz de administrador, validación de formularios web, i18n, implementación de patrones de observador, todos los mencionados anteriormente y otros), pero los buenos diseños provienen de diseñadores experimentados.
PD .: use la clase 'Usuario' de la aplicación de autenticación (de django estándar), puede hacer, por ejemplo, perfiles de usuario, o al menos leer su código, será útil para su caso.
fuente
Una vieja pregunta, pero me gustaría ofrecer mi solución de todos modos. Se basa en la aceptación de que los objetos modelo también requieren alguna funcionalidad adicional, mientras que es incómodo colocarlo dentro de models.py . La lógica comercial pesada puede escribirse por separado dependiendo del gusto personal, pero al menos me gusta que el modelo haga todo lo relacionado consigo mismo. Esta solución también es compatible con aquellos a quienes les gusta tener toda la lógica colocada dentro de los propios modelos.
Como tal, ideé un truco que me permite separar la lógica de las definiciones del modelo y aún obtener toda la indirecta de mi IDE.
Las ventajas deberían ser obvias, pero esto enumera algunas que he observado:
He estado usando esto con Python 3.4 y superior y Django 1.8 y superior.
app / models.py
app / logic / user.py
Lo único que no puedo entender es cómo hacer que mi IDE (PyCharm en este caso) reconozca que UserLogic es en realidad un modelo de usuario. Pero dado que esto es obviamente un truco, estoy muy contento de aceptar la pequeña molestia de especificar siempre el tipo de
self
parámetro.fuente
Tendre que estar de acuerdo contigo. Hay muchas posibilidades en django, pero el mejor lugar para comenzar es revisar la filosofía de diseño de Django .
Llamar a una API desde una propiedad de modelo no sería ideal, parece que tendría más sentido hacer algo como esto en la vista y posiblemente crear una capa de servicio para mantener las cosas secas. Si la llamada a la API no es de bloqueo y la llamada es costosa, puede tener sentido enviar la solicitud a un trabajador de servicio (un trabajador que consume de una cola).
Según la filosofía de diseño de Django, los modelos encapsulan cada aspecto de un "objeto". Entonces, toda la lógica de negocios relacionada con ese objeto debería vivir allí:
Los efectos secundarios que describe son evidentes, la lógica aquí podría dividirse mejor en conjuntos de consultas y administradores. Aquí hay un ejemplo:
modelos.py
admin.py
fuente
Principalmente estoy de acuerdo con la respuesta elegida ( https://stackoverflow.com/a/12857584/871392 ), pero quiero agregar la opción en la sección Hacer consultas.
Se pueden definir clases de QuerySet para modelos para realizar consultas de filtro, etc. Después de eso, puede proxy esta clase de conjunto de consultas para el administrador del modelo, como lo hacen las clases Build-in Manager y QuerySet.
Sin embargo, si tuviera que consultar varios modelos de datos para obtener un modelo de dominio, me parece más razonable poner esto en un módulo separado como se sugirió anteriormente.
fuente
Artículo más completo sobre las diferentes opciones con pros y contras:
Fuente: https://sunscrapers.com/blog/where-to-put-business-logic-django/
fuente
Django está diseñado para ser utilizado fácilmente para entregar páginas web. Si no se siente cómodo con esto, quizás debería usar otra solución.
Estoy escribiendo la raíz o las operaciones comunes en el modelo (para tener la misma interfaz) y las demás en el controlador del modelo. Si necesito una operación de otro modelo, importo su controlador.
Este enfoque es suficiente para mí y la complejidad de mis aplicaciones.
La respuesta de Hedde es un ejemplo que muestra la flexibilidad de django y python.
Muy interesante pregunta de todos modos!
fuente