Как реализовать `others` для Django Model Choices-CharField?
У меня есть модель Django, в которой есть несколько CharFields. Многие из них представляют информацию типа
- Item Type 1
- Item Type 2
- Прочее
Обычный способ определения вариантов для поля CharField -
models.CharField(
max_length=255,
choices=ChoiceList.choices, # Defining Choices
)
Как вы уже догадались, он не отвечает моим требованиям, поскольку я не могу передать ему ничего (других), кроме заранее заданных вариантов.
Я не могу отбросить параметр choices
, поскольку мне нужно, чтобы модель содержала информацию choices
для каждого поля. Этот параметр будет использоваться для получения всех возможных вариантов для конкретного поля.
Я попробовал использовать пользовательскую функцию validator
def validate_choice(value, choices):
if value == "Others:":
print(value, choices)
valid_choices = [choice[0] for choice in choices]
if not value.startswith("Others:") and value not in valid_choices:
raise ValidationError(f"{value} is not a valid choice")
(Для простоты я определил свою логику как
)- Либо значение находится в списке выбора
- Или оно начинается с
Others:
(чтобы указать, что это намеренное значениеOther
)
Впоследствии я модифицировал CharField следующим образом, чтобы использовать новую функцию валидатора
models.CharField(
max_length=255,
choices=IPCSections.choices,
validators=[partial(validate_choice, choices=ChoiceList.choices)],
)
Затем я заметил, что валидация Choice (по Django) происходит до вызова пользовательского валидатора. Что снова приводит к той же проблеме
Others:% is not a valid choice
Заглянув в документацию Django, я нашел методы вроде
model.clean()
model.clean_all() etc.
Я пробовал переопределить эти методы, но в итоге не получил ничего полезного, а в документации не указано, в каком порядке и когда они вызываются.
Похоже, что вы имеете дело с ситуацией, когда вы хотите предоставить предустановленные варианты выбора для поля Django CharField, но также позволить пользователям вводить пользовательские значения, такие как "Others: something" Проблема, с которой вы столкнулись, заключается в том, что встроенная проверка выбора Django происходит перед вашей пользовательской проверкой, что не позволяет "Others: something" быть принятым как правильный выбор
Одним из способов решения этой проблемы является предварительная обработка вводимых данных до того, как они попадут в модель Django. Например, вы можете использовать форму или сериализатор для проверки и предварительной обработки ввода перед сохранением его в модели
from django import forms from .models import YourModel
class YourModelForm(forms.ModelForm): class Meta: model = YourModel fields = ['your_char_field']
def clean_your_char_field(self):
data = self.cleaned_data['your_char_field']
# Check if the input starts with "Others:" and process it accordingly
if data.startswith('Others:'):
# Strip "Others:" and any leading/trailing whitespace
processed_data = data[len('Others:'):].strip()
return processed_data
else:
# Check if the input is in the predefined choices
choices = dict(YourModel.YOUR_CHAR_FIELD_CHOICES)
if data not in choices:
raise forms.ValidationError('Invalid choice')
return data
Затем вы будете использовать эту форму в своих представлениях для проверки вводимых пользователем данных перед сохранением их в модели
from django.shortcuts import render, redirect
from .forms import YourModelForm
def your_view(request):
if request.method == 'POST':
form = YourModelForm(request.POST)
if form.is_valid():
instance = form.save()
# Redirect or do something else
return redirect('success_url')
else:
form = YourModelForm()
return render(request, 'your_template.html', {'form': form})
Этот подход отделяет логику проверки ввода от самой модели, позволяя вам обрабатывать пользовательский ввод, используя при этом механизмы проверки форм Django