Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/297.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python Django:复杂的注释,如何避免for循环?_Python_Django_Django Orm - Fatal编程技术网

Python Django:复杂的注释,如何避免for循环?

Python Django:复杂的注释,如何避免for循环?,python,django,django-orm,Python,Django,Django Orm,对于使用Google Realtime analytics API的分析应用程序,我的models.py定义如下: class Report(BaseModel): ios_report = JSONField() android_report = JSONField() class Article(BaseModel): internal_id = models.IntegerField(unique=True) title = models.CharFie

对于使用Google Realtime analytics API的分析应用程序,我的
models.py
定义如下:

class Report(BaseModel):
    ios_report = JSONField()
    android_report = JSONField()

class Article(BaseModel):

    internal_id = models.IntegerField(unique=True)
    title = models.CharField(max_length=500)
    short_title = models.CharField(max_length=500)
    picture_url = models.URLField()
    published_date = models.DateField()
    clip_link = models.URLField()
    reports = models.ManyToManyField(
        "Report", through="ArticleInReport", related_name="articles"
    )

class ArticleInReport(BaseModel):

    article = models.ForeignKey("core.Article", on_delete=models.CASCADE, related_name='articleinreports')
    report = models.ForeignKey("core.Report", on_delete=models.CASCADE, related_name='articleinreports')
    ios_views = models.IntegerField()
    android_views = models.IntegerField()

    @property
    def total_views(self):
        return self.ios_views + self.android_views
一切都从一个以设定间隔创建的
报告
对象开始。本报告包含有关文章及其各自观点的数据。
报告
将通过
ArticleInReport
文章
建立关系,该报告包含导入报告时
文章
中的用户总数

在我看来,我需要显示以下信息:

  • 在过去24小时内收到评论的所有文章
  • 每一篇文章都附有以下信息:
  • 如果存在,
    文章
    对象在上一次
    报告
    中的视图数。如果不存在,则为0
我在我的
视图.py中实现了以下目标:

reports_in_time_range = Report.objects.filter(created_date__range=[starting_range, right_now])
last_report = Report.objects.last()
unique_articles = Article.objects.filter(articleinreports__report__in=reports_in_time_range).distinct('id')

    articles = Article.objects.filter(id__in=unique_articles).distinct('id').annotate(
        total_views=Case(
                When(articleinreports__report=last_report,
                     then=(F("articleinreports__ios_views") + F("articleinreports__android_views"))), default=0, output_field=IntegerField(),
        ))

    sorted_articles = sorted(articles, key=operator.attrgetter('total_views'), reverse=True)
但我还需要为显示的每篇文章提供一个“趋势图”,其中包含以下信息:

  • X轴:在过去6小时内导入的所有报告(或者更确切地说,报告日期),无论文章ID是否出现在其中
  • Y轴:每个报告中
    总视图的值
    :如果文章存在,则显示
    总视图
    ,如果没有,则返回
    0
  • 如果不使用多个for循环,我无法找到有效的方法来执行此操作。我目前的做法是在
    文章
    模型中添加以下方法:

    class Article(BaseModel):
    
        def get_article_data_for_reports(self, report_objs):
            graph_dict = {}
            graph_dict['x_vals'] = [x.created_date for x in report_objs]
            graph_dict['y_vals'] = []
            for passed_report in report_objs:
                try:
                    graph_dict['y_vals'].append(ArticleInReport.objects.get(article=self, report=passed_report).total_views)
                except ArticleInReport.DoesNotExist:
                    graph_dict['y_vals'].append(0)
            print(graph_dict)
            return graph_dict
    
    views.py
    文件中,我执行以下操作:

        context["articles"] = sorted_articles
        context["article_graphs"] = {}
    
        for article in sorted_articles:
            context["article_graphs"][article.internal_id]= article.get_article_data_for_reports(xhours_ago_reports)
    
    然后我可以在视图的上下文中使用它。但在继续之前,我想知道是否有更好的方法。每次刷新时,页面加载时间从毫秒增加到5-9秒

    from django.db.models import F
    
    
    reports = Report.objects.all()  # Filter reports here
    
    # This creates LEFT OUTER JOIN with all ArticleInReport, so each
    # Article will appear in result once per each report which includes it
    articles_with_reports = Article.objects.annotate(
        report_id=F('articleinreports__report_id')
    )
    # We are only interested in some reports
    articles_in_reports = articles_with_reports.filter(
        report_id__in=reports.values('id')
    )
    # As each result row is actually ArticleInReport, this effectively gives
    # amount of views per article per report
    articles_with_views = articles_in_reports.annotate(
        views=(
                F('articleinreports__ios_views')
                + F('articleinreports__android_views')
        )
    )
    # Now do some processing in python - it's cheap
    # We need dictionary to create final chart data
    articles_map = {}  # {Article: {report_id: article_with_view}}
    for article in articles_with_views:
        articles_map.setdefault(article, {})
        articles_map[article][article.report_id] = article.views
    
    article_graphs = {}
    # Force-evaluate to cache Reports
    # Actually this would happen automatically, but to be certain...
    reports = list(reports)
    # As we want all Articles, we have to fetch them
    for article in Article.objects.all():
        x_vals = []
        y_vals = []
        # Now for each report we will set article.views or 0
        # this will execute only once
        for report in reports:
            x_vals.append(report.created_date)
            if (
                article in articles_map
                and report.id in articles_map[article]
            ):
                # We have views for this article in this record
                y_vals.append(articles_map[article][report.id])
            else:
                # Defaults
                y_vals.append(0)
        article_graphs[article] = {
            'x_vals': x_vals,
            'y_vals': y_vals
        }
    
    # Finally, we have article_graphs
    # {
    #    Article: {
    #        'x_vals': [Date, Date, Date],
    #        'y_vals': [100, 0, 50]
    #    },
    #    ....
    # }
    
    更新

    要仅为最近报告中至少出现1次的
    文章
    构建图表,我们只想直接使用
    文章映射

    article_graphs = {}
    # Force-evaluate to cache Reports
    # Actually this would happen automatically, but to be certain...
    reports = list(reports)
    for article, views_by_report in articles_map.items():
        x_vals = []
        y_vals = []
        # Now for each report we will set article.views or 0
        for report in reports:
            x_vals.append(report.created_date)
            y_vals.append(views_by_report.get(report.id, 0))
        article_graphs[article] = {
            'x_vals': x_vals,
            'y_vals': y_vals
        }
    

    我不相信这个注释是正确的:因为这里基本上是第一个(可以是随机顺序)
    报告,其中出现了
    文章
    ,然后使用这些视图。但是,如果一篇
    文章
    出现在多个
    报告
    s中,你就不能总结这些观点。@WillemVanOnsem我不知道你的意思。你说的是第一个注释,还是第二个注释?另外,我不需要总结任何东西。我只需要在趋势图旁边显示文章的最新
    total_views
    数据。@WillemVanOnsem无论如何,我更大的问题是for循环,我不确定如何用更快的方法解决。@WillemVanOnsem我已经解决了不同的问题。如果你对避免for循环有什么见解的话,那就太好了。我有过类似的问题,我用了两件事:1。在Postgres中创建视图并减少复杂查询。视图表的工作方式与中的普通模型类似,模型中有代码调整,我确信文档2中提供了代码调整。我使用redis将数据缓存24小时。这看起来很有希望。我将不得不测试和更新。但是有一个问题:
    #由于我们需要所有文章,我们必须获取它们
    -我们实际上只需要在过去X小时的报告中出现的文章,即我们正在筛选的报告。我还认为我们在这里不必要地查询了所有文章对象两次:再次,因为我们只需要那些出现在我们筛选的报告中的内容。@zerohedge我会在一段时间内更新它,只是误解了您的“但是我还需要为显示的每篇文章提供一个“趋势图”,谢谢。在“articles\u with_reports”中,我们是否应该将这些内容进一步过滤到我们之前查询的reports变量中出现的文章?另外:你确定“带视图的文章”应该引用“带报告的文章”而不是“报告中的文章”吗?@zerohedge这没有区别。由于查询集是惰性的,它们将只在循环内进行计算,在此之前,我们只是创建SQL查询