Создание поля динамического выбора

136

У меня возникли проблемы, пытаясь понять, как создать поле динамического выбора в Django. У меня есть модель, настроенная примерно так:

class rider(models.Model):
     user = models.ForeignKey(User)
     waypoint = models.ManyToManyField(Waypoint)

class Waypoint(models.Model):
     lat = models.FloatField()
     lng = models.FloatField()

То, что я пытаюсь сделать, это создать поле выбора, значения которого являются путевыми точками, связанными с этим гонщиком (который будет человеком, вошедшим в систему).

В настоящее время я переопределяю init в моих формах следующим образом:

class waypointForm(forms.Form):
     def __init__(self, *args, **kwargs):
          super(joinTripForm, self).__init__(*args, **kwargs)
          self.fields['waypoints'] = forms.ChoiceField(choices=[ (o.id, str(o)) for o in Waypoint.objects.all()])

Но все, что делает, это перечисляет все путевые точки, они не связаны с каким-либо конкретным райдером. Любые идеи? Спасибо.

что-что
источник

Ответы:

191

Вы можете фильтровать путевые точки, передавая пользователю форму инициализации

class waypointForm(forms.Form):
    def __init__(self, user, *args, **kwargs):
        super(waypointForm, self).__init__(*args, **kwargs)
        self.fields['waypoints'] = forms.ChoiceField(
            choices=[(o.id, str(o)) for o in Waypoint.objects.filter(user=user)]
        )

с вашей точки зрения при инициации формы передать пользователю

form = waypointForm(user)

в случае модельной формы

class waypointForm(forms.ModelForm):
    def __init__(self, user, *args, **kwargs):
        super(waypointForm, self).__init__(*args, **kwargs)
        self.fields['waypoints'] = forms.ModelChoiceField(
            queryset=Waypoint.objects.filter(user=user)
        )

    class Meta:
        model = Waypoint
Ashok
источник
20
Используйте ModelChoiceField независимо от того, является ли он ModelForm - он работает и на обычных формах.
Даниэль Розман
9
Что вы делаете, когда хотите получить данные запроса? waypointForm (request.POST) не будет проверяться в первом, потому что данных для проверки больше нет.
Breedly
1
@Ashok Как можно использовать виджет CheckboxSelectMultiple в этом случае? Для модели формы особенно.
wasabigeek
2
Если вы хотите использовать CheckboxSelectMultipleвиджет с этим, вы захотите использовать MultipleChoiceFieldили ModelMultipleChoiceField. Поначалу кажется, что он работает ChoiceField, но внутреннее устройство сломается, когда вы попытаетесь сохранить форму.
coredumperror
1
Спасибо @CoreDumpError - просто боролся с этим почти 2 часа, прежде чем читать ваш комментарий (дох!), И это наконец заставило все работать. Другие, остерегайтесь нарушения формы отправки (проблемы с Unicode), если вы планируете использовать CheckboxSelectMultipleвиджет с этим кодом. Обязательно смените ChoiceFormнаMultipleChoiceForm
мягко
11

Есть встроенное решение для вашей проблемы: ModelChoiceField .

Как правило, всегда стоит пытаться использовать, ModelFormкогда вам нужно создать / изменить объекты базы данных. Работает в 95% случаев, и это намного чище, чем создание собственной реализации.

Александр Лебедев
источник
8

проблема в том, когда вы делаете

def __init__(self, user, *args, **kwargs):
    super(waypointForm, self).__init__(*args, **kwargs)
    self.fields['waypoints'] = forms.ChoiceField(choices=[ (o.id, str(o)) for o in Waypoint.objects.filter(user=user)])

в запросе на обновление предыдущее значение будет потеряно!

Liang
источник
3

Как насчет передачи экземпляра райдера в форму при его инициализации?

class WaypointForm(forms.Form):
    def __init__(self, rider, *args, **kwargs):
      super(joinTripForm, self).__init__(*args, **kwargs)
      qs = rider.Waypoint_set.all()
      self.fields['waypoints'] = forms.ChoiceField(choices=[(o.id, str(o)) for o in qs])

# In view:
rider = request.user
form = WaypointForm(rider) 
Маной Говиндан
источник
2

Под рабочим раствором с нормальным выбором поля. Моя проблема заключалась в том, что каждый пользователь имеет свои собственные опции поля выбора CUSTOM, основанные на нескольких условиях.

class SupportForm(BaseForm):

    affiliated = ChoiceField(required=False, label='Fieldname', choices=[], widget=Select(attrs={'onchange': 'sysAdminCheck();'}))

    def __init__(self, *args, **kwargs):

        self.request = kwargs.pop('request', None)
        grid_id = get_user_from_request(self.request)
        for l in get_all_choices().filter(user=user_id):
            admin = 'y' if l in self.core else 'n'
            choice = (('%s_%s' % (l.name, admin)), ('%s' % l.name))
            self.affiliated_choices.append(choice)
        super(SupportForm, self).__init__(*args, **kwargs)
        self.fields['affiliated'].choices = self.affiliated_choice
Deil
источник
Именно то, что я искал! Спасибо!
Raptor
это сделало мою жизнь проще .. Большое спасибо .. :)
Аравинд Р Пиллаи
1

Как указали Breedly и Liang, решение Ashok не позволит вам получить значение select при публикации формы.

Один немного другой, но все же несовершенный способ решения проблемы:

class waypointForm(forms.Form):
    def __init__(self, user, *args, **kwargs):
        self.base_fields['waypoints'].choices = self._do_the_choicy_thing()
        super(waypointForm, self).__init__(*args, **kwargs)

Это может вызвать некоторые проблемы с совпадением.

Haroldo_OK
источник
1

Вы можете объявить поле как первоклассный атрибут вашей формы и просто динамически устанавливать выбор:

class WaypointForm(forms.Form):
    waypoints = forms.ChoiceField(choices=[])

    def __init__(self, user, *args, **kwargs):
        super().__init__(*args, **kwargs)
        waypoint_choices = [(o.id, str(o)) for o in Waypoint.objects.filter(user=user)]
        self.fields['waypoints'].choices = waypoint_choices

Вы также можете использовать ModelChoiceField и установить набор запросов в init аналогичным образом.

Zags
источник
0

Если вам нужно поле динамического выбора в django admin; Это работает для django> = 2.1.

class CarAdminForm(forms.ModelForm):
    class Meta:
        model = Car

    def __init__(self, *args, **kwargs):
        super(CarForm, self).__init__(*args, **kwargs)

        # Now you can make it dynamic.
        choices = (
            ('audi', 'Audi'),
            ('tesla', 'Tesla')
        )

        self.fields.get('car_field').choices = choices

    car_field = forms.ChoiceField(choices=[])

@admin.register(Car)
class CarAdmin(admin.ModelAdmin):
    form = CarAdminForm

Надеюсь это поможет.

Тобиас Эрнст
источник