Сравнение "похожих" строк
Имеется вот такая модель инвентаря:
class Inventory(BaseModel):
type = models.ForeignKey(
InventoryType, verbose_name='Наименование инвентаря',
on_delete=models.CASCADE)
code = models.CharField(max_length=255, verbose_name='Инвентарный номер')
date_begin_use = models.DateField(
verbose_name='Дата ввода в эксплуатацию',
validators=[MinValueValidator(date(1900, 1, 1))],
)
date_end_use = models.DateField(
null=True, blank=True,
verbose_name='Дата списания',
validators=[MinValueValidator(date(1900, 1, 1))],
)
reason_stop_using = models.CharField(
max_length=255,
blank=True, null=True,
verbose_name='Причина списания',
)
У него есть поле code - Инвентарный номер. Этот инвентарный номер должен быть уникальным, но тут есть одно НО.
Пример инвентарного номера - K35. Неясно, русская здесь K или английская. Так вот, нужно сделать проверку, которая будет при сохранении проверять новый инвентарный код на уникальность, при этом также исключая возможность сохранить "похожий" номер.
У меня, собственно, не получается придумать такой алгоритм, который производил бы такое сравнение, при этом не нагромождая код кучей костылей
Ответы (1 шт):
Добавьте в код параметр unique. Получится:
code = models.CharField(max_length=255, verbose_name='Инвентарный номер', unique=True)
Английская K и русская К имеют разный символьный код, т.е. система выдаст вам False при сравнении.
Если на страницу выводите инфу через джанговские формы, то можете ещё и в форме сделать валидацию вводимых данных: https://djbook.ru/rel1.5/ref/forms/validation.html
UPD.ver.1. Костыль через валидацию. При этом в БД должны быть коды только с символами одного языка, т.е. нельзя в одном инв.номере иметь и русские, и английские буквы. Ну и желательно, чтобы символы в инв.номерах были одного кейса: только заглавные или только строчные. Не факт, что будет работать, но мысль должна быть понятна.
from django.core.exceptions import ValidationError
import re
def my_validator(value):
status = 0
rus_val = re.findall(r'[а-яА-ЯёЁ]+', value)
eng_val = re.findall(r'[a-zA-Z]+', value)
if rus_val:
status = 1
if eng_val:
status = 2
letters_rus = {'А' : 'A', 'В' : 'B', 'Е' : 'E', 'К' : 'K', 'М' : 'M', 'Н' : 'H', 'О' : 'O', 'Р' : 'P', 'С' : 'C', 'Т' : 'T', 'Х' : 'X'}
letters_eng = {'A' : 'А', 'B' : 'В', 'E' : 'Е', 'K' : 'К', 'M' : 'М', 'H' : 'Н', 'O' : 'О', 'P' : 'Р', 'C' : 'С', 'T' : 'Т', 'X' : 'Х'}
replica = ''
if status == 1:
for element in value:
if element.upper() in letters_rus:
replica += letters_rus[element.upper()]
else:
replica += element.upper()
elif status == 2:
for element in value:
if element.upper() in letters_eng:
replica += letters_eng[element.upper()]
else:
replica += element.upper()
if Inventory.objects.filter(code=value).exists() or Inventory.objects.filter(code=replica).exists():
raise ValidationError(u'%s уже существует!' % value)
class Inventory(BaseModel):
code = models.CharField(max_length=255, verbose_name='Инвентарный номер', validators=[my_validator])
UPD.ver.2. Переопределить метод save. Тот же костыль, но внутри модели.
class Inventory(BaseModel):
code = models.CharField(max_length=255, verbose_name='Инвентарный номер')
def save(self, *args, **kwargs):
status = 0
rus_val = re.findall(r'[а-яА-ЯёЁ]+', self.code)
eng_val = re.findall(r'[a-zA-Z]+', self.code)
if rus_val:
status = 1
if eng_val:
status = 2
letters_rus = {'А' : 'A', 'В' : 'B', 'Е' : 'E', 'К' : 'K', 'М' : 'M', 'Н' : 'H', 'О' : 'O', 'Р' : 'P', 'С' : 'C', 'Т' : 'T', 'Х' : 'X'}
letters_eng = {'A' : 'А', 'B' : 'В', 'E' : 'Е', 'K' : 'К', 'M' : 'М', 'H' : 'Н', 'O' : 'О', 'P' : 'Р', 'C' : 'С', 'T' : 'Т', 'X' : 'Х'}
replica = ''
if status == 1:
for element in self.code:
if element.upper() in letters_rus:
replica += letters_rus[element.upper()]
else:
replica += element.upper()
elif status == 2:
for element in self.code:
if element.upper() in letters_eng:
replica += letters_eng[element.upper()]
else:
replica += element.upper()
if super(Inventory, self).objects.filter(code=self.code).exists() or super(Inventory, self).objects.filter(code=replica).exists():
raise Exception('{} уже существует!'.format(self.code))
else:
super(Inventory, self).save(*args, **kwargs)