2009-07-08 10 views
14

Estoy desarrollando una aplicación web simple, y tiene mucho sentido almacenar algunos datos desnormalizados.¿La mejor manera de desnormalizar datos en Django?

Imagine una plataforma de blogs que realiza un seguimiento de los Comentarios, y el modelo BlogEntry tiene un campo "CommentCount" que me gustaría mantener actualizado.

Una forma de hacerlo sería usar señales de Django.

Otra forma de hacer esto sería poner ganchos directamente en mi código que crea y elimina objetos Comment para llamar de forma síncrona algunos métodos en BlogEntry para incrementar/disminuir el conteo de comentarios.

Supongo que hay otras formas pitónicas de lograr esto con los decoradores o algún otro vudú.

¿Cuál es el patrón de diseño estándar para desnormalizar en Django? En la práctica, ¿también tiene que escribir comprobadores de consistencia y arregladores de datos en caso de errores?

Respuesta

17

Tienes administradores en Django.

Utilice un administrador personalizado para crear y mantener las relaciones FK.

El administrador puede actualizar los recuentos a medida que se actualizan los conjuntos de elementos secundarios.

Si no desea crear administradores personalizados, simplemente amplíe el método save. Todo lo que desea hacer para desnormalizar recuentos y sumas se puede hacer en save.

No necesita señales. Solo extienda save.

+0

Un gran consejo, eso es lo que hice también – kender

+0

Tomo este enfoque también, no he tenido problemas hasta el momento. – Prairiedogg

+4

¿Conoces algún buen ejemplo de este estilo? Me sorprende que la documentación de Django (o Django Book) no mencione en absoluto los enfoques de desnormalización ... – slacy

-1

Por qué no acaba de obtener el conjunto de los comentarios, y encontrar el número de elementos, utilizando el método count():

count = blog_entry.comment_set.count() 

entonces puede pasar que en su plantilla.

O, alternativa, en la propia plantilla, que puede hacer:

{{ blog_entry.comment_set.count }} 

para obtener el número de comentarios.

+0

Cada vez que llamo count() que va a hacer un "SELECT count (1) de la Observación, donde ..." que terminan causando problemas de rendimiento cuando hay un gran número de observaciones. – slacy

+1

¿Cuántas personas están dejando comentarios en tu blog? – mipadi

4

El primer enfoque (señales) tiene la ventaja de perder el acoplamiento entre los modelos.
Sin embargo, las señales son de alguna manera más difíciles de mantener, porque las dependencias son menos explícitas (al menos, en mi opinión).
Si la corrección del recuento de comentarios no es tan importante, también podría pensar en un trabajo cron que lo actualizará cada n minutos.

Sin embargo, no importa la solución, desnormalización hará que el mantenimiento sea más difícil; por esta razón, intentaría evitarlo tanto como sea posible, resolviendo en cambio usar cachés u otras técnicas; por ejemplo, usar with comments.count as cnt en plantillas puede mejorar bastante el rendimiento.
Luego, si todo lo demás falla, y solo en ese caso, piense cuál podría ser el mejor enfoque para el problema específico.

+0

Entiendo los pormenores de la normalización (y desnormalización) de datos, pero hay muchos casos en que los datos desnormalizados pueden aumentar en gran medida el rendimiento de las consultas, por lo que estoy pensando en ello. Mi ejemplo de "recuento de comentarios" es sintético, pero sirve como un buen ejemplo para cualquier propuesta de desnormalización. El almacenamiento en caché es una gran idea, y comenzaré a considerar que ... – slacy

+0

El almacenamiento en caché tendrá todos los problemas de mantenimiento de la desnormalización, es decir, mantendrá el caché al día e invalidará los datos en caché cuando corresponda. Peor aún, no tendrá el beneficio del aparato Django ORM para hacerlo. Mi voto sería para django-denorm como sugirió @gorsky: se ocupa de todos los problemas de mantenimiento si tiene uno de los casos de uso de desnormalización que cubre. – Anentropic

9

He encontrado django-denorm para ser útil. Utiliza desencadenadores de nivel de base de datos en lugar de señales, pero hasta donde yo sé, también hay una rama basada en un enfoque diferente.

+0

+1 para django-denorm en lugar de piratear manualmente sus propias señales y métodos anulados, es un sistema excelente y fácil. – Anentropic

+0

Miré en el código fuente django-denorm. No veo ganchos para eliminar operaciones ... ¿Sabes si están gestionados? También me parece que no se utilizan desencadenadores de base de datos, pero esto no está mal. Una tabla ordinaria se actualiza mediante métodos de guardado posterior en los campos modelo. –

1

Django ofrece una alternativa excelente y eficiente (aunque no muy conocida) a contra desnormalización.

Se guardará sus muchas líneas de código y es muy lento ya que recuperar el recuento en la misma consulta SQL.

Voy a suponer que usted tiene estas clases:

class BlogEntry(models.Model): 
    title = models.CharField() 
    ... 


class Comment(models.Model): 
    body = models.TextField() 
    blog_entry = models.ForeignKey(BlogEntry) 

En su views.py, utilice annotations:

from django.db.models import Count 

def blog_entry_list(Request): 
    blog_entries = BlogEntry.objects.annotate(count=Count('comment_set')).all() 
    ... 

y usted tendrá un campo extra por cada BlogEntry, que contiene el recuento de comentarios, más el resto de los campos de BlobEntry.

Puede utilizar este campo extra en las plantillas también:

{% for blog_entry in blog_entries %} 
    {{ blog_entry.title }} has {{ blog_entry.count }} comments! 
{% endfor %} 

Esto no sólo le ahorrará la codificación y el tiempo de mantenimiento, pero es muy eficiente (la consulta tarda sólo un poco más en ser ejecutado).

Cuestiones relacionadas