variable urls tutorial template examples django django-models django-templates django-views

django - urls - Mostrar objetos de diferentes modelos en la misma página de acuerdo con su fecha de publicación



django tutorial (6)

Tengo tres modelos diferentes para mi aplicación. Todos están trabajando como esperaba.

class Tender(models.Model): title = models.CharField(max_length=256) description = models.TextField() department = models.CharField(max_length=50) address = models.CharField(max_length=50) nature_of_work = models.CharField(choices=WORK_NATURE, max_length=1) period_of_completion = models.DateField() pubdat = models.DateTimeField(default=timezone.now) class Job(models.Model): user = models.ForeignKey(settings.AUTH_USER_MODEL) title = models.CharField(max_length=256) qualification = models.CharField(max_length=256) interview_type = models.CharField(max_length=2, choices=INTERVIEW_TYPE) type_of_job = models.CharField(max_length=1, choices=JOB_TYPE) number_of_vacancies = models.IntegerField() employer = models.CharField(max_length=50) salary = models.IntegerField() pubdat = models.DateTimeField(default=timezone.now) class News(models.Model): user = models.ForeignKey(settings.AUTH_USER_MODEL) title = models.CharField(max_length=150) body = models.TextField() pubdat = models.DateTimeField(default=timezone.now)

Ahora estoy mostrando cada uno de ellos en una página separada para cada uno de los modelos (por ejemplo, en la página de trabajos , estoy mostrando solo los trabajos). Pero ahora en la página de inicio, quiero mostrarlos de acuerdo a su fecha de publicación en la misma página. ¿Cómo puedo mostrar diferentes objetos de diferentes modelos en la misma página? ¿Hago un modelo separado, por ejemplo, class Post y luego uso la señal para crear una nueva publicación cada vez que se crea un nuevo objeto a partir de Tender , o Job , o News ? Realmente espero que haya una mejor manera de lograr esto. ¿O utilizo la herencia de mesas múltiples? Por favor, ayúdame. Gracias.

Actualizar:

No quiero mostrar cada uno de los objetos del modelo por separado en la misma página. Pero como feeds de Facebook o cualquier otra red social. Supongamos que en fb, cualquier publicación (ya sea una imagen, estado, compartir) se muestran juntas dentro de la página de inicio. Del mismo modo, en mi caso, supongamos que se creó un nuevo objeto Job, y luego se crea un nuevo objeto Noticias. Luego, quiero mostrar el objeto Noticias primero, y luego el objeto Trabajo, y así sucesivamente.


¿Hago un modelo separado, por ejemplo, clase Post y luego uso la señal para crear una nueva publicación cada vez que se crea un nuevo objeto a partir de licitación, o trabajo, o noticias? Realmente espero que haya una mejor manera de lograr esto. ¿O utilizo la herencia de mesas múltiples?

No quiero mostrar cada uno de los objetos del modelo por separado en la misma página. Pero como feeds de Facebook o cualquier otra red social.

Personalmente, no veo nada malo con otro modelo, en mi humilde opinión es incluso preferible utilizar otro modelo, especialmente cuando hay una aplicación para eso.

¿Por qué? Porque nunca me gustaría volver a escribir código para algo que se puede lograr al extender mi código actual. Estás sobreingeniendo este problema, y ​​si no ahora, vas a sufrir más tarde.


Una solución de trabajo

Hay dos soluciones de trabajo y otras dos respuestas. Ambos implican tres consultas . Y está consultando toda la tabla con .all() . Los resultados de estas consultas se combinaron en una sola lista. Si cada una de sus tablas tiene aproximadamente 10k registros, esto supondrá una carga enorme tanto para su servidor wsgi como para su base de datos. Incluso si cada tabla tiene solo 100 registros cada uno, estará repitiendo innecesariamente 300 veces en su vista. En breve respuesta lenta .

Una solución de trabajo eficiente.

La herencia de tablas múltiples es definitivamente el camino correcto si desea una solución que sea eficiente. Sus modelos pueden verse así:

class Post(models.Model): title = models.CharField(max_length=256) description = models.TextField() pubdat = models.DateTimeField(default=timezone.now, db_index = True) class Tender(Post): department = models.CharField(max_length=50) address = models.CharField(max_length=50) nature_of_work = models.CharField(choices=WORK_NATURE, max_length=1) period_of_completion = models.DateField() class Job(Post): user = models.ForeignKey(settings.AUTH_USER_MODEL) qualification = models.CharField(max_length=256) interview_type = models.CharField(max_length=2, choices=INTERVIEW_TYPE) type_of_job = models.CharField(max_length=1, choices=JOB_TYPE) number_of_vacancies = models.IntegerField() employer = models.CharField(max_length=50) salary = models.IntegerField() class News(models.Model): user = models.ForeignKey(settings.AUTH_USER_MODEL) def _get_body(self): return self.description body = property(_get_body)

ahora su consulta es simplemente

Post.objects.select_related( ''job'',''tender'',''news'').all().order_by(''-pubdat'') # you really should slice

El campo pubdat ahora está indexado (consulte el nuevo modelo de publicación que publiqué). Eso hace que la consulta sea realmente rápida. No hay iteración a través de todos los registros en python.

¿Cómo averiguas cuál es cuál en la plantilla? Con algo como esto

{% if post.tender %} {% else %} {% if post.news %} {% else %} {% else %}

Mayor optimización

Hay algo de espacio en su diseño para normalizar la base de datos. Por ejemplo, es probable que la misma empresa pueda publicar múltiples trabajos o licitaciones. Como tal, un modelo de compañía podría ser útil.

Una solución aún más eficiente.

¿Qué tal uno sin herencia de tablas múltiples o múltiples consultas de bases de datos? ¿Qué tal una solución donde incluso podría eliminar la sobrecarga de renderizar cada artículo individual?

Eso viene con la cortesía de los conjuntos clasificados redis . Cada vez que guardas un objeto Post , Job o News , lo agregas a un conjunto redis ordenado.

from django.db.models.signals import pre_delete, post_save from django.forms.models import model_to_dict @receiver(post_save, sender=News) @receiver(post_save, sender=Post) @receiver(post_save, sender=Job) def add_to_redis(sender, instance, **kwargs): rdb = redis.Redis() #instead of adding the instance, you can consider adding the #rendered HTML, that ought to save you a few more CPU cycles. rdb.zadd(key, instance.pubdat, model_to_dict(instance) if (rdb.zcard > 100) : # choose a suitable number rdb.zremrangebyrank(key, 0, 100)

Del mismo modo, debe agregar un pre_delete para eliminarlos de redis

La clara ventaja de este método es que no necesita ninguna consulta de base de datos y sus modelos continúan siendo muy simples + obtiene captura lanzada en la mezcla. Si estás en twitter, tu línea de tiempo probablemente se genere a través de un mecanismo similar a este.


Lo siguiente debería querer lo que necesita. Pero para mejorar el rendimiento, puede crear un campo de type adicional en cada uno de sus modelos para evitar la annotation .

Tu vista se verá algo así como:

from django.db.models import CharField def home(request): # annotate a type for each model to be used in the template tenders = Tender.object.all().annotate(type=Value(''tender'', CharField())) jobs = Job.object.all().annotate(type=Value(''job'', CharField())) news = News.object.all().annotate(type=Value(''news'', CharField())) all_items = list(tenders) + list(jobs) + list(news) # all items sorted by publication date. Most recent first all_items_feed = sorted(all_items, key=lambda obj: obj.pubdat) return render(request, ''home.html'', {''all_items_feed'': all_items_feed})

En su plantilla, los elementos vienen en el orden en que fueron ordenados (por recencia), y puede aplicar el html y el estilo apropiados para cada elemento al distinguir con el tipo de elemento:

# home.html {% for item in all_items_feed %} {% if item.type == ''tender'' %} {% comment "html block for tender items" %}{% endcomment %} {% elif item.type == ''news'' %} {% comment "html block for news items" %}{% endcomment %} {% else %} {% comment "html block for job items" %}{% endcomment %} {% endif %} {% endfor %}

Puede evitar la annotation por completo utilizando el atributo __class__ de los objetos del modelo para distinguirlos y colocarlos en el bloque html apropiado.

Para un objeto Tender , el item.__class__ será app_name.models.Tender donde app_name es el nombre de la aplicación Django que contiene el modelo.

Entonces, sin usar anotaciones en su vista de home , su plantilla se verá:

{% for item in all_items_feed %} {% if item.__class__ == ''app_name.models.Tender'' %} {% elif item.__class__ == ''app_name.models.News'' %} ... {% endif %} {% endfor %}

Con esto, ahorras una sobrecarga adicional en las anotaciones o tienes que modificar tus modelos.


No puedo probarlo ahora, pero debes crear un modelo como:

class Post(models.Model): pubdat = models.DateTimeField(default=timezone.now) tender = models.ForeignKey(''Tender'') job = models.ForeignKey(''Job'') news = models.ForeignKey(''News'')

Luego, cada vez que se crea un nuevo modelo, también se crea una Publicación y se relaciona con la Oferta / Trabajo / Noticias. Debe relacionar cada publicación con solo uno de los tres modelos.

Cree un serializador para publicación con serializadores con sangría para licitación, trabajo y noticias.

Perdón por la respuesta corta. Si crees que puede funcionar para tu problema, escribiré más tarde.


Una manera directa es usar la cadena en combinación con ordenada:

Ver

# your_app/views.py from django.shortcuts import render from itertools import chain from models import Tender, Job, News def feed(request): object_list = sorted(chain( Tender.objects.all(), Job.objects.all(), News.objects.all() ), key=lambda obj: obj.pubdat) return render(request, ''feed.html'', {''feed'': object_list})

Tenga en cuenta que los conjuntos de consultas mencionados anteriormente utilizando .all() deben entender como marcadores de posición. Al igual que con muchas entradas, esto podría ser un problema de rendimiento. El código de ejemplo evaluaría los conjuntos de consultas primero y luego los ordenaría. Hasta unos cientos de registros, es probable que no tengan un impacto (medible) en el rendimiento, pero en una situación con millones / miles de millones de entradas vale la pena mirar.

Para tomar una porción antes de ordenar utilice algo como:

Tender.objects.all()[:20]

o use un administrador personalizado para sus modelos para descargar la lógica.

class JobManager(models.Manager): def featured(self): return self.get_query_set().filter(featured=True)

Entonces puedes usar algo como:

Job.objects.featured()

Modelo

Si necesita lógica adicional según la clase de objeto, cree una etiqueta de plantilla simple:

#templatetags/ctype_tags.py from django import template register = template.Library() @register.filter def ctype(value): return value.__class__.__name__.lower()

y

#templates/feed.html {% load ctype_tags %} <div> {% for item in feed reversed %} <p>{{ item|ctype }} - {{ item.title }} - {{ item.pubdat }}</p> {% endfor %} </div>

Bonificación: combinar objetos con diferentes nombres de campo

A veces puede ser necesario crear este tipo de feeds con modelos existentes / de terceros. En ese caso, no tiene el mismo nombre de campo para ordenar por todos los modelos.

DATE_FIELD_MAPPING = { Tender: ''pubdat'', Job: ''publish_date'', News: ''created'', } def date_key_mapping(obj): return getattr(obj, DATE_FIELD_MAPPING[type(obj)]) def feed(request): object_list = sorted(chain( Tender.objects.all(), Job.objects.all(), News.objects.all() ), key=date_key_mapping)


Una solución alternativa sería usar el pajar de Django:

Le permite buscar a través de modelos no relacionados. Es más trabajo que las otras soluciones, pero es eficiente (1 consulta rápida) y también podrá filtrar fácilmente su listado.

En su caso, querrá definir pubdate en todos los índices de búsqueda.