Как добавить пользовательский метод к полю модели в Django?
У меня есть две модели, которые будут использовать одно и то же поле CardNumberField()
для хранения номеров кредитных карт. Как я могу добавить пользовательский метод к полю для маскировки номеров карт?
Я создал CardNumberField()
, который наследуется от models.Charfield
:
# CARD NUMBER FIELD
class CardNumberField(models.CharField):
description = _('card number')
def __init__(self, *args, **kwargs):
kwargs['max_length'] = 19
super().__init__(*args, **kwargs)
Затем CardNumberField()
импортируется и используется в моем customers/models.py:
# CARD MODEL
class Card(models.Model):
number = CardNumberField()
...
def __str__(self):
return 'Card [{number}]'.format(number=self.number)
... и в моем transactions/models.py:
# TRANSACTION MODEL
class Transaction(models.Model):
card_number = CardNumberField()
...
def __str__(self):
return 'Transaction ...'
Итак, как я могу добавить следующий метод в мою CardNumberField()
, чтобы его использовали обе мои модели?
def masked_number(self):
# display masked card number
number = self.number
return number[-4:].rjust(len(number), '#')
Кроме того, как мне захватить этот метод поля в классе сериализатора DRF?
Вместо этого используйте абстрактную модель, создание подкласса поля полезно для создания совершенно новых типов столбцов, а не для этой цели.
class ModelWithCardNumber(models.Model):
card_number = models.CharField(max_length=19)
@property
def masked_number(self):
return self.card_number[-4:].rjust(len(number), '#')
class Meta:
abstract = True
class Card(ModelWithCardNumber):
def __str__(self):
return 'Card [{number}]'.format(number=self.number)
class Transaction(ModelWithCardNumber):
def __str__(self):
return 'Transaction ...'
Теперь в вашем сериализаторе вы можете получить доступ к Card.masked_number
и Transaction.masked_number
.
Вы можете переопределить метод contribute_to_class
, чтобы не только внести поле, но и включить дополнительный метод:
from functools import partialmethod
def _mask_number(self, field):
number = getattr(self, field.attname)
return number[-4:].rjust(len(number), '#')
# CARD NUMBER FIELD
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)
setattr(
cls, f'masked_{self.name}',
partialmethod(_mask_number, field=self)
)
Если вы добавляете поле foo
в класс модели, он автоматически добавит метод masked_foo
в этот класс. Таким образом, это также означает, что если у вас есть два или более CardNumberField
, он добавит два или более masked_foo
методов.