关于覆盖外键表单小部件以接收用户输入的 Django 最佳实践

Posted

技术标签:

【中文标题】关于覆盖外键表单小部件以接收用户输入的 Django 最佳实践【英文标题】:Django Best practices on overriding foreign key form widget to receive user input 【发布时间】:2013-11-14 10:04:10 【问题描述】:

我定义了一个模型,它有一些指向其他模型的外键。这样我在 models.py 中有以下内容:

class Appelation(models.Model):

    appelation = models.CharField(max_length=100,
                              verbose_name=_('Appelation'),
                              validators=[non_numeric],
                              blank=True,
                              unique=True
                             )


class Wine(models.Model):
    appelation = models.ForeignKey(ForeignKeyModel, null=True, blank=True, verbose_name=_('Appelation'))

forms.py

class WineForm(ModelForm):

    class Meta: 
        model = Wine

    appelation= CharField(widget=TextInput)

views.py

class WineCreateView(WineActionMixin, LoginRequiredMixin, CreateView):

model = Wine
form_class = WineForm
action = 'created'

def post(self, *args, **kwargs):
    self.request.POST = self.request.POST.copy()  # makes the request mutable

    appelationForm = modelform_factory(Appelation, fields=('appelation',))

    form_dict = 
        'appelation': appelationForm
    
    for k, modelForm in form_dict.iteritems():
        model_class = modelForm.Meta.model
        log.debug('current model_class is: %s' % model_class)
        log.debug('request is %s' % self.request.POST[k])
        try:
            obj = model_class.objects.get( **k: self.request.POST[k] )
            log.debug("object exists. %s pk from post request %s " % (model_class,obj.pk))
            self.request.POST[k] = obj.id
        except ObjectDoesNotExist as e:
            log.error('Exception %s' % e)
            f = modelForm(self.request.POST)            
            log.debug('errors %s' % f.errors)
            if f.is_valid():
                model_instance = f.save()
                self.request.POST[k] = model_instance.pk

    return super(WineCreateView,self).post(self.request, *args, **kwargs)

基本上,视图代码所做的是,如果我们传递的 Appelation 模型实例不存在,它会尝试创建一个新的 Appelation 模型实例(它是 Wine 的一个 fk)。它返回字段中的 pk,因为我们期望一个 pk,而不是字符串作为输入。

我想创建 appelationForm,因为我需要应用一些自定义验证器来验证 foreignKey 输入。

我现在看到的限制是,我看不到如何将 appelationForm 中的验证错误附加到主表单的错误,以便显示它们,而不是我们通常会从 foreignKey 字段中显示的错误。

查看完整示例代码:

https://github.com/quantumlicht/django-wine/blob/master/project/corewine/models.py https://github.com/quantumlicht/django-wine/blob/master/project/corewine/forms.py https://github.com/quantumlicht/django-wine/blob/master/project/corewine/views.py

【问题讨论】:

【参考方案1】:

您应该做的是在您的WineForm 上编写一个clean_appelation 方法,该方法根据您的标准(即现有的Appelation id 或新的Appelation 名称)全面验证输入,并引发相应的错误。然后在您看来,您可以假设表单数据是有效的并且可以工作。这应该给你一些东西开始:

class WineForm(ModelForm):
    ...
    appelation= CharField(widget=TextInput)
    def clean_appelation(self):
        data = self.cleaned_data['appelation']
        if data.isdigit():
             # assume it's an id, and validate as such
             if not Appelation.objects.filter(pk=data):
                 raise forms.ValidationError('Invalid Appelation id')
        else:
             # assume it's a name
             if ...:
                 raise forms.ValidationError('Invalid Appelation name')
        return data

【讨论】:

但是,我仍然需要将现有的 Appelation 转换为有效的 foreignKey id 对吗?如果是,这意味着我仍然需要检查 FK 存在并在需要时创建它的视图代码。 是的,您仍然在视图中进行保存,但您不必担心处理任何DoesNotExist 异常,因为您已经验证了表单中的数据。

以上是关于关于覆盖外键表单小部件以接收用户输入的 Django 最佳实践的主要内容,如果未能解决你的问题,请参考以下文章

Flutter TextField 自动完成覆盖

WordPress小部件中的重复表单字段?

如何在 ActiveScaffold 中覆盖表单标题或标题

如何编写自己的 Django 表单小部件? [复制]

在 QWidget 上绘画以覆盖整个小部件

小部件可见性更改时在 MacOS 中呈现 Qt 错误表单