Django复杂注释

时间:2018-10-07 15:18:58

标签: django django-orm

先决条件:

  • 查询集必须返回Article s
  • 查询集必须返回唯一对象
  • 绝不能利用击中数据库的循环(意味着对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对象。该报告包含有关文章及其各自视图的数据。 Report将与ArticleArticleInReport有关联,而Article在导入报告时保留了Article中的用户总数。

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

  • 最近30分钟内获得观看次数的所有文章。
  • 每篇文章都带有以下信息,这是我面临的问题:
  

如果存在,则Report对象在 last views.py中的观看次数。如果不存在,则为0。

我的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)

对我的思维过程的一些解释:首先,仅获取出现在时间范围内(ArticleInReport)的相关报告中的文章,仅返回不同的文章。接下来,如果文章的ID(当然是通过ArticleInReport出现在上次报告的文章列表中,请计算该Article的iOS视图和Android视图。

上面的注释适用于大多数 CREATE TABLE tweets ( id BIGINT, created_at STRING, source STRING, favorited BOOLEAN, retweeted_status STRUCT< text : STRING, `user` : STRUCT<screen_name : STRING,name : STRING>, retweet_count : INT>, entities STRUCT< urls : ARRAY<STRUCT<expanded_url : STRING>>, user_mentions : ARRAY<STRUCT<screen_name : STRING,name : STRING>>, hashtags : ARRAY<STRUCT<text : STRING>>>, text STRING, `user` STRUCT< screen_name : STRING, name : STRING, friends_count : INT, followers_count : INT, statuses_count : INT, verified : BOOLEAN, utc_offset : INT, time_zone : STRING>, in_reply_to_screen_name STRING ) ROW FORMAT SERDE 'org.apache.hive.hcatalog.data.JsonSerDe' Location '/user/flume/tweets/'; ,但由于其他原因而失败,但没有明显的原因。我尝试了许多不同的方法,但似乎总是得到错误的结果。

3 个答案:

答案 0 :(得分:2)

避免数据库命中非常重要,但不要以此为代价。我认为您应该将查询分为两个或多个查询。拆分查询将提高可读性,并可能提高性能(有时两个简单查询的运行速度比复杂查询快)。请记住,您具有dics,理解和itertools的全部功能,可以处理部分结果。

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

答案 1 :(得分:1)

我可以看到x = df[(df.OBJECTID == 4440) & (df.Landuse == 'Grass - Urban')] want_area = x['Area'].sum() #returning the whole dataframe sum for that field!! summed = x['Area'].sum() ratio = round(want_area / summed, 2) 的问题,因为它不知道要使用哪个ArticleInReport。...因此它可能会为与每个Article相关的每个ArticleInReport创建重复项。如@daniherrera所建议,您可以首先获取所需的所有文章,然后从上一个报告中获取所有ArticleInReport,这将是3个查询。然后,您可以循环浏览Articles,如果有Article的ArticleInReport,则分配视图计数,如果没有-分配零。如果您不需要使用then=F('articleinreports__ios_views') + F('articleinreports__android_views')进行任何其他的sql操作,则此方法将起作用。您可能想要在循环之前构建{Article.id:ArticleInReport}的字典,以便于查找。

另一种方法(如果需要某种过滤或排序或其他方法)是使用上次报告中的ArticleInReport的total_views来为Article queryset添加Subquery注释。然后,当文章在上一个报告中未收到任何视图时,您可以使用total_views运算符将Null替换为零。

P。 S.我认为Coalesce是无用的,因为无论如何您都使用values_list。 P. P. S也不需要对unique_articles和article进行区分,因为__in查找将已经产生明显的结果

答案 2 :(得分:0)

您需要使用IN来匹配唯一准确ID的方法所带来的问题将返回一个超出预期范围的边界,您可以直接使用反向名称来过滤商品对象,也可以过度使用unique < / p>

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