models.py se está volviendo enorme, ¿cuál es la mejor manera de dividirlo?

91

Instrucciones de mi supervisor: "Quiero evitar poner lógica en el models.py. De aquí en adelante, usemos eso como clases únicas para acceder a la base de datos, y mantengamos toda la lógica en clases externas que usan las clases de modelos, o envuélvalas".

Siento que este es el camino equivocado. Siento que mantener la lógica fuera de los modelos solo para mantener el archivo pequeño es una mala idea. Si la lógica es mejor en el modelo, ahí es donde realmente debería ir independientemente del tamaño del archivo.

Entonces, ¿hay una forma sencilla de usar inclusiones? En lenguaje PHP, me gustaría proponerle al supervisor que acabamos de models.pyincluir () las clases modelo de otros lugares. Conceptualmente, esto permitiría que los modelos tuvieran toda la lógica que queremos, pero mantener el tamaño de archivo bajo aumentando el número de archivos (lo que conduce a menos problemas de control de revisión como conflictos, etc.).

Entonces, ¿hay una manera simple de eliminar las clases de modelo del archivo models.py, pero aún así hacer que los modelos funcionen con todas las herramientas de Django? ¿O hay una solución completamente diferente pero elegante para el problema general de un archivo models.py "grande"? Se agradecería cualquier aporte.

Edificado
fuente
7
Conoce la declaración de importación, ¿verdad?
Balpha
7
PD. No lo digo de manera ofensiva, solo quiero saber dónde estás.
Balpha
1
Sí, pero no sabía si las herramientas de administración de django funcionarían simplemente usando declaraciones de importación para extraer los modelos. Preferiría preguntar aquí que pasar mucho tiempo probando el uso de importaciones ole simples solo para descubrir que las herramientas de django no funcionan bien con ellas. Admito que soy más nuevo en python y django, por lo que probablemente solo tenga una comprensión simple de la declaración de importación ...
Edificado el

Respuestas:

64

Django está diseñado para permitirle construir muchas aplicaciones pequeñas en lugar de una gran aplicación.

Dentro de cada aplicación grande hay muchas aplicaciones pequeñas que luchan por ser gratuitas.

Si se models.pysiente grande, está haciendo demasiado. Detener. Relajarse. Descomponer.

Encuentre componentes o piezas de aplicaciones pequeñas más pequeñas y potencialmente reutilizables. Usted no tiene que realmente volver a utilizarlos. Piense en ellos como potencialmente reutilizables.

Considere sus rutas de actualización y descomponga las aplicaciones que quizás desee reemplazar algún día. Usted no tiene que realmente reemplazarlos, pero se puede considerar como un "módulo" independiente de programación que podrían quedar reemplazado con algo más fresco en el futuro.

Tenemos alrededor de una docena de aplicaciones, cada model.pyuna no tiene más de 400 líneas de código. Todos están bastante enfocados en menos de media docena de definiciones de clases discretas. (Estos no son límites estrictos, son observaciones sobre nuestro código).

Nos descomponemos temprano y con frecuencia.

S. Lot
fuente
1
justo en el punto. cualquier aplicación web no trivial serían varias 'aplicaciones' pequeñas. Eche un vistazo a contrib y otras aplicaciones populares, la autenticación de usuario es una aplicación, el etiquetado es otra, los perfiles de usuario una más, etc.
Javier
4
Si bien esta es la forma "correcta" y es útil saberlo, no es exactamente lo que estaba buscando. Pido disculpas si no había forma de saber qué tipo de respuesta estaba buscando. :)
Edificado el
@Eddified: si no haces esto, solo empeorará. Empiece a dividirse ahora.
S.Lott
Curiosamente, en este mismo momento estoy escuchando a Jacob Kaplan Moss (en OSCON) explicando exactamente esto con gran detalle y muy justificado ;-).
Alex Martelli
13
La respuesta de Glenn Maynard es mucho mejor en este caso. Dividir una aplicación web compleja en muchas aplicaciones es sin duda una buena práctica, pero también lo es refactorizar un archivo model.py DENTRO de una aplicación. Las dos acciones pueden ser ortogonales.
Erik
108

Es natural que las clases de modelo contengan métodos para operar en el modelo. Si tengo un modelo de libro, con un método book.get_noun_count(), ahí es donde pertenece - no quiero tener que escribir " get_noun_count(book)", a menos que el método realmente pertenezca intrínsecamente a algún otro paquete. (Podría, por ejemplo, si tengo un paquete para acceder a la API de Amazon con " get_amazon_product_id(book)").

Me estremecí cuando la documentación de Django sugirió poner modelos en un solo archivo, y me tomé unos minutos desde el principio para descubrir cómo dividirlo en un subpaquete adecuado.

site/models/__init__.py
site/models/book.py

__init__.py parece:

from .book import Book

así que todavía puedo escribir "from site.models import Book".


Lo siguiente solo es necesario para versiones anteriores a Django 1.7, consulte https://code.djangoproject.com/ticket/3591

El único truco es que necesita configurar explícitamente la aplicación de cada modelo, debido a un error en Django: asume que el nombre de la aplicación es la penúltima entrada en la ruta del modelo. "site.models.Book" da como resultado "sitio", que es correcto; "site.models.book.Book" hace pensar que el nombre de la aplicación es "modelos". Este es un truco bastante desagradable por parte de Django; probablemente debería buscar en la lista de aplicaciones instaladas una coincidencia de prefijo.

class Book(models.Model):
    class Meta: app_label = "site"

Probablemente podría usar una clase base o una metaclase para generalizar esto, pero todavía no me he molestado con eso.

Glenn Maynard
fuente
2
+1 He usado esto con éxito. Si bien S. Lott tiene razón en que múltiples aplicaciones son una buena idea, esta es la solución aquí y ahora.
Alexander Ljungberg
35
No veo mucho beneficio de dividir las cosas en un montón de aplicaciones, cuando sus modelos están estrecha e intrínsecamente relacionados.
Glenn Maynard
2
Esto me interesa. Leí el enlace de la wiki de django scompt publicado y encontré esto: "Se ha verificado que esto funciona sin la clase Meta app_labels, en la rama principal actual". Entonces, ¿eso significa que si está trabajando con la rama principal podemos descartar las cosas Meta: app_label? Es confuso, ya que está después del comentario sobre el ticket para resolver este problema.
Dan.StackOverflow
2
Acabo de probar con el tronco (a partir de hoy, r11286); si no se establece app_name, el modelo simplemente no aparece en "sqlall appname" y probablemente no será creado por syncdb (pero no lo uso, así que no puedo probarlo). Es un caso de error bastante confuso, porque no desencadena ningún error; simplemente no aparece en silencio.
Glenn Maynard
2
Vaya, casi 10 años después y todavía amo esta solución. Estuve de acuerdo en que es un enfoque mucho mejor que dividir su código en aplicaciones más pequeñas, lo que en mi opinión puede llevar a una base de código sobre la que es difícil razonar.
Michael Hays
5

No puedo entender cuál de los muchos posibles problemas puede tener. Aquí hay algunas posibilidades con respuestas:

  • varios modelos en el mismo archivo

    Colóquelos en archivos separados. Si hay dependencias, use la importación para extraer los modelos adicionales.

  • funciones de utilidad / lógica extraña en models.py

    Coloque la lógica adicional en archivos separados.

  • métodos estáticos para seleccionar algunas instancias de modelo de la base de datos

    Cree un nuevo administrador en un archivo separado.

  • métodos obviamente relacionados con el modelo

    save, __unicode__ y get_absolute_url son ejemplos.

hughdbrown
fuente