代码之家  ›  专栏  ›  技术社区  ›  zerohedge

Django复合注释

  •  2
  • zerohedge  · 技术社区  · 6 年前

    先决条件:

    • Queryset必须返回 Article
    • Queryset必须返回唯一的对象
    • 不能使用命中数据库的for循环(意味着N个要注释的对象的N个查询)

    我的模型:

    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
    

    一切都从一开始 Report 报告 将与一个 文章 通过 ArticleInReport 文章 在导入报告时 .

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

    • 在过去30分钟内收到评论的所有文章。
    • 每一篇文章都附有以下信息,这就是我面临的问题:

    如有,浏览次数; 对象在 . 如果不存在,则为0。

    我的 views.py 文件:

    reports_in_time_range = Report.objects.filter(created_date__range=[starting_range, right_now]).order_by('created_date')
    
    last_report = reports_in_time_range.prefetch_related('articles').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(id__in=last_report.articles.values_list('id', flat=True),
                     then=F('articleinreports__ios_views') + F('articleinreports__android_views')),
                default=0, output_field=IntegerField(),
        ))
    

    filter(id__in=unique_articles) ),只返回不同的文章。接下来,如果文章的ID出现在 上次报告 的文章列表(到) 条款报告 条款报告 .

    上述注释适用于大多数情况 s、 但对于其他人来说,却没有明显的原因而惨败。我尝试过许多不同的方法,但似乎总是得到错误的结果。

    3 回复  |  直到 6 年前
        1
  •  2
  •   dani herrera    6 年前

    reports_in_time_range = ( Report
                             .objects
                             .filter(created_date__range=[starting_range, right_now])
                             .order_by('created_date'))
    
    last_report = reports_in_time_range.prefetch_related('articles').last()
    
    report_articles_ids = ( Article
                           .objects
                           .filter(articleinreports__report=last_report)
                           .values_list('id', flat=True)
                           .distinct())
    
    report_articles = ( Article
                       .objects
                       .filter(id__in=report_articles_ids)
                       .annotate( total_views=Sum(  
                                       F('articleinreports__ios_views') +
                                       F('articleinreports__android_views'),
                                       output_field=IntegerField()
                       )))
    
    other_articles = ( Article
                       .objects
                       .exclude(id__in=report_articles_ids)
                       .annotate( total_views=ExpressionWrapper(
                                        Value(0),
                                        output_field=IntegerField())
                       )))
    
    articles = report_articles | other_articles
    
        2
  •  1
  •   jackotonye Dmit3Y    6 年前

    您的方法的问题在于,您需要匹配所使用的唯一确切id IN

    articles_with_views_in_range = (
        Article.objects
            .annotate(
                  total_views=Case(
                      When(articleinreports__range=(start_range, end_range), 
                           then=F('articleinreports__ios_views') + F('articleinreports__android_views')),
                      default=0, output_field=IntegerField(),
                  )
            ).filter(total_views__gt=0)
      )
    
        3
  •  1
  •   Alexandr Tatarinov    6 年前

    我能看出你的问题 then=F('articleinreports__ios_views') + F('articleinreports__android_views') ,因为它不知道要使用哪篇文章报告。。。。因此,它可能会在与每篇文章关联的报告中为每篇文章创建重复项。正如@Danierrera所建议的,您可以首先获取所需的所有文章,然后从上一个报告中获取所有ArticleInReport,这将是3个查询。然后您可以循环浏览文章,如果您有文章的ArticleInReport,则分配视图计数,如果没有,则分配零。如果您不需要使用任何进一步的sql操作,这将起作用 total_views

    Subquery 总视图 文章queryset的注释。然后您可以使用 Coalesce 当项目在上次报告中未收到任何视图时,运算符将Null替换为零。

    PS我认为 prefetch_related('articles') PP另外,您不需要对唯一的文章和文章使用distinct,因为查找中的文章将已经产生不同的结果