Ambrose
Ambrose

Reputation: 363

Annotating a QuerySet in ListView: Django

I am learning Django by building an example app in which I have student users who can sign up for Study sections. For each student user I have a ListView which creates a table list of all of the relevant study sections for them. Each Study model is related to a Detail model which contains the details of the study section such as location and start time. For each Study I would like to add values of the associated Detail such as location and start time, but I am having trouble figuring out how to pass this information into the template from the ListView.

models.py

class Study(models.Model):
    owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name='studies')
    name = models.CharField(max_length=255)
    condition = models.ForeignKey(Condition, on_delete=models.CASCADE, related_name='studies')

    def get_details(self, study):
        details = study.details.all().order_by('start_date')
        return details

    def __str__(self):
        return self.name


class Detail(models.Model):
    study = models.ForeignKey(Study, on_delete=models.CASCADE, related_name='details')
    Location = models.CharField('Detail', max_length=255)
    start_date = models.DateField('event start date', default=datetime.date.today)
    end_date = models.DateField('event end date', default=datetime.date.today)
    start_time = models.TimeField('event start time', default=now)
    end_time = models.TimeField('event end time', default=now)
    description = models.TextField(blank=True)

    def __str__(self):
        return self.Location

views.py

@method_decorator([login_required, student_required], name='dispatch')
class StudyListView(ListView):
    model = Study
    ordering = ('name', )
    context_object_name = 'studies'
    template_name = 'classroom/students/study_list.html'

    def get_object(self):
        return self.request.user.studies

    def get_context_data(self, **kwargs):
        kwargs['details'] = self.get_object().details.annotate()
        return super().get_context_data(**kwargs)

    def get_queryset(self):
        student = self.request.user.student
        student_conditions = student.conditions.values_list('pk', flat=True)
        participating_studies = student.study_answers.filter(participate=True).values_list('study_id', flat=True)
        queryset = Study.objects.filter(condition__in=student_conditions) \
        .exclude(pk__in=participating_studies)
        return queryset

template.html

        {% for study,detail in zip(studies,details) %}
      <tr>
        <td class="align-middle">{{ study.name }}</td>
        <td class="align-middle">{{ study.condition.get_html_badge }}</td>
        <td class="align-middle">{{ detail.Location }} details</td>
        <td class="text-right">
          <a href="{% url 'students:past_study' study.pk %}" class="btn btn-primary">View Study</a>
        </td>
      </tr>

This current try yields the error:

Exception Value: 'RelatedManager' object has no attribute 'details'

Upvotes: 1

Views: 828

Answers (1)

mgradowski
mgradowski

Reputation: 81

Django ORM doesn't create a Study.details property. To access all related Detail objects, you should write Study.detail_set. Furthermore, Study.get_details is a method, so it shouldn't accept a second argument – it just makes no sense. Together, this means your code should look like this:

def get_details(self):
    return self.detail_set.order_by('start_date').all()

Also, your code seems chaotic, let me know if you have more issues.

Upvotes: 2

Related Questions