You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Django自定义模型字段添加掩码方法并在DRF序列化器中调用

解决方案

一、为CardNumberField添加通用掩码方法

这里提供两种常用实现方案,都可以适配两个模型不同的字段命名:

方案1:自定义卡号值类型(推荐)

给字段返回的卡号值本身绑定masked方法,不管字段叫什么名字,直接调用值的方法即可:

# CARD NUMBER FIELD
class CardNumber(str):
    """自定义卡号类型,自带掩码能力"""
    def masked(self):
        return self[-4:].rjust(len(self), '#')


class CardNumberField(models.CharField):
    description = _('card number')

    def __init__(self, *args, **kwargs):
        kwargs['max_length'] = 19
        super().__init__(*args, **kwargs)
    
    # 处理从数据库读取的字段值
    def from_db_value(self, value, expression, connection):
        if value is None:
            return value
        return CardNumber(value)
    
    # 处理赋值给字段的内容
    def to_python(self, value):
        value = super().to_python(value)
        if value is None:
            return value
        return CardNumber(value)

使用方式:

# Card模型调用
card = Card.objects.first()
print(card.number.masked()) # 输出形如 ###########1234

# Transaction模型调用
trans = Transaction.objects.first()
print(trans.card_number.masked()) # 输出形如 ###########5678

方案2:自动给模型生成对应掩码方法

通过contribute_to_class钩子,自动给使用该字段的模型添加masked_字段名的方法:

class CardNumberField(models.CharField):
    description = _('card number')

    def __init__(self, *args, **kwargs):
        kwargs['max_length'] = 19
        super().__init__(*args, **kwargs)
    
    def contribute_to_class(self, cls, name, **kwargs):
        super().contribute_to_class(cls, name, **kwargs)
        # 动态生成掩码方法
        def get_masked_value(instance):
            number = getattr(instance, name)
            return number[-4:].rjust(len(number), '#')
        # 方法名随字段名自动适配
        setattr(cls, f'masked_{name}', get_masked_value)

使用方式:

# Card模型调用
card = Card.objects.first()
print(card.masked_number()) # 输出掩码后的卡号

# Transaction模型调用
trans = Transaction.objects.first()
print(trans.masked_card_number()) # 输出掩码后的卡号

二、DRF序列化器中调用方法

根据你选的实现方案,有两种常用调用方式:

1. 使用SerializerMethodField(通用方案)

不管选上面哪个字段实现方案都能用:

from rest_framework import serializers
from customers.models import Card
from transactions.models import Transaction

class CardSerializer(serializers.ModelSerializer):
    masked_number = serializers.SerializerMethodField()

    def get_masked_number(self, obj):
        # 如果你用方案1,写法:
        return obj.number.masked()
        # 如果你用方案2,写法:
        # return obj.masked_number()
    
    class Meta:
        model = Card
        fields = ['id', 'masked_number'] # 可以按需选择是否返回原始卡号

class TransactionSerializer(serializers.ModelSerializer):
    masked_card_number = serializers.SerializerMethodField()

    def get_masked_card_number(self, obj):
        # 如果你用方案1,写法:
        return obj.card_number.masked()
        # 如果你用方案2,写法:
        # return obj.masked_card_number()
    
    class Meta:
        model = Transaction
        fields = ['id', 'masked_card_number', 'amount']

2. 自定义DRF序列化字段(复用性高)

如果多处需要序列化掩码卡号,可以自定义专属序列化字段:

class MaskedCardNumberField(serializers.CharField):
    def to_representation(self, value):
        # value已经是CardNumber类型(方案1的情况)
        return value.masked()

# 使用示例
class TransactionSerializer(serializers.ModelSerializer):
    card_number = MaskedCardNumberField() # 直接序列化输出掩码值
    
    class Meta:
        model = Transaction
        fields = ['id', 'card_number', 'amount']

内容的提问来源于stack exchange,提问作者Jeroen van der Merwe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 11:45:09