Django 验证电子邮件外键

Django Validate an Email Foreign Key

我有这个配置文件资源:

class ProfileResource(resources.ModelResource):
    email = fields.Field(attribute='email', 
        widget=CharWidget(), 
        column_name='email')

    class Meta:
        model = Profile
        clean_model_instances = True
        import_id_fields = ('email',)

在创建配置文件时验证电子邮件。它工作正常但是当我将它用作外键时,如:

class InvestmentResource(resources.ModelResource):
    email = fields.Field(attribute='email', 
        widget=ForeignKeyWidget(Profile, field='email'), 
        column_name='email')

    class Meta:
        model = Investment
        clean_model_instances = True
        import_id_fields = ('id',)

    def before_import_row(self, row, row_number=None, **kwargs):
        self.email = row["email"]
        self.profile__firstname = row["firstname"]
        self.profile__lastname = row["lastname"]

    def after_import_instance(self, instance, new, row_number=None, **kwargs):
        """
        Create any missing Profile entries prior to importing rows.
        """
        try:
            profile, created = Profile.objects.get_or_create(email=self.email)

            profile.firstname = self.profile__firstname
            profile.lastname = self.profile__lastname
            profile.save()

            instance.profile = profile

        except Exception as e:
            print(e, file=sys.stderr)

它不再验证电子邮件。我尝试为 InvestmentResource 上的电子邮件添加两个小部件 ForeignKeyWidgetCharWidget,但它没有用。

然后如何验证 InvestmentResource 中的电子邮件?

问题是您的 'email' 字段被视为查找键而不是电子邮件地址。这可以通过自定义 ForeignKeyWidget 添加电子邮件验证来解决。

发生的情况是您正在导入投资资源,它可能类似于:

email,firstname,lastname
bob@example.com,bob,smith

您已将 InvestmentResource 配置为使用电子邮件作为 Profile 的查找键。这意味着 django-import-export 不会将其作为电子邮件地址处理,而是作为 Profile 的查找键进行处理,因此在代码 (ForeignKeyWidget) 中,它将执行如下操作:

Profile.objects.get(email='bob@example.com')

如果成功,您的 Investment 实例现在将具有正确的配置文件作为外键引用。

如果关联的 Profile 不存在,这将引发 DoesNotExist 异常。因此,您可以争辩说,在 csv 中发送的电子邮件地址必须有效,因为如果无效,则无法进行查找。

但是,如果您想在尝试加载 Profile 之前检查电子邮件在语法上是否有效,那么这很容易做到,您需要先覆盖 ForeignKeyWidget 来执行验证:

from django.core.exceptions import ValidationError
from django.core.validators import validate_email

class ValidatingForeignKeyWidget(widgets.ForeignKeyWidget):

    def clean(self, value, row=None, *args, **kwargs):
        
        try:
            validate_email(value)
        except ValidationError as e:
            # a quirk of import-export means that the ValidationError 
            # should be re-raised
            raise ValueError(f"invalid email {e}")
    
        try:
            val = super().clean(value)
        except self.model.DoesNotExist:
            raise ValueError(f"{self.model.__name__} with value={value} does not exist")
        return val