在Django/Python中,如何在正确验证后,将字段数据插入到数据库中,并将其格式化为所需的格式?

In Django/Python, how to, after proper validation, insert a field data in the database formatted as desired?

我正在使用 Django/Python 开发一个 Web 系统,我需要来自用户模型的特定字段:CPF (https://en.wikipedia.org/wiki/Cadastro_de_Pessoas_Físicas)。我需要它与验证器并在插入数据库之前正确格式化。

我尝试使用 django-localflavordjango-localflavor-br (https://django-localflavor.readthedocs.io/en/latest/localflavor/br/) 中的 localflavor.br.forms.BRCPFField,但 makemigrations 也无法识别新字段migrate 可以更新数据库中相应的 table (我认为这是一个错误,但现在不是这个问题)。因此,在考虑了其他选择之后,我决定尝试一下并编写自己的验证器。

编辑 1: 代码已更正,如我对 Daniel 的回答的评论中所述。

models.py中:

from django.core.exceptions import ValidationError

def validate_cpf(cpf):
    digitos = [int(digit) for digit in cpf if digit.isdigit()]
    if len(digitos) < 11 or len(digitos) > 11:
        raise ValidationError("Wrong number of digits")
    sop = sum(a*b for a, b in zip(digitos[0:9], range(10, 1, -1)))
    ed1 = (sop * 10 % 11) % 10
    sop = sum(a*b for a, b in zip(digitos[0:10], range(11, 1, -1)))
    ed2 = (sop * 10 % 11) % 10
    if ed1 != digitos[9] or ed2 != digitos[10]:
        raise ValidationError("Verification digits don't match")
    else:
        numeros = ''.join(str(d) for d in digitos)
        return f'{numeros[0:3]}.{numeros[3:6]}.{numeros[6:9]}-{numeros[9:]}'

class Usuario(models.Model):
    name = models.CharField(max_length=256)
    cpf = models.CharField(max_length=14, validators=[validate_cpf])

admin.py中:

from .models import Usuario

class UsuarioAdmin(admin.ModelAdmin):
    fieldsets = [ ('ID', { 'fields': ['name', 'cpf', ], }), ]
    list_display = ('name', 'cpf', )

admin.site.register(Usuario, UsuarioAdmin)

在管理区域 (http://localhost:8000/admin/) 中,我可以毫无问题地添加新用户。书面验证器也可以正常工作:

  1. 23755124050 被接受。
  2. 237.551.240-50 被接受。
  3. +-*23755124050 或其他一些可以想象的字符串,其中包含此特定顺序中的 11 位数字。
  4. 237.551.240-51 不被接受:("Verification digits don't match")
  5. 37.551.240-50 不被接受:("Wrong number of digits")

所以,验证器工作正常。

编辑 2: 文本在第一次编辑后更正。

问题是,当我将它保存到数据库中时,无论输入大小写 (1) 到 (3),我都希望将其格式保存为:f'{numeros[0:3]}.{numeros[3:6]}.{numeros[6:9]}-{numeros[9:]}'。但是数据库总是保存验证前给定的输入文本(将输入文本视为已验证)。所以 +-*23755124050 按原样保存,而不是 237.551.240-50 我想要的方式。

这对我来说很棘手,我在这个过程中遗漏了一些重要的东西。有没有人能赐教一下?

与其使用验证器,不如使用带有 clean_cpf 方法的自定义表单;这具有验证输入和以您要存储的形式返回数据的双重责任。 (请注意,在干净的方法中,您需要提高 forms.ValidationError,而不是 core.exceptions.ValidationError)。

class MyUsarioForm(forms.ModelForm):
    def clean_cpf(self):
        cpf = self.cleaned_data['cpf']
        digitos = [int(digit) for digit in cpf if digit.isdigit()]
        if len(digitos) < 11 or len(digitos) > 11:
            raise forms.ValidationError("Wrong number of digits")
        sop = sum(a*b for a, b in zip(digitos[0:9], range(10, 1, -1)))
        ed1 = (sop * 10 % 11) % 10
        sop = sum(a*b for a, b in zip(digitos[0:10], range(11, 1, -1)))
        ed2 = (sop * 10 % 11) % 10
        if ed1 != digitos[9] or ed2 != digitos[10]:
            raise forms.ValidationError("Verification digits don't match")
        else:
            return f'{digitos[0:2]}.{digitos[3:5]}.{digitos[6:8]}-{digitos[9:10]}'


class UsuarioAdmin(admin.ModelAdmin):
    form = UsarioForm