如何将链接模型中的交叉表样式项聚合到其父项?

我希望从下面显示的模型结构构建一个交叉表视图。 我想要一个矩阵,其中日历的日期在LHS的下方,课程名称在顶部,并且每个单元格中的某些内容表示该日期/课程上存在Booking实例。

简化的模型结构:

class Calendar(models.Model):
    crCalDate = models.DateField(primary_key=True)

class Course(models.Model):
    ceName = models.CharField()

class CourseBooking(models.Model):
    cbCourse = models.ForeignKey(Course,on_delete=models.CASCADE,related_name='booked_course')
    cbday = models.ForeignKey(Calendar,related_name='booked_day')

我的视图函数当前的查询集:

class DayView(ListView):

    def get_queryset(self):
        # construct index of course tuples
        course_index = [(cn['id'],'course' + str(i)) for i,cn in enumerate(Course.objects.order_by('id').values('id'))]
        coursebookings = CourseBooking.objects.filter(cbday__crCalDate__gte=dt.datetime.today()).values('id','cbday__crCalDate','cbCourse__id','cbCourse__ceName')
        calendar_dates = list(Calendar.objects.filter(crCalDate__gte=dt.datetime.today()).order_by('crCalDate').values('crCalDate','crSunrise','crSunset'))
        for cb in coursebookings:
            for i,cal in enumerate(calendar_dates):
                if cal['crCalDate'] == cb['cbday__crCalDate']:
                    course = [ci[1] for ci in course_index if ci[0] == cb['cbCourse__id']]
                    if course:
                        calendar_dates[i][course[0]] = 'Occupied'
                    break
        return calendar_dates

这一切都感觉很繁琐且昂贵,而且我想知道我是在错误地尝试这种方法吗,尝试了annotate()aggregate()函数以实现合适的摘要查询没有成功。

作为奖励,我真的希望能够显示每个日历日每个课程的预定行数,而不是我现在在其中输入的文字。 表示此的附加模型如下:

class BookLine(models.Model):
    blTime = models.TimeField(verbose_name='Booking Time')
    blDay = models.ForeignKey(CourseBooking,on_delete = models.CASCADE,db_index=True,related_name='bookinglines')

对于实现这一目标的更好方法的任何指导,甚至验证这种方法(除了我上面的奖金要求),都倍受赞赏。

lala_2009 回答:如何将链接模型中的交叉表样式项聚合到其父项?

看起来还不错。在for循环的外部有三个数据库查询,因此这不是N + 1查询问题。我不能说我能一眼就明白了。

您可以仅使用一个查询来获取CourseBooking对象或用ceNamecrCalDate值注释的值的列表。您还可以按这些值进行过滤。然后,您可以对所有这些对象或值进行一次遍历,设置字典calendar_dates[date][course]

然后,您必须获取任何课程指南中都包含的一组课程,并将其转换为列表以用作顶部标题。然后遍历日期,将每个课程字典替换为具有适当表元素值的课程列表。

我认为这段代码可能会更清晰,但是如果您使用的代码正在运行,则可能不值得花时间更改它。但是,它可以让您进行计数,最简单的方法是将内部路线字典设为collections.Counter对象。请注意,计数器的任何键的默认值为0,您永远不会收到KeyError。

table = {}
for obj in course_bookings:  # annotated with date_anno and course_anno
    date,course = obj.date_anno,obj.course_anno
    if date not in table:
         table[date] = Counter() # or make table a DefaultDict
    table[date][course] += 1

course_headers = set()
for course_dict in table:
   course_headers.update( course_dict.keys() )

course_headers = sorted( list( course_headers))
date_column = sorted( table.keys() )

然后,您将通过遍历date_columncourse_headers并以正确的顺序提取计数,然后再传递要渲染的计数,来构建列表列表。或者,您可以使用我最近发现的技巧:

class DotDict( dict):
    def __getattr__( self,key):
        return self[attr] # or with a default None,self.get( attr,None)

这是一个各方面的决定,除了您无法在其上有效地使用setattr之外,因为getattr已与对其进行索引一样……因此Django模板引擎可以引用{{dotdict.key}},不需要精美的自定义模板标签。从Counter继承同样有效。

本文链接:https://www.f2er.com/3160907.html

大家都在问