ValueError: Не удалось найти валидатор функций при вызове makemigrations в Django 4.0
Используя решение здесь для проверки URLField, я получаю ValueError при запуске python manage.py makemigrations и не уверен почему. Что я делаю неправильно?
from django.contrib.auth.models import User
from django.db import models
from django.core.exceptions import ValidationError
from urllib.parse import urlparse
def validate_hostname(*hostnames):
hostnames = set(hostnames)
def validator(value):
try:
result = urlparse(value)
if result.hostname not in hostnames:
raise ValidationError(f'The hostname {result.hostname} is not allowed.')
except ValueError:
raise ValidationError('Invalid URL')
return validator
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.SET_NULL, null=True)
twitter = models.URLField(
blank=True,
null=True,
validators=[validate_hostname('twitter.com', 'www.twitter.com')]
)
Traceback
Вы делаете return validator, однако validator - это функция. Попробуйте return validator(hostnames), возможно, или любой другой аргумент, который вам нужно дать вашей функции.
Если вы посмотрите на вашу функцию validate_hostname, то у нее есть внутренняя функция validator, которую вы пытаетесь вернуть.
отсюда и ошибка: ValueError: Could not find function validator in userprofiles.models.
Этот фрагмент кода должен работать:
from django.contrib.auth.models import User
from django.db import models
from django.core.exceptions import ValidationError
HOST_NAMES = ['twitter.com', 'www.twitter.com']
def validate_hostname(entered_hostname):
if entered_hostname in HOST_NAMES:
return entered_hostname
else:
raise ValidationError(f'The hostname {entered_hostname} is not allowed.')
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.SET_NULL, null=True)
twitter = models.URLField(
blank=True,
null=True,
validators=[validate_hostname]
)
Вы можете изменить это, используя urlparse в validate_hostname функции
Обратите внимание, что вы также можете объявить валидаторы следующим образом:
from django.utils.deconstruct import deconstructible
@deconstructible
class HostnameValidator:
def __init__(self, hostnames):
self.hostnames = set(hostnames)
def __call__(self, value):
try:
result = urlparse(value)
if result.hostname not in self.hostnames:
raise ValidationError(f'The hostname {result.hostname} is not allowed.')
except ValueError:
raise ValidationError('Invalid URL')
def __eq__(self, other):
return (
isinstance(other, HostnameValidator) and
self.hostnames == other.hostnames
)
Таким образом можно "кривить" валидатор.
hostname = models.CharField(max_length=150, validators=[HostnameValidator(["example.org", "example.net"])])
Это можно исправить, добавив украшение functools.wraps:
from functools import wraps
[...]
def validate_hostname(*hostnames):
[...]
@wraps(validate_hostname)
def validator(value):
[...]
return validator