管理表单中的django自定义模型字段给出无效选择错误 [英] django custom model field in admin form gives invalid choice error

查看:1351
本文介绍了管理表单中的django自定义模型字段给出无效选择错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下类用于自定义模型字段:

I have the following class which to be used for a custom model field:

class PaymentGateway(object):

    def fullname(self):
        return self.__module__ + "." + self.__class__.__name__

    def authorize(self):
        raise NotImplemented()

    def pay(self):
        raise NotImplemented()

    def __unicode__(self):
        return self.fullname()

class DPS(PaymentGateway):
    def authorize(self):
        pass

    def pay(self):
        pass

这是我如何编写自定义模型字段:

This is how I am writing the custom model field:

from django.db import models
from django.utils.six import with_metaclass
from django.utils.module_loading import import_by_path


class PaymentGatewayField(with_metaclass(models.SubfieldBase, models.CharField)):

    def __init__(self, *args, **kwargs):
        kwargs['max_length'] = 255
        super(PaymentGatewayField, self).__init__(*args, **kwargs)

    def to_python(self, value):
        if value and isinstance(value, basestring):
            kls = import_by_path(value)
            return kls()
        return value

    def get_prep_value(self, value):
        if value and not isinstance(value, basestring):
            return value.fullname()
        return value

    def value_from_object(self, obj):
        return self.get_prep_value(getattr(obj, self.attname))


    def formfield(self, **kwargs):
        defaults = {'form_class': PaymentGatewayFormField}
        defaults.update(kwargs)
        return super(PaymentGatewayField, self).formfield(**defaults)


class PaymentGatewayFormField(BaseTemporalField):

    def to_python(self, value):
        if value in self.empty_values:
            return None
        if isinstance(value, PaymentGateway):
            return value
        if value and isinstance(value, basestring):
            kls = import_by_path(value)
            return kls()
        return super(PaymentGatewayFormField, self).to_python(value)

这是如何在一个模型中使用的:

And this is how it is used in a model:

class BillingToken(models.Model):
    user = models.ForeignKey('User', related_name='billingtokens')
    name = models.CharField(max_length=255)
    card_number = models.CharField(max_length=255)
    expire_on = models.DateField()
    token = models.CharField(max_length=255)
    payment_gateway = PaymentGatewayField(choices=[('project.contrib.paymentgateways.dps.DPS', 'DPS')])

我已将模型添加到管理员:

I have added the model to admin:

class BillingTokenInline(admin.StackedInline):
    model = BillingToken
    extra = 0


class UserAdmin(admin.ModelAdmin):
    inlines = [BillingTokenInline]


admin.site.register(User, UserAdmin)

所以如果我去编辑ex已经选择了DPS,并且保存,我收到一个无效的选择错误:

So if I go to edit existing user record, which it's billingtoken record has 'DPS' already chosen, and hit save, I get a invalid choice error:

Select a valid choice. project.contrib.paymentgateways.dps.DPS is not one of the available choices. 

我试图跟踪django代码,发现错误消息定义在 django.forms.fields.ChoiceField

I have tried to trace the django code and found the error message is defined in django.forms.fields.ChoiceField:

class ChoiceField(Field):
    widget = Select
    default_error_messages = {
        'invalid_choice': _('Select a valid choice. %(value)s is not one of the available choices.'),
    }

    def __init__(self, choices=(), required=True, widget=None, label=None,
                 initial=None, help_text='', *args, **kwargs):
        super(ChoiceField, self).__init__(required=required, widget=widget, label=label,
                                        initial=initial, help_text=help_text, *args, **kwargs)
        self.choices = choices

    def __deepcopy__(self, memo):
        result = super(ChoiceField, self).__deepcopy__(memo)
        result._choices = copy.deepcopy(self._choices, memo)
        return result

    def _get_choices(self):
        return self._choices

    def _set_choices(self, value):
        # Setting choices also sets the choices on the widget.
        # choices can be any iterable, but we call list() on it because
        # it will be consumed more than once.
        self._choices = self.widget.choices = list(value)

    choices = property(_get_choices, _set_choices)

    def to_python(self, value):
        "Returns a Unicode object."
        if value in self.empty_values:
            return ''
        return smart_text(value)

    def validate(self, value):
        """
        Validates that the input is in self.choices.
        """
        super(ChoiceField, self).validate(value)
        if value and not self.valid_value(value):
            raise ValidationError(
                self.error_messages['invalid_choice'],
                code='nvalid_choice',
                params={'value': value},
            )

    def valid_value(self, value):
        "Check to see if the provided value is a valid choice"
        text_value = force_text(value)
        for k, v in self.choices:
            if isinstance(v, (list, tuple)):
                # This is an optgroup, so look inside the group for options
                for k2, v2 in v:
                    if value == k2 or text_value == force_text(k2):
                        return True
            else:
                if value == k or text_value == force_text(k):
                    return True
        return False

但是在将提交ValidationError 行之前添加一些调试语句在这个函数中,这里没有提出异常,但错误信息绝对是从这里引用的。这暗示我在其他地方延伸ChoiceField可能会提高这个异常,我已经尝试了明显的(ChoiceField,TypedChoiceField,MultipleChoiceField,TypedMultipleChoiceField)仍然没有运气。这已经消耗了很多时间,并希望找到一些聪明的线索。

But after putting some debug statements before the raise ValidationError line in this function, the exception is not raised here, but the error message is definitely referenced from here. Which hints me that somewhere else is extending ChoiceField might be raising this exception, and I have tried the obvious ones (ChoiceField, TypedChoiceField, MultipleChoiceField, TypedMultipleChoiceField) still no luck. This has already consumed a lot of my time and would like to seek some clever clues.

推荐答案

最后,弄清楚它在哪里抛出错误:

Finally, figured out where it is throwing the error:

它在 django / db / models / fields / __ init __。py 第236行

通常是因为第234行:

typically because of line 234:

elif value == option_key:

其中value是一个 PaymentGateway object和option_key是字符串

Where value is a PaymentGateway object and option_key is a string

要解决这个问题,我不得不重写干净的方法:

To fix this problem, I had to override the clean method:

def clean(self, value, model_instance):
    value = unicode(value) 
    self.validate(value, model_instance)
    self.run_validators(value)      
    return self.to_python(value)

这篇关于管理表单中的django自定义模型字段给出无效选择错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆