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

时间:2016-06-10 11:43:23

标签: 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 = 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)

现在,我在每个模型的单独页面上显示每个模型(例如,在作业页面中,我只显示作业。)。但现在在主页上,我想根据他们在同一页面上发布的日期显示这些内容。如何在同一页面上显示不同模型的不同对象?我是否制作单独的模型,例如class Post然后在从TenderJobNews创建新对象时使用信号创建新帖子?我真的希望有更好的方法来实现这一目标。或者我使用多表继承?请帮我。谢谢。

更新

我不想在同一页面上单独显示每个模型对象。但就像Facebook或任何其他社交媒体的饲料。假设在fb中,任何帖子(无论是图像,状态,共享)都在主页中一起显示。同样在我的情况下,假设创建了一个新的Job对象,之后创建了一个新的News对象。然后,我想首先显示News对象,然后显示Job对象,依此类推。

6 个答案:

答案 0 :(得分:11)

工作解决方案

有两个工作解决方案另外两个答案。这两个都涉及三个查询。你用.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字段现已编入索引(请参阅我发布的新帖子模型)。这使得查询非常快。 python中的所有记录都没有迭代。

如何找出模板中的哪个?有这样的东西。

{% if post.tender %}

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

进一步优化

您的设计中有一些空间来规范化数据库。例如,同一家公司可能会发布多个工作或招标。因此,公司模式可能会有用。

更有效的解决方案。

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

这是redis sorted sets的礼貌所带来的。每次保存PostJobNews对象时,都会将其添加到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上,您的时间表可能是通过类似的机制生成的。

答案 1 :(得分:10)

以下应该需要您。但是为了提高性能,您可以在每个模型中创建额外的type字段,以便可以避免使用annotation

您的观点将如下所示:

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})

在您的模板中,项目按其排序顺序(按新近度)排序,您可以通过区分项目类型为每个项目应用相应的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 %}

您可以通过使用模型对象的annotation属性来区分并将它们放在适当的html块中,从而完全避免使用__class__

对于Tender对象,item.__class__将为app_name.models.Tender,其中app_name是包含该模型的Django应用程序的名称。

因此,如果不在home视图中使用注释,您的模板将会显示:

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

    {% elif item.__class__ == 'app_name.models.News' %}
     ...
    {% endif %}
{% endfor %}

通过这种方式,您可以节省注释的额外开销,或者不得不修改模型。

答案 2 :(得分:5)

直接的方法是将chain与已排序:

结合使用

视图

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

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

要在排序之前使用切片,请使用以下内容:

Tender.objects.all()[:20]

或使用自定义Manager为您的模型卸载逻辑。

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

然后你可以使用类似的东西:

Job.objects.featured()

模板

如果您需要依赖于对象类的其他逻辑,请创建一个简单的模板标记:

#templatetags/ctype_tags.py
from django import template
register = template.Library()

@register.filter
def ctype(value):
    return value.__class__.__name__.lower()

#templates/feed.html
{% load ctype_tags %}

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

奖励 - 组合具有不同字段名称的对象

有时可能需要使用现有/第三方模型创建这些类型的Feed。在这种情况下,您不必为所有要排序的模型使用相同的字段名称。

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)

答案 3 :(得分:1)

  

我是否制作单独的模型,例如class Post然后使用信号来   每当从Tender创建新对象时创建一个新帖子,或   工作还是新闻?我真的希望有更好的方法来实现这一目标。要么   使用多表继承?

     

我不想单独显示每个模型对象   页。但就像Facebook或任何其他社交媒体的提要一样。

我个人没有看到使用其他模型的任何错误,恕我直言,甚至更喜欢使用其他型号,特别是当有an app时。

为什么呢?因为我永远不想重写代码,因为扩展我当前的代码可以实现。你过度设计了这个问题,如果不是现在,你将会受到影响。

答案 4 :(得分:1)

另一种解决方案是使用Django haystack:

它允许您搜索不相关的模型。它比其他解决方案更有效,但效率很高(1快速查询),您也可以轻松过滤您的商家信息。

在您的情况下,您需要在所有搜索索引中定义pubdate

答案 5 :(得分:0)

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

class Post(models.Model):

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

然后,每次创建新模型时,您也会创建一个Post并将其与Tender / Job / News相关联。您应该将每个帖子仅与三个模型中的一个相关联。

为Post创建一个序列化器,其中包含缩进的序列化器,用于招标,作业和新闻。

对不起,简短的回答。如果您认为它可以解决您的问题,我会稍后再写。