如何为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
相关产品推荐
相关产品推荐

