Как рассчитать общее количество часов в моей модели расписания?

#python #django

#python #django

Вопрос:

Я устанавливаю две модели: расписание и расписание. TimesheetEntry будет встроенным и устанавливается в admin.py файл, и оттуда вы можете указать текущие часы для каждого проекта. Мой вопрос в том, как создать функцию и вычислить total_hours, чтобы я мог визуализировать ее в своей пользовательской таблице в шаблоне Django?

 class TimesheetEntry(TimeStampedModel):
    hours = models.DecimalField(max_digits=4, decimal_places=2, default=0, verbose_name=_("Hours"))
    timesheet = models.ForeignKey('timesheet.TimeSheet', verbose_name=_("Timesheet"),
                                  related_name='timesheet_entries', on_delete=models.CASCADE)
    project = models.ForeignKey('project.Project', verbose_name=_("Project"),
                                related_name='timesheet_entries', on_delete=models.PROTECT, null=True)
    project_role = models.ForeignKey('project.ProjectRole', verbose_name=_("Project role"),
                                     related_name='timesheet_entries', on_delete=models.PROTECT, null=True)

    def __str__(self):
        return f"{self.timesheet} - {self.project} - {self.project_role}"

    def save(self, **kwargs):
        self.timesheet.save()
        super().save(**kwargs)

    class Meta:
        verbose_name = _("Timesheet entry")
        verbose_name_plural = _("Timesheet entries")


class Timesheet(TimeStampedModel):
    date = models.DateField(verbose_name=_("Date"), default=datetime.date.today, editable=True)

    total_hours = models.DecimalField(max_digits=4, decimal_places=2, verbose_name=_("Total hours"), editable=False)

    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='timesheet')

    def __str__(self):
        return f"{self.user} - {date_format(self.date, 'DATE_FORMAT')}"

    def recalculate(self):
        self.total_hours = self.timesheet_entries.all().aggregate(total_hours=Sum('hours'))['total_hours'] or 0

    def save(self, **kwargs):
        self.recalculate()
        super().save(**kwargs)

    def projects(self):
        from hr.project.models import Project

        return Project.objects.filter(
            pk__in=self.timesheet_entries.all().values_list('project_id', flat=True).distinct()
        )

    class Meta:
        verbose_name = _("Timesheet")
        verbose_name_plural = _("Timesheets")
    ```
  

Комментарии:

1.вы пытались добавить свойство в свою модель? @property docs.djangoproject.com/en/2.2/topics/db/models/#model-methods

2. Я никогда не использовал это, потому что я немного новичок.

3. Добро пожаловать в Stack Overflow… где вы сможете попробовать что-то новое… определенно рекомендую прочитать соответствующие документы.

Ответ №1:

Агрегирование — это не то, что вы хотите использовать здесь, поскольку вы суммируете его в экземпляре. итак, это должно сделать это:

     self.total_hours = sum(self.timesheet_entries.all().values_list("hours",flat=True)
  

Затем вы либо делаете это свойством (как указано Icognos), либо переопределяете функцию сохранения и вызываете пересчет в функции сохранения.

Ответ №2:

Ответ прост и сложен одновременно. Вы можете использовать @cached_property декоратор для метода модели (например recalculate , — который вы могли бы переименовать total_hours и удалить из своего save метода), а затем кэшировать результат cached_property — только для того, чтобы аннулировать его каждый раз, когда появляется новая или отредактированная запись времени — путем удаления cached_property . Кэшированное свойство

 class Timesheet(TimeStampedModel):
    date = models.DateField(verbose_name=_("Date"), default=datetime.date.today, editable=True)

    total_hours = models.DecimalField(max_digits=4, decimal_places=2, verbose_name=_("Total hours"), editable=False)

    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='timesheet')

    def __str__(self):
        return f"{self.user} - {date_format(self.date, 'DATE_FORMAT')}"

    @cached_property
    def total_hours(self):
       return self.timesheet_entries.all().aggregate(total_hours=Sum('hours'))['total_hours'] or 0

    def save(self, **kwargs):

        super().save(**kwargs)
  

Ответ №3:

Ну, это def recalculate в модели расписания работает, когда производится редактирование. Реальная проблема заключалась в том, что мне нужно выполнить обратный вызов в сериализаторе расписания, и там я должен вызвать функцию timesheet.recalculate и save ее. В обоих случаях это работает из ответов службы поддержки. Спасибо!

 class TimesheetSerializer(ModelSerializer):
    timesheet_entries = TimesheetEntrySerializer(many=True)

    class Meta:
        model = Timesheet
        fields = ['date', 'user', 'total_hours', 'timesheet_entries']

    def to_representation(self, instance):
        result = super().to_representation(instance)
        result['user'] = UserDetailsSerializer(instance=instance.user).data
        return result

    def create(self, validated_data):
        entries = validated_data.pop('timesheet_entries')

        timesheet = Timesheet.objects.create(**validated_data)

        for entry in entries:
            TimesheetEntry.objects.create(timesheet=timesheet, **entry)

        timesheet.recalculate()
        timesheet.save()

        return timesheet
    ```