在Django / Python中,如何在正确验证后,在数据库中插入按需格式化的字段数据?

问题描述 投票:0回答:1

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

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

编辑1:我对丹尼尔答案的评论中提到的代码更正了。

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 :(“验证数字不匹配”)
  5. 不接受37.551.240-50 :(“错误的位数”)

因此,验证器工作正常。

编辑2:首次编辑后文本已更正。

问题是,当我将它保存到数据库时,我想 - 无论输入情况(1)到(3) - 保存格式为:f'{numeros[0:3]}.{numeros[3:6]}.{numeros[6:9]}-{numeros[9:]}'。但是数据库总是在验证之前保存输入文本(将输入文本视为已验证)。所以+-*23755124050保存原样,而不是像我想要的那样237.551.240-50

这对我来说很棘手,我在这个过程中遗漏了一些重要的东西。有人可以开导我吗?

django model admin customvalidator
1个回答
3
投票

您应该使用带有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
© www.soinside.com 2019 - 2024. All rights reserved.