Создание списка выбора из одного поля в другое через внешний ключ Django

У меня есть две модели, связанные между собой внешним ключом, как показано ниже :

class Service(models.Model):
    title = models.CharField(max_length=100)
    counter = models.PositiveIntegerField()

    def __str__(self) -> str:
        return f'{self.title}'


class Reservation(models.Model):
    user    = models.ForeignKey(User, on_delete=models.CASCADE)
    service = models.ForeignKey(Service, on_delete=models.CASCADE)
    turn    = models.PositiveIntegerField()

    def __str__(self) -> str:
        return f'{self.user}'

Я хочу сделать так: поле turn в Reservation получает опцию choices и значения выбора от 1 до service.counter. service является внешним ключом. Например, если counter в модели Service равно 5, то поле turn в Reservation будет выглядеть следующим образом : turn = models.PositiveIntegerField(choices=[1,2,3,4,5]). цикл for явно не будет работать :) . Должен ли я создать поле property или класс Meta? В любом случае, как я могу изменить мое поле таким образом? Любая помощь будет признательна и благодарна.

Вариант 1: Только Service уже существует, а вы хотите создать совершенно новый Reservation и позволить пользователю выбрать подключенный Service. Ответ на этот вариант, скорее всего, не будет найден в django, бэкенде, а должен быть обработан во фронтенде. Причина в том, что требуется переключать возможные варианты turn на лету, когда пользователь выбирает другой подключенный Service.

Вариант 2: Начальный Reservation-объект с подключенным Service уже существует, когда пользователю предлагается выбрать turn. Это можно сделать с помощью django. Но не на уровне models.py. Эта логика должна быть в forms.py. Нам нужна модель формы, в которой нужно заполнить только поле turn.

class ReservationForm(forms.ModelForm):
    class Meta:
        model = Reservation
        fields = ['turn']
        widgets = {
            'turn': forms.Select(),  # Set the widget for the 'turn' field to Select
        }

    def __init__(self, *args, **kwargs):
        service_instance = kwargs.pop('service_instance', None)
        super(ReservationForm, self).__init__(*args, **kwargs)

        if service_instance:
            # Set the choices for the 'turn' field based on the service instance's counter
            self.fields['turn'].choices = [(i, i) for i in range(service_instance.counter + 1)]
        else:
            # If no service instance is provided, default to an empty list
            self.fields['turn'].choices = []


# views.py
service_instance = Service.objects.get(id=service_id)
form = ReservationForm(service_instance=service_instance)

Выше показано, как делать динамические формы. По сути, вы заранее выбираете в представлении, что вы хотите передать форме. Затем в методе __init__ вы перехватываете это и устанавливаете соответствующим образом.

Поскольку вы сказали, что эта форма всегда уже привязана к объекту и, следовательно, уже подключена к службе, вы можете выбрать этот вариант:

class ReservationForm(forms.ModelForm):
    class Meta:
        model = Reservation
        fields = ['turn']
        widgets = {
            'turn': forms.Select(),  # Set the widget for the 'turn' field to Select
        }

    def __init__(self, *args, **kwargs):
        super(ReservationForm, self).__init__(*args, **kwargs)
        # next line checks if an object is already connected to the form and a service connected
        service_id = self.data.get('service') if self.is_bound else None

        if service_id:
            try:
                service_instance = Service.objects.get(id=service_id)
                # Set the choices for the 'turn' field based on the service instance's counter
                self.fields['turn'].choices = [(i, i) for i in range(service_instance.counter + 1)]
            except Service.DoesNotExist:
                # If the service does not exist, default to an empty list
                self.fields['turn'].choices = []
        else:
            # If no service ID is provided, default to an empty list
            self.fields['turn'].choices = []

# views.py
# here you need to pass an instance to the form
form = ReservationForm(request.POST, instance=Reservation.objects.get(id=1)
Вернуться на верх