Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/326.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/django/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python Formset保存除0以外的任何十进制数,即使Formset已保存且有效()通过_Python_Django_Django Forms - Fatal编程技术网

Python Formset保存除0以外的任何十进制数,即使Formset已保存且有效()通过

Python Formset保存除0以外的任何十进制数,即使Formset已保存且有效()通过,python,django,django-forms,Python,Django,Django Forms,我创建了一个表单集,允许用户每周记录小时数。 我遇到的问题是,我无法在输入字段中保存“0”-除了0之外,任何十进制数都有效(请参见结尾的gif以获取说明) TLDR: Formset保存除0以外的任何输入,不知道原因。请参见此处的gif以了解图示: 我的时间表和时间表型号如下所示: class Timesheet(Model): year = PositiveIntegerField(validators=[MaxValueValidator(2500), MinValueVal

我创建了一个表单集,允许用户每周记录小时数。 我遇到的问题是,我无法在输入字段中保存“0”-除了0之外,任何十进制数都有效(请参见结尾的gif以获取说明)


TLDR: Formset保存除0以外的任何输入,不知道原因。请参见此处的gif以了解图示:


我的
时间表
时间表
型号如下所示:


class Timesheet(Model):

    year = PositiveIntegerField(validators=[MaxValueValidator(2500), MinValueValidator(1900)])
    week = PositiveIntegerField()
    project = ForeignKey("projects.Project", on_delete=CASCADE)
    user = ForeignKey(User, on_delete=CASCADE)
    status = CharField(
        max_length=15, choices=STATUS_CHOICES, blank=True, default=STATUS_OPEN
    )

    objects = TimesheetManager()

    class Meta:
        db_table = 'timesheet'
        ordering = ('id',)
        unique_together = (('year', 'week', 'user', 'project',),)

    def __getattr__(self, attr):
        allowed_days = (
            'day_1', 'day_2', 'day_3', 'day_4', 'day_5', 'day_6', 'day_7'
        )
        if attr in allowed_days:
            day = int(attr[-1]) - 1
            entry = self.timesheetentry_set.filter(
                date=Week(self.year, self.week).day(day)
            ).first()
            if entry:
                return entry.hours
            return None
        return super().__getattr__(attr)

    def total_duration(self):
        return self.timesheetentry_set.aggregate(
            total_duration=Coalesce(Sum('hours'), 0)
        ).get('total_duration')


class TimesheetEntry(Model):
    timesheet = ForeignKey(Timesheet, on_delete=CASCADE)
    hours = DecimalField(max_digits=4, decimal_places=1, null=True, blank=True)
    date = DateField()

    class Meta:
        db_table = 'timesheet_entry'
        ordering = ('date',)


DAYS = (
    'day_1', 'day_2', 'day_3', 'day_4', 'day_5', 'day_6', 'day_7'
)

class TimesheetModelForm(ModelForm):

    class Meta:
        model = Timesheet
        exclude = ("user", "status")

class BaseFormSetValidation(BaseModelFormSet):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for form in self.forms:
            for day in DAYS:
                form.fields[day].initial = getattr(form.instance, day)
       


    def add_fields(self, form, index):
        super().add_fields(form, index)
        for day in DAYS:
            form.fields[day] = DecimalField(required=False)

    def save(self, commit=True):
        super(BaseFormSetValidation, self).save(commit)
        from isoweek import Week
        for form in self.forms:
            week = Week(form.instance.year, form.instance.week)
            for day in DAYS:
                if form.cleaned_data.get(day):
                    date = week.day(int(day[-1]) - 1)
    
                    TimesheetEntry.objects.update_or_create(
                        timesheet=form.instance,
                        date=date,
                        defaults={'hours': form.cleaned_data.get(day)}
                    )



TimesheetModelFormSet = modelformset_factory(
    Timesheet,
    formset=BaseFormSetValidation,
    exclude=("year", "week", "project", "user"),
    extra=0,
)
forms.py
如下所示:


class Timesheet(Model):

    year = PositiveIntegerField(validators=[MaxValueValidator(2500), MinValueValidator(1900)])
    week = PositiveIntegerField()
    project = ForeignKey("projects.Project", on_delete=CASCADE)
    user = ForeignKey(User, on_delete=CASCADE)
    status = CharField(
        max_length=15, choices=STATUS_CHOICES, blank=True, default=STATUS_OPEN
    )

    objects = TimesheetManager()

    class Meta:
        db_table = 'timesheet'
        ordering = ('id',)
        unique_together = (('year', 'week', 'user', 'project',),)

    def __getattr__(self, attr):
        allowed_days = (
            'day_1', 'day_2', 'day_3', 'day_4', 'day_5', 'day_6', 'day_7'
        )
        if attr in allowed_days:
            day = int(attr[-1]) - 1
            entry = self.timesheetentry_set.filter(
                date=Week(self.year, self.week).day(day)
            ).first()
            if entry:
                return entry.hours
            return None
        return super().__getattr__(attr)

    def total_duration(self):
        return self.timesheetentry_set.aggregate(
            total_duration=Coalesce(Sum('hours'), 0)
        ).get('total_duration')


class TimesheetEntry(Model):
    timesheet = ForeignKey(Timesheet, on_delete=CASCADE)
    hours = DecimalField(max_digits=4, decimal_places=1, null=True, blank=True)
    date = DateField()

    class Meta:
        db_table = 'timesheet_entry'
        ordering = ('date',)


DAYS = (
    'day_1', 'day_2', 'day_3', 'day_4', 'day_5', 'day_6', 'day_7'
)

class TimesheetModelForm(ModelForm):

    class Meta:
        model = Timesheet
        exclude = ("user", "status")

class BaseFormSetValidation(BaseModelFormSet):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for form in self.forms:
            for day in DAYS:
                form.fields[day].initial = getattr(form.instance, day)
       


    def add_fields(self, form, index):
        super().add_fields(form, index)
        for day in DAYS:
            form.fields[day] = DecimalField(required=False)

    def save(self, commit=True):
        super(BaseFormSetValidation, self).save(commit)
        from isoweek import Week
        for form in self.forms:
            week = Week(form.instance.year, form.instance.week)
            for day in DAYS:
                if form.cleaned_data.get(day):
                    date = week.day(int(day[-1]) - 1)
    
                    TimesheetEntry.objects.update_or_create(
                        timesheet=form.instance,
                        date=date,
                        defaults={'hours': form.cleaned_data.get(day)}
                    )



TimesheetModelFormSet = modelformset_factory(
    Timesheet,
    formset=BaseFormSetValidation,
    exclude=("year", "week", "project", "user"),
    extra=0,
)
views.py

class TimesheetEditorView(BaseTimesheet, TemplateView):
    form_class = TimesheetModelFormSet
    template_name = "timesheets/timesheet.html"

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        # only show timesheet rows that belongs to logged in user
        timesheet = Timesheet.objects.filter(
            year=context["year"],
            week=context["week"],
            user=self.request.user
        ).order_by("project_id")

        timesheet_formset = self.form_class(queryset=timesheet)
        create_timesheet_form = TimesheetModelForm(self.request.user)

        context.update(
            timesheet=Timesheet.objects.none(),
            timesheet_formset=timesheet_formset,
            create_timesheet_form=create_timesheet_form
        )
        return context


    def post(self, request, *args, **kwargs):
        context = self.get_context_data(**kwargs)
        timesheet_formset = self.form_class(request.POST, error_class=DivErrorList)
        context.update(timesheet_formset=timesheet_formset)

        if timesheet_formset.is_valid():
            timesheet_formset.save()
            message = "Weekly timesheet (is_valid())"

            messages.info(request, message, extra_tags='timesheet')
            success_url = reverse("timesheets:current-week", args=(context["year"], context["week"]))
            return HttpResponseRedirect(success_url)
        else:
            print(timesheet_formset.errors)
            print(timesheet_formset.non_form_errors)

        return render(request, "timesheets/timesheet.html", context)
timesheets.html


<tbody>

                        {{ timesheet_formset.management_form }}
                        {% for row in timesheet_formset %}
                        
                        <tr class="tracker">
                            <th scope="row" class="align-middle">{{ row.instance.project }} {{ row.id }}</th>
                            <td>{{ row.day_1 }}</td>
                            <td>{{ row.day_2 }}</td>
                            <td>{{ row.day_3 }}</td>
                            <td>{{ row.day_4 }}</td>
                            <td>{{ row.day_5 }}</td>
                            <td>{{ row.day_6 }}</td>
                            <td>{{ row.day_7 }}</td>
                            <td class="align-middle">{{ row.instance.total_duration }}</td>
 
                        </tr>
                        {% endfor %}
                    </tbody>

{{timesheet\u formset.management\u form}
{时间表中的行的%u表单集%}
{{row.instance.project}{{row.id}}
{{row.day_1}}
{{row.day_2}}
{{row.day_3}}
{{row.day_4}}
{{row.day_5}}
{{row.day_6}}
{{row.day_7}}
{{row.instance.total_duration}
{%endfor%}
除0外,任何十进制输入都有效-表单被保存且
有效()
被触发,但由于某些原因,表单将0视为
且未正确保存

我附加了一个gif来说明这个问题-请注意任何十进制输入是如何工作的,但当我输入“0”时,它只是恢复到最新的值:


关于为什么会发生这种情况,您有什么想法吗?

BaseFormSetValidation
的方法
save()
中,您正在检查值:

0
为falsy,因此if条件将为
False
,并且该条件将不会运行

None
0
False
是错误的值

相反,您可以显式检查
None

class BaseFormSetValidation(BaseModelFormSet):

    ...

    def save(self, commit=True):
        super(BaseFormSetValidation, self).save(commit)
        from isoweek import Week
        for form in self.forms:
            week = Week(form.instance.year, form.instance.week)
            for day in DAYS:
                if form.cleaned_data.get(day) is not None:
                    date = week.day(int(day[-1]) - 1)
    
                    TimesheetEntry.objects.update_or_create(
                        timesheet=form.instance,
                        date=date,
                        defaults={'hours': form.cleaned_data.get(day)}
                    )
类BaseFormSetValidation(BaseModelFormSet):
...
def save(self,commit=True):
超级(BaseFormSetValidation,self).保存(提交)
从isoweek导入周
对于self.forms中的表单:
周=周(form.instance.year,form.instance.week)
对于以天为单位的天:
如果form.cleaned_data.get(day)不是无:
日期=周日(整数(日[-1])-1)
timesheeententry.objects.update\u或创建(
时间表=form.instance,
日期=日期,
默认值={'hours':form.cleaned_data.get(day)}
)

伙计,我真不敢相信我错过了。实际上我不知道
0
是假的。非常感谢!