Как реализовать `others` для Django Model Choices-CharField?

У меня есть модель Django, в которой есть несколько CharFields. Многие из них представляют информацию типа

  1. Item Type 1
  2. Item Type 2
  3. Прочее

Обычный способ определения вариантов для поля 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")

(Для простоты я определил свою логику как

)
  1. Либо значение находится в списке выбора
  2. Или оно начинается с 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

Вернуться на верх