【问题标题】:keeping django models clean method validating foreign key object and using ModelForm save保持 django 模型清洁方法验证外键对象并使用 ModelForm 保存
【发布时间】:2015-06-21 01:29:40
【问题描述】:

在我的模型的 clean 方法中,我验证是否在外键参展商 is_premium 中给出,并验证他没有超过 MAX_DISCOUNTS_PER_EXHIBITOR 的活动对象。

它在 django admin 中运行良好。添加时和编辑时都一样。

我想让它在我的自定义(非 django-admin)视图中工作。我正在使用模型表单。

第一种方法

我将existor in view 分配给最初commit=False 保存的对象。

它与DoesNotExist 崩溃:折扣没有参展商,因为clean 已执行,但参展商尚未分配。实施它的正确方法应该是什么?

models.py

class Discount(models.Model):
    exhibitor = models.ForeignKey(
        'core_backend.Exhibitor', related_name='discounts')
    is_active = models.BooleanField(default=False, verbose_name=u"Aktywny")
    title = models.TextField(verbose_name=u"Tytuł")

    def clean(self):

        if not self.exhibitor.is_premium:
            raise ValidationError(
                u'Discounts only for premium')

        count = Discount.objects.filter(
            is_active=True,
            exhibitor=self.exhibitor
        ).count()

        if not self.pk:
            # newly added
            count = count + (1 if self.is_active else 0)
        else:
            # edited
            discount = Discount.objects.get(pk=self.pk)
            if discount.is_active and not self.is_active:
                count = count - 1
            elif not discount.is_active and self.is_active:
                count = count + 1

        if count > settings.MAX_DISCOUNTS_PER_EXHIBITOR:
            raise ValidationError(
                u'Max %s active discounts' % (
                    settings.MAX_DISCOUNTS_PER_EXHIBITOR
                )
            )

forms.py

class DiscountForm(forms.ModelForm):

    class Meta:
        model = Discount
        fields = (
            'title',
            'description',
            'is_activa',
        )

views.py

def add_discount(request, fair_pk, exhibitor_pk):
    fair = get_object_or_404(Fair, pk=fair_pk)
    exhibitor = get_object_or_404(
        Exhibitor, fair=fair, pk=exhibitor_pk, user=request.user)

    if request.method == 'GET':

        form = DiscountForm()

        return render(request, 'new_panel/add_discount.html', {
            'exhibitor': exhibitor,
            'discount_form': form,
        })

    if request.method == 'POST':
        form = DiscountForm(data=request.POST)

        if form.is_valid():
            discount = form.save(commit=False)
            discount.exhibitor = exhibitor
            discount.save()
            return redirect(reverse(
                'discounts_list',
                kwargs={"fair_pk": fair.pk, 'exhibitor_pk': exhibitor.pk}
            ))
        return render(request, 'new_panel/add_discount.html', {
            'exhibitor': exhibitor,
            'discount_form': form,
        })

第二种方法

我也尝试了不同的方法,使用单独的表单用于 POST,但它崩溃并出现相同的错误:

forms.py

class DiscountFormAdd(forms.ModelForm):

    class Meta:
        model = Discount
        fields = (
            'title',
            'exhibitor',
            'is_active',
        )

    def __init__(self, exhibitor, *args, **kwargs):
        self.exhibitor = exhibitor
        super(DiscountFormAdd, self).__init__(*args, **kwargs)

    def save(self, commit=False):
        discount = super(DiscountFormAdd, self).save(commit=False)
        discount.exhibitor = self.exhibitor

        if commit:
            discount.save()

        return discount

views.py

def add_discount(request, fair_pk, exhibitor_pk):
    fair = get_object_or_404(Fair, pk=fair_pk)
    exhibitor = get_object_or_404(
        Exhibitor, fair=fair, pk=exhibitor_pk, user=request.user)

    if request.method == 'GET':

        form = DiscountForm()

        return render(request, 'new_panel/add_discount.html', {
            'exhibitor': exhibitor,
            'discount_form': form,
        })

    if request.method == 'POST':
        form = DiscountFormAdd(data=request.POST, exhibitor=exhibitor)

        if form.is_valid():
            discount = form.save(commit=True)

            return redirect(reverse(
                'discounts_list',
                kwargs={"fair_pk": fair.pk, 'exhibitor_pk': exhibitor.pk}
            ))
        return render(request, 'new_panel/add_discount.html', {
            'exhibitor': exhibitor,
            'discount_form': form,
        })

我想坚持使用模型清理验证,而不是跳入清理表单。如果 django admin 能够做到,那么肯定可以在自定义模型表单中实现。

【问题讨论】:

    标签: python django django-models django-forms django-validation


    【解决方案1】:

    当使用self.exhibitor = exhibitor 指定参展商时,这会在您的表单上设置一个property,这与表单的内容无关。

    实际设置参展商,请改用以下代码:

    import copy
    .
    .
    .
        if request.method == 'POST':
            # request.POST is an immutable QueryDict so it needs to be copied
            form_data = copy.copy(request.POST)
            form_data['exhibitor'] = exhibitor.id
            form = DiscountFormAdd(data=form_data)
    

    并完全放弃在表单的__init__ 中设置exhibitor

    这样,您的表单数据(用于创建您的Discount)对象将是正确的并且可以被清理

    [编辑] 如何在 ModelForm 中创建隐藏的输入字段:

    class DiscountFormAdd(forms.ModelForm):
        def __init__(self, *args, **kwargs):
            super(DiscountFormAdd, self).__init__(*args, **kwargs)
            self.fields['exhibitor'].widget = forms.HiddenInput()
    
        ...
    

    或者你也可以使用 Meta 类的widgets 属性

    class DiscountFormAdd(forms.ModelForm):
    
        class Meta:
            model = Discount
            widgets = {'exhibitor': forms.HiddenInput()}
            ...
    

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-06-17
    • 2012-08-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-26
    相关资源
    最近更新 更多