Django动态替换ModelForm实例或不保存Form数据[image]

时间:2022-09-11 18:22:37

data entry people keep adding and duplicating models because they can't easily search for an existing item inline so I added fields to the Brand ModelForm to allow autosuggesting a Brand model.

数据输入人员不断添加和复制模型,因为他们无法轻松搜索内联的现有项目,因此我将字段添加到Brand ModelForm以允许自动提供品牌模型。

The Problem

Brand.name is a required field by the Brand model. Thus when only the Autocomplete field is filled (in the case of reassigning an existing Brand to the current company) the form.save() fails (its empty but a Brand.name is required by save() and in any case I don't want to save the form as a model instance as I've just reassigned ).

Brand.name是Brand模型的必填字段。因此,当仅填写自动填充字段时(在将现有品牌重新分配给当前公司的情况下),form.save()失败(其为空,但save()需要Brand.name,并且无论如何我都不知道我想将表单保存为模型实例,因为我刚刚重新分配了)。

If a Brand is submitted in the reassign field, I want to just set that Brands.company to the parent formset value, and silently return without trying to save the ModelForm.

如果在重新分配字段中提交了Brand,我想将Brands.company设置为父formset值,并在不尝试保存ModelForm的情况下静默返回。

The relation is a Foreign Key from Brand Model to the Company model - a company may have many Brands.

关系是从品牌模型到公司模型的外键 - 公司可能有很多品牌。

A picture says a thousand words so...

一张照片说千言万语......

Django动态替换ModelForm实例或不保存Form数据[image]

Code

class BrandAdminForm(forms.ModelForm):
    reassign_existing = AutoCompleteSelectField('brand', required=False,)
    confirm_reassign = BooleanField(required=False, help_text=_("Are you sure you want to reassign the brand?"))

    def __init__(self, *args, **kwargs):
        super(BrandAdminForm, self).__init__(*args, **kwargs)
        self.fields['name'].required = False

    def clean(self):
        cleaned_data = super(BrandAdminForm, self).clean()
        import ipdb; ipdb.set_trace()

        if cleaned_data['reassign_existing'] \
                and cleaned_data['confirm_reassign'] \
                and not self.instance.pk:
            self.instance = cleaned_data['reassign_existing']
            cleaned_data['reassign_existing'].company = self.instance.company
            cleaned_data['id'] = self.instance.id
            cleaned_data['category'] = self.instance.category.all()
            cleaned_data['website'] = self.instance.website
            cleaned_data['twitter_handle'] = self.instance.twitter_handle
            cleaned_data['wikipedia_uri'] = self.instance.wikipedia_uri
            cleaned_data['email'] = self.instance.email
            cleaned_data['name'] = self.instance.name
            return cleaned_data
        elif cleaned_data['reassign_existing'] \
                and cleaned_data['confirm_reassign'] \
                and self.instance.pk:
            raise forms.ValidationError("You can't reassign AND add/edit a brand in the same form. Clear one of the sections.")
        else:
            if not cleaned_data['name']:
                msg = u"You must add a name to a new brand."
                self._errors["name"] = self.error_class([msg])
            return cleaned_data
    class Meta:
        model = Brand

This approach almost works, only when reassigning a Brand's Categories are being list. This is M2M field.

这种方法几乎可行,只有在重新分配品牌的类别时才会列出。这是M2M领域。

Edit 1

I tried to override the save to not save when there is value in 'reassign_existing' but end up with a

当'reassign_existing'中有值但最终得到a时,我试图覆盖保存为不保存

'NoneType' object has no attribute 'company_id'

this is the setup

这是设置

class BrandAdminForm(forms.ModelForm):
reassign_existing = AutoCompleteSelectField('brand', required=False,
                                            )
confirm_reassign = BooleanField(required=False, help_text=_("Are you sure you want to reassign the brand?"))

def __init__(self, *args, **kwargs):
    super(BrandAdminForm, self).__init__(*args, **kwargs)
    self.fields['name'].required = False

def clean(self):
    cleaned_data = super(BrandAdminForm, self).clean()

    if cleaned_data['reassign_existing'] \
            and cleaned_data['confirm_reassign'] \
            and not self.instance.pk:
        cleaned_data['reassign_existing'].company = self.instance.company
        cleaned_data['reassign_existing'].save()
        return cleaned_data
    elif cleaned_data['reassign_existing'] \
            and cleaned_data['confirm_reassign'] \
            and self.instance.pk:
        raise forms.ValidationError("You can't reassign AND add/edit a brand in the same form. Clear one of the sections.")
    else:
        if not cleaned_data['name']:
            msg = u"You must add a name to a new brand."
            self._errors["name"] = self.error_class([msg])
        return cleaned_data

def save(self, *args, **kwargs):
    if not self.cleaned_data['reassign_existing']:
        super(BrandAdminForm, self).save(*args, **kwargs)

class Meta:
    model = Brand

1 个解决方案

#1


1  

This is the solution I came up with. Requires subclassing ModelForm and BaseFormsetInline

这是我提出的解决方案。需要继承ModelForm和BaseFormsetInline

class BrandBaseFormSet(BaseInlineFormSet):
def save_new(self, form, commit=True):
    import ipdb;ipdb.set_trace()
    if form.cleaned_data['reassign_existing'] \
            and form.cleaned_data['confirm_reassign'] \
            and not form.instance.pk:
        return form.cleaned_data['reassign_existing']
    else:
        import ipdb;ipdb.set_trace()
        return super(BrandBaseFormSet, self).save_new(form, commit=commit)

class BrandAdminForm(forms.ModelForm):
"""
Allow for reassigning of reverse fk relationships inline of the child.
"""
reassign_existing = AutoCompleteSelectField('brand', required=False)
confirm_reassign = BooleanField(required=False)

def __init__(self, *args, **kwargs):
    super(BrandAdminForm, self).__init__(*args, **kwargs)
    self.fields['name'].required = False

def clean(self):
    """
    Here we check if its a new form or reassigning an existing brand. If its reassigning, we just do that in
    this method.
    :return: cleaned form data
    """
    cleaned_data = super(BrandAdminForm, self).clean()
    if cleaned_data['reassign_existing'] \
            and cleaned_data['confirm_reassign'] \
            and not self.instance.pk:
        cleaned_data['reassign_existing'].company = self.instance.company
        cleaned_data['reassign_existing'].save()
        return cleaned_data
    elif cleaned_data['reassign_existing'] \
            and cleaned_data['confirm_reassign'] \
            and self.instance.pk:
        raise forms.ValidationError("You can't reassign AND add/edit a brand in the same form. Clear one of the sections.")
    else:
        if not cleaned_data['name']:
            msg = u"You must add a name to a new brand."
            self._errors["name"] = self.error_class([msg])
        return cleaned_data

def save(self, *args, **kwargs):
    if not self.cleaned_data['reassign_existing']:
        return super(BrandAdminForm, self).save(*args, **kwargs)
    else:
        return self.cleaned_data['reassign_existing']

class Meta:
    model = Brand

#1


1  

This is the solution I came up with. Requires subclassing ModelForm and BaseFormsetInline

这是我提出的解决方案。需要继承ModelForm和BaseFormsetInline

class BrandBaseFormSet(BaseInlineFormSet):
def save_new(self, form, commit=True):
    import ipdb;ipdb.set_trace()
    if form.cleaned_data['reassign_existing'] \
            and form.cleaned_data['confirm_reassign'] \
            and not form.instance.pk:
        return form.cleaned_data['reassign_existing']
    else:
        import ipdb;ipdb.set_trace()
        return super(BrandBaseFormSet, self).save_new(form, commit=commit)

class BrandAdminForm(forms.ModelForm):
"""
Allow for reassigning of reverse fk relationships inline of the child.
"""
reassign_existing = AutoCompleteSelectField('brand', required=False)
confirm_reassign = BooleanField(required=False)

def __init__(self, *args, **kwargs):
    super(BrandAdminForm, self).__init__(*args, **kwargs)
    self.fields['name'].required = False

def clean(self):
    """
    Here we check if its a new form or reassigning an existing brand. If its reassigning, we just do that in
    this method.
    :return: cleaned form data
    """
    cleaned_data = super(BrandAdminForm, self).clean()
    if cleaned_data['reassign_existing'] \
            and cleaned_data['confirm_reassign'] \
            and not self.instance.pk:
        cleaned_data['reassign_existing'].company = self.instance.company
        cleaned_data['reassign_existing'].save()
        return cleaned_data
    elif cleaned_data['reassign_existing'] \
            and cleaned_data['confirm_reassign'] \
            and self.instance.pk:
        raise forms.ValidationError("You can't reassign AND add/edit a brand in the same form. Clear one of the sections.")
    else:
        if not cleaned_data['name']:
            msg = u"You must add a name to a new brand."
            self._errors["name"] = self.error_class([msg])
        return cleaned_data

def save(self, *args, **kwargs):
    if not self.cleaned_data['reassign_existing']:
        return super(BrandAdminForm, self).save(*args, **kwargs)
    else:
        return self.cleaned_data['reassign_existing']

class Meta:
    model = Brand