Separación de lógica de negocio y acceso a datos en django


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 un cierto tiempo, dejo de entender lo que realmente está sucediendo.

Esto es lo que me molesta:

  1. 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) es también enviar correo electrónico, caminar en API a otros servicios, etc.
  2. 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 nuevos instancias de User, pero técnicamente debería crearlas uniformemente.
  3. No siempre me doy cuenta cuando los métodos y las propiedades de mis modelos se vuelven no deterministas y cuando se desarrollan efectos secundarios.

He aquí un ejemplo sencillo. Al principio, el modelo User 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 entidades en mi código:

  1. Entidades de mi base de datos, nivel de base de datos: ¿Qué contiene mi aplicación?
  2. Entidades de mi aplicación, nivel de lógica de negocio: ¿Qué puede hacer mi aplicación?

¿Cuáles son las buenas prácticas para implementar tal enfoque que se puede aplicar en Django?

Author: Adam, 2012-09-25

8 answers

Parece que está preguntando sobre la diferencia entre el modelo de datos y el modelo de dominio : este último es donde puede encontrar la lógica de negocios y las entidades percibidas por su usuario final, el primero es donde realmente almacena sus datos.

Además, he interpretado la 3a parte de su pregunta como: cómo notar la falta de mantener estos modelos separados.

Estos son dos conceptos muy diferentes y siempre es difícil mantenerlos separado. Sin embargo, hay algunos patrones comunes y herramientas que se pueden utilizar para este propósito.

Acerca del 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 los términos clásicos: se trata de consultas y comandos.

Pensando en Órdenes

Comencemos por mirar los comandos en su ejemplo: "activar este usuario" y "desactivar este usuario". Lo bueno de los comandos es que pueden ser fácilmente expresados por pequeños escenarios dados-cuando-entonces:

Dado un usuario inactivo
cuando la administración activa de este usuario
entonces el usuario se convierte en activo
y un e-mail de confirmación se envía al usuario
y se agrega una entrada al registro del sistema
(sucesivamente. sucesivamente.)

Estos 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, su registro del sistema, etc.

Este escenario también le ayuda realmente a configurar un entorno de Desarrollo impulsado por pruebas.

Y finalmente, pensar en comandos realmente te ayuda a crear un aplicación orientada a tareas. Sus usuarios apreciarán esto: -)

Expresando Órdenes

Django proporciona dos formas fáciles de expresar comandos; ambas 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í se define un módulo separado y cada comando se representa como una función.

Services.py

def activate_user(user_id):
    user = User.objects.get(pk=user_id)

    # set active flag
    user.active = True
    user.save()

    # mail user
    send_mail(...)

    # etc etc

Usando formularios

La otra forma es usar un formulario Django para cada comando. Prefiero este enfoque, porque combina múltiples aspectos estrechamente relacionados:

  • ejecución del comando (¿qué hace?)
  • validación de los parámetros del comando (¿puede hacer esto?)
  • presentación del comando (¿cómo puedo hacer esto?)

Forms.py

class ActivateUserForm(forms.Form):

    user_id = IntegerField(widget = UsernameSelectWidget, verbose_name="Select a user to activate")
    # the username select widget is not a standard Django widget, I just made it up

    def clean_user_id(self):
        user_id = self.cleaned_data['user_id']
        if User.objects.get(pk=user_id).active:
            raise ValidationError("This user cannot be activated")
        # you can also check authorizations etc. 
        return user_id

    def execute(self):
        """
        This is not a standard method in the forms API; it is intended to replace the 
        'extract-data-from-form-in-view-and-do-stuff' pattern by a more testable pattern. 
        """
        user_id = self.cleaned_data['user_id']

        user = User.objects.get(pk=user_id)

        # set active flag
        user.active = True
        user.save()

        # mail user
        send_mail(...)

        # etc etc

Pensando en Consultas

Tu ejemplo no contenía ninguna consulta, así que me tomé la libertad de hacer algunas consultas útiles. Prefiero usar el término "pregunta", pero 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?", "Show me a list of deactivated users", y "What is the geographical distribution of deactivated users?"

Antes de embarcarse en responder a estas preguntas, siempre debe hacerse dos preguntas: es esta es una consulta presentacional solo para mis plantillas, y/o una consulta lógica de negocios vinculada a la ejecución de mis comandos, y/o una consulta reporting.

Las consultas de presentación se realizan simplemente para mejorar la interfaz de usuario. Las respuestas a las consultas de lógica empresarial afectan directamente a la ejecución de sus comandos. Las consultas de presentación de informes son meramente para fines analíticos y tienen limitaciones de tiempo más flexibles. Estas categorías no se excluyen mutuamente.

El 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 dependemos de una API externa.

Haciendo consultas

La consulta más básica en Django es el uso del objeto Manager:

User.objects.filter(active=True)

Por supuesto, esto solo funciona si los datos están realmente representados en su modelo de datos. No siempre es así. En esos casos, puede considerar las opciones debajo.

Etiquetas y filtros personalizados

La primera alternativa es útil para consultas que son meramente de presentación: etiquetas personalizadas y filtros de plantilla.

Plantilla.html

<h1>Welcome, {{ user|friendly_name }}</h1>

Template_tags.py

@register.filter
def friendly_name(user):
    return remote_api.get_cached_name(user.id)

Métodos de consulta

Si su consulta no es meramente de presentación, puede agregar consultas a su services.py (si está usando eso), o introduzca un queries.py módulo:

Queries.py

def inactive_users():
    return User.objects.filter(active=False)


def users_called_publysher():
    for user in User.objects.all():
        if remote_api.get_cached_name(user.id) == "publysher":
            yield user 

Modelos de proxy

Los modelos proxy son muy útiles en el contexto de la lógica de negocio y los informes. Básicamente defines un subconjunto mejorado de tu modelo. Puede anular el conjunto de consultas base de un Administrador Manager.get_queryset() método.

Models.py

class InactiveUserManager(models.Manager):
    def get_queryset(self):
        query_set = super(InactiveUserManager, self).get_queryset()
        return query_set.filter(active=False)

class InactiveUser(User):
    """
    >>> for user in InactiveUser.objects.all():
    …        assert user.active is False 
    """

    objects = InactiveUserManager()
    class Meta:
        proxy = True

Modelos de consulta

Para las 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 en sincronización con el modelo primario. Los modelos de consulta solo se pueden usar si los cambios están completamente bajo su control.

Models.py

class InactiveUserDistribution(models.Model):
    country = CharField(max_length=200)
    inactive_user_count = IntegerField(default=0)

La primera opción es actualizar estos modelos en sus comandos. Esto es muy útil si estos modelos solo se cambian por uno o dos comandos.

Forms.py

class ActivateUserForm(forms.Form):
    # see above

    def execute(self):
        # see above
        query_model = InactiveUserDistribution.objects.get_or_create(country=user.country)
        query_model.inactive_user_count -= 1
        query_model.save()

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 varios modelos de consulta sincronizados con su modelo original. Además, el procesamiento de señales se puede descargar a tareas en segundo plano, utilizando Apio o marcos similares.

Signals.py

user_activated = Signal(providing_args = ['user'])
user_deactivated = Signal(providing_args = ['user'])

Forms.py

class ActivateUserForm(forms.Form):
    # see above

    def execute(self):
        # see above
        user_activated.send_robust(sender=self, user=user)

Models.py

class InactiveUserDistribution(models.Model):
    # see above

@receiver(user_activated)
def on_user_activated(sender, **kwargs):
        user = kwargs['user']
        query_model = InactiveUserDistribution.objects.get_or_create(country=user.country)
        query_model.inactive_user_count -= 1
        query_model.save()

Manteniéndolo limpio

Cuando se utiliza este enfoque, se vuelve ridículamente fácil determinar si su código se mantiene limpio. Solo siga estas pautas:

  • ¿Mi modelo contiene métodos que hacen más que administrar el estado de la base de datos? Deberías extraer una orden.
  • ¿Mi modelo contiene propiedades que no se asignan a la base de datos ¿fields? Debe extraer una consulta.
  • ¿Hace referencia mi modelo a una infraestructura que no es mi base de datos (como el correo)? Deberías extraer una orden.

Lo mismo ocurre con las vistas (porque las vistas a menudo sufren del mismo problema).

  • ¿Mi vista administra activamente los modelos de bases de datos? Deberías extraer una orden.

Algunas referencias

Documentación de Django: modelos proxy

Documentación de Django: señales

Arquitectura: Domain Driven Design

 462
Author: publysher,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-28 14:40:31

Suelo implementar una capa de servicio entre vistas y modelos. Esto actúa como la API de su proyecto y le da una buena vista en helicóptero de lo que está sucediendo. Heredé esta práctica de un colega mío que usa esta técnica de capas mucho con proyectos Java (JSF), por ejemplo:

Models.py

class Book:
   author = models.ForeignKey(User)
   title = models.CharField(max_length=125)

   class Meta:
       app_label = "library"

Services.py

from library.models import Book

def get_books(limit=None, **filters):
    """ simple service function for retrieving books can be widely extended """
    if limit:
        return Book.objects.filter(**filters)[:limit]
    return Book.objects.filter(**filters)

Views.py

from library.services import get_books

class BookListView(ListView):
    """ simple view, e.g. implement a _build and _apply filters function """
    queryset = get_books()

Eso sí, normalmente llevo modelos, vistas y servicios a nivel de módulo y separar aún más dependiendo del tamaño del proyecto

 106
Author: Hedde van der Heide,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-25 09:25:37

En primer lugar, No te repitas.

Entonces, por favor, tenga cuidado de no sobreingenieros, a veces es solo una pérdida de tiempo, y hace que alguien pierda el enfoque en lo que es importante. Revisa el zen de python de vez en cuando.

Echa un vistazo a los proyectos activos

  • más personas = más necesidad de organizarse correctamente
  • el repositorio django tienen una estructura sencilla.
  • el pip repository tienen una estructura de directorios straigtforward.
  • El repositorio de tela también es bueno para mirar.

    • puedes colocar todos tus modelos bajo yourapp/models/logicalgroup.py
  • por ejemplo User, Group y los modelos relacionados pueden ir bajo yourapp/models/users.py
  • por ejemplo Poll, Question, Answer ... podría ir por debajo yourapp/models/polls.py
  • carga lo que necesitas en __all__ dentro de yourapp/models/__init__.py

Más sobre MVC

  • el modelo es sus datos
    • esto incluye sus datos reales
    • esto también incluye sus datos de sesión / cookie / cache / fs / index
  • el usuario interactúa con el controlador para manipular el modelo
    • esto podría ser una API, o una vista que guarda/actualiza sus datos
    • esto se puede afinar con request.GET / request.POST ...etc
    • creo paginación o filtrado demasiado.
  • los datos actualizan la vista
    • las plantillas tomar los datos y formatearlos en consecuencia
    • Las API incluso sin plantillas son parte de la vista; por ejemplo, tastypie o piston
    • esto también debería tener en cuenta el middleware.

Tome ventaja de middleware / templatetags

  • Si necesita hacer algún trabajo para cada solicitud, middleware es una forma de hacerlo.
    • por ejemplo, añadiendo marcas de tiempo
    • por ejemplo, actualización de métricas sobre visitas a páginas
    • e. g. rellenar un caché
  • Si tiene fragmentos de código que siempre vuelven a aparecer para formatear objetos, las etiquetas de plantilla son buenas.
    • e. g. active tab / url breadcrumbs

Aproveche los gestores de modelos

  • crear User puede ir en un UserManager(models.Manager).
  • los detalles sangrientos para las instancias deben ir en models.Model.
  • detalles sangrientos para queryset podrían ir en un models.Manager.
  • es posible que desee crear un 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:

class UserManager(models.Manager):
   def create_user(self, username, ...):
      # plain create
   def create_superuser(self, username, ...):
      # may set is_superuser field.
   def activate(self, username):
      # may use save() and send_mail()
   def activate_in_bulk(self, queryset):
      # may use queryset.update() instead of save()
      # may use send_mass_mail() instead of send_mail()

Utilizar formularios cuando 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 tienes mucha personalización (o a veces evitas cíclico importar errores para usos más avanzados).

Use comandos de administración cuando sea posible

  • por ejemplo, yourapp/management/commands/createsuperuser.py
  • por ejemplo, yourapp/management/commands/activateinbulk.py

Si tienes lógica de negocios, puedes separarla

  • django.contrib.auth usa backends , al igual que db tiene un backend...sucesivamente.
  • agregue un setting para su lógica de negocio (por ejemplo, AUTHENTICATION_BACKENDS)
  • podrías usar django.contrib.auth.backends.RemoteUserBackend
  • podrías usar yourapp.backends.remote_api.RemoteUserBackend
  • usted podría use yourapp.backends.memcached.RemoteUserBackend
  • delegar la lógica de negocio difícil al backend
  • asegúrese de establecer la expectativa correcta en la entrada/salida.
  • cambiar la lógica de negocio es tan simple como cambiar una configuración:)

Ejemplo de Backend:

class User(db.Models):
    def get_present_name(self): 
        # property became not deterministic in terms of database
        # data is taken from another service by api
        return remote_api.request_user_name(self.uid) or 'Anonymous' 

Podría convertirse en:

class User(db.Models):
   def get_present_name(self):
      for backend in get_backends():
         try:
            return backend.get_present_name(self)
         except: # make pylint happy.
            pass
      return None

Más sobre los patrones de diseño

Más sobre límites de interfaz

  • Es el código que desea utilizar parte de los modelos? -> yourapp.models
  • Es el código parte de la lógica de negocio? -> yourapp.vendor
  • ¿Es el código parte de generic tools / libs? -> yourapp.libs
  • ¿Es el código parte de business logic libs? - > yourapp.libs.vendor o yourapp.vendor.libs
  • Aquí hay una buena: ¿puede probar su ¿código independientemente?
    • sí, bueno :)
    • no, es posible que tenga un problema de interfaz
    • cuando hay una separación clara, unittest debe ser una brisa con el uso de burlarse
  • ¿Es lógica la separación?
    • sí, bueno :)
    • no, es posible que tenga problemas para probar esos conceptos lógicos por separado.
  • ¿Crees que necesitarás refactorizar cuando obtengas 10 veces más código?
    • sí, no bueno, no bueno, refactor podría ser un montón de trabajo
    • no, eso es simplemente impresionante!

En resumen, usted podría tienen

  • 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 le ayude; encontrar las interfaces que necesita y los límites le ayudará.

 51
Author: dnozay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:54:59

Django emplea un tipo ligeramente modificado de MVC. No hay concepto de "controlador" en Django. El proxy más cercano es una "vista", que tiende a causar confusión con MVC converts porque en MVC una vista es más como la"plantilla" de Django.

En Django, un "modelo" no es simplemente una abstracción de base de datos. En algunos aspectos, comparte el deber con la "vista" de Django como el controlador de MVC. Contiene la totalidad del comportamiento asociado a una instancia. Si esa instancia necesita interactuar con un API externa como parte de su comportamiento, entonces eso sigue siendo código de modelo. De hecho, los modelos no están obligados a interactuar con la base de datos en absoluto, por lo que podría concebirse tener modelos que existen completamente como una capa interactiva a una API externa. Es un concepto mucho más libre de un"modelo".

 21
Author: Chris Pratt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-25 14:40:12

En Django, la estructura MVC es como dijo Chris Pratt, diferente del modelo MVC clásico utilizado en otros frameworks, creo que la razón principal para hacer esto es evitar una estructura de aplicación demasiado estricta, como sucede en otros frameworks MVC como CakePHP.

En Django, MVC se implementó de la siguiente manera:

La capa de vista se divide en dos. Las vistas se deben usar solo para administrar solicitudes HTTP, se llaman y responden a ellas. Vistas comunicarse con el resto de su aplicación (formularios, modelforms, clases personalizadas, o en casos simples directamente con modelos). Para crear la interfaz utilizamos Plantillas. Las plantillas son como cadenas para Django, mapea un contexto dentro de ellas, y este contexto fue comunicado a la vista por la aplicación (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 usted debe hacer enorme models.py archivos (en hecho un muy buen consejo es dividir sus modelos en diferentes archivos, ponerlos en una carpeta llamada 'modelos', hacer un '__init__.py ' archivo en esta carpeta donde se importan todos los modelos y finalmente utilizar el atributo 'app_label' de los modelos.Clase modelo). El modelo debe abstraerle de operar 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 utilizar heritage en los modelos, estableciendo el atributo 'abstract' de la Meta clase de tu modelo a 'True'.

¿Dónde está el resto? Bueno, las aplicaciones web pequeñas generalmente son una especie de interfaz para datos, en algunos casos de programas pequeños usar vistas para consultar o insertar datos sería suficiente. Los casos más comunes usarán Formularios o ModelForms, que en realidad son "controladores". Esto no es más que una solución práctica a un problema común, y muy rápido. Es lo que un sitio web utiliza para hacer.

Si los formularios no son enogh para usted, entonces debe crear su propio clases para hacer la magia, un muy buen ejemplo de esto es la aplicación admin: se puede leer código ModelAmin, esto en realidad funciona como un controlador. No hay una estructura estándar, le sugiero que examine las aplicaciones Django existentes, depende de cada caso. Esto es lo que los desarrolladores de Django pretendían, puede agregar una clase de analizador xml, una clase de conector API, agregar Apio para realizar tareas, retorcido para una aplicación basada en reactor, usar solo el OR, hacer un servicio web, modificar la aplicación de administración y más... Es tu responsabilidad hacer código de buena calidad, respetar la filosofía MVC o no, hacerlo basado en módulos y crear tus propias capas de abstracción. Es muy flexible.

Mi consejo: lee tanto código como puedas, hay muchas aplicaciones de django alrededor, pero no las tomes tan en serio. Cada caso es diferente, los patrones y la teoría ayuda, pero no siempre, esto es una ciencia imprecisa, django solo le proporciona buenas herramientas que puede usar para aliviar algunos dolores (como la interfaz de administración, la web validación de formularios, i18n, implementación de patrones de observador, todo lo mencionado anteriormente y otros), pero los buenos diseños provienen de diseñadores experimentados.

PS.: utilice la clase 'User' de la aplicación auth (de django estándar), puede hacer por ejemplo perfiles de usuario, o al menos leer su código, será útil para su caso.

 5
Author: Nate Gentile,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-17 07:14:59

Estoy mayormente de acuerdo con la respuesta elegida ( https://stackoverflow.com/a/12857584/871392 ), pero desea agregar una opción en la sección Hacer consultas.

Se pueden definir clases QuerySet para modelos para realizar consultas de filtro y son on. Después de eso, puede proxy esta clase queryset para el administrador del modelo, como lo hacen las clases build-in Manager y QuerySet.

Aunque, si tuviera que consultar varios modelos de datos para obtener un modelo de dominio, me parece más razonable poner esto por separado módulo como se sugirió antes.

 0
Author: l0ki,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:18:21

Una vieja pregunta, pero me gustaría ofrecer mi solución de todos modos. Se basa en la aceptación de que los objetos del modelo también requieren alguna funcionalidad adicional, mientras que es incómodo colocarlos dentro de models.py . La lógica de los negocios pesados se puede escribir por separado dependiendo del gusto personal, pero al menos me gusta el modelo para hacer todo lo relacionado con sí mismo. Esta solución también es compatible con aquellos que les gusta tener toda la lógica colocada dentro de los propios modelos.

Como tal, ideé un hack que me permite separar la lógica de las definiciones del modelo y aún así obtener todas las insinuaciones de mi IDE.

Las ventajas deberían ser obvias, pero esto enumera algunas que he observado:

  • Las definiciones de DB siguen siendo solo eso-no hay lógica "basura" adjunta
  • La lógica relacionada con el modelo se coloca cuidadosamente en un solo lugar
  • Todos los servicios (formularios, REST, vistas) tienen un único punto de acceso a logic
  • Lo mejor de todo: No tuve que reescribir ninguna código una vez que me di cuenta de que mi models.py se volvió demasiado desordenado y tuvo que separar la lógica. La separación es suave e iterativa: podría hacer una función a la vez o toda la clase o la totalidad models.py.

He estado usando esto con Python 3.4 y superior y Django 1.8 y superior.

App/models.py

....
from app.logic.user import UserLogic

class User(models.Model, UserLogic):
    field1 = models.AnyField(....)
    ... field definitions ...

App/logic/user.py

if False:
    # This allows the IDE to know about the User model and its member fields
    from main.models import User

class UserLogic(object):
    def logic_function(self: 'User'):
        ... code with hinting working normally ...

Lo único que no puedo averiguar es cómo hacer que mi IDE (PyCharm en este caso) reconozca esa lógica de usuario es en realidad modelo de usuario. Pero como esto es obviamente un truco, estoy muy feliz de aceptar la pequeña molestia de especificar siempre el tipo para el parámetro self.

 0
Author: velis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-27 08:00:23

Django está diseñado para ser fácilmente utilizado para entregar páginas web. Si no está cómodo con esto, tal vez debería usar otra solución.

Estoy escribiendo la raíz u 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 flexibilidad de django y python.

Pregunta muy interesante de todos modos!

 -2
Author: pvilas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-25 09:40:37