Django 根据发布日期在同一页面上显示不同模型的对象

Django 根据发布日期在同一页面上显示不同模型的对象,django,django-models,django-templates,django-views,Django,Django Models,Django Templates,Django Views,我的应用程序有三种不同的型号。一切都如我所料 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 = m

我的应用程序有三种不同的型号。一切都如我所料

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)
现在,我在每个模型的单独页面上显示它们(例如,在作业页面中,我仅显示作业)。但是现在在主页上,我想根据它们在同一页面上的发布日期显示它们。如何在同一页面上显示来自不同模型的不同对象?我是否制作一个单独的模型,例如,
类帖子
,然后在从
投标
、或
作业
、或
新闻
创建新对象时使用信号创建新帖子?我真的希望有更好的方法来实现这一点。还是使用多表继承?请帮帮我。多谢各位

更新:


我不想在同一页上分别显示每个模型对象。但就像facebook或其他社交媒体的订阅源一样。假设在fb中,任何帖子(无论是图像、状态、共享)都显示在主页中。同样,在我的例子中,假设创建了一个新的Job对象,然后创建了一个新的News对象。然后,我想先显示News对象,然后显示Job对象,依此类推。

我现在无法测试它,但您应该创建一个如下模型:

class Post(models.Model):

    pubdat = models.DateTimeField(default=timezone.now)
    tender = models.ForeignKey('Tender')
    job = models.ForeignKey('Job')
    news = models.ForeignKey('News')
然后,每次创建一个新模型时,您也会创建一个帖子,并将其与投标/工作/新闻关联起来。您应该将每个帖子仅与三个模型中的一个关联

为文章创建一个序列化程序,其中包含投标、作业和新闻的缩进序列化程序


很抱歉回答得这么简短。如果你认为它可以解决你的问题,我稍后会写更多。

以下内容应该是你需要的。但是为了提高性能,您可以在每个模型中创建一个额外的
类型
字段,这样就可以避免
注释

您的视图将类似于:

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})
Tender.objects.all()[:20]
Job.objects.featured()

在模板中,项目按排序顺序排列(按最近情况),您可以通过区分项目类型为每个项目应用适当的html和样式:

# 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 %}

您可以通过使用模型对象的
\uuuuu class\uuuuu
属性来区分并将它们放入适当的html块中,从而完全避免使用
注释

对于
Tender
对象,
项.\uuuu class\uuuuu
将是
app\u name.models。Tender
其中
app\u name
是包含模型的Django应用程序的名称

因此,如果不在
主页
视图中使用注释,您的模板将看起来:

{% for item in all_items_feed %}
    {% if item.__class__ == 'app_name.models.Tender' %}

    {% elif item.__class__ == 'app_name.models.News' %}
     ...
    {% endif %}
{% endfor %}
这样,您就可以节省注释或修改模型的额外开销。

一个可行的解决方案 有两个可行的解决方案,另外两个答案。这两个问题都涉及三个问题。您正在使用
.all()
查询整个表。这些查询的结果合并到一个列表中。如果每个表都有大约10k条记录,这将给wsgi服务器和数据库带来巨大的压力。即使每个表只有100条记录,在视图中也不需要循环300次。简言之,反应缓慢

有效的工作解决方案。 如果您想要一个高效的解决方案,多表继承无疑是正确的选择。您的模型可能如下所示:

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)
现在您的查询很简单

 Post.objects.select_related(
   'job','tender','news').all().order_by('-pubdat')   # you really should slice
pubdat
字段现在已编入索引(请参阅我发布的新Post模型)。这使得查询速度非常快。python中没有对所有记录进行迭代

如何找出模板中的哪个是哪个?用这样的东西

{% if post.tender %}

{% else %} 
   {% if post.news %}
   {% else %}
{% else %}
进一步优化 在您的设计中有一些空间来规范化数据库。例如,同一家公司可能会发布多个职位或标书。因此,这样的公司模式可能是有用的

更有效的解决方案。 一个没有多表继承或多数据库查询的如何?一个甚至可以消除渲染每个单独项目的开销的解决方案怎么样

这是我的礼貌。每次保存
帖子
作业
新闻
对象时,都会将其添加到redis排序集

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)
同样,您需要添加一个pre_delete来将它们从redis中删除

这种方法的明显优点是,您根本不需要任何数据库查询,而且您的模型仍然非常简单,而且您可以在混合中使用。如果你在twitter上,你的时间线可能是通过类似的机制生成的

我是否制作了一个单独的模型,例如班级张贴,然后使用信号 无论何时从Tender创建新对象,都要创建新帖子,或者 工作,还是新闻?我真的希望有更好的方法来实现这一点。或 我是否使用多表继承

我不想同时分别显示每个模型对象 页但就像facebook或其他社交媒体的订阅源一样

我个人认为使用另一个模型没有任何错误,我认为使用另一个模型更可取,特别是当有其他模型的时候


为什么??因为我永远都不想为通过扩展我当前的代码而实现的东西重写代码。你在设计这个问题,如果不是现在,你以后会受到影响。

一个简单的方法是结合使用:

看法 请注意-上述使用
.all()
的查询集应理解为占位符。与许多条目一样,这可能是一个性能问题。示例代码将首先计算查询集,然后对它们进行排序。多达数百条记录可能不会对性能产生(可测量的)影响,但在有数百万/数十亿条记录的情况下,它值得一看

要在排序前获取切片,请使用以下方法:

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})
Tender.objects.all()[:20]
Job.objects.featured()
或者为您的模型使用自定义逻辑来卸载逻辑

class JobManager(models.Manager):
    def featured(self):
        return self.get_query_set().filter(featured=True)
然后你可以用
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)