Как рассчитать общее количество часов в моей модели расписания? - PullRequest
0 голосов
/ 16 апреля 2019

Я устанавливаю две модели: Timesheet и TimesheetEntry. 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")
    ```

Ответы [ 3 ]

1 голос
/ 16 апреля 2019

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

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

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

1 голос
/ 16 апреля 2019

Ответ прост и сложен одновременно.Вы можете использовать декоратор @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)
0 голосов
/ 16 апреля 2019

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

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
    ```
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...