简体   繁体   中英

Django: create multi choice form with input option

I wanted to ask if someone know how to make a selection in a form where one of the choices is to add input

in my forms.py:

class InRes(forms.ModelForm):

    class Meta:
        model=Results

in my models.py:

PORT_STATUS=(
    ('FTP','21'),
    ('HTTP','443,80'),
)

class Results(models.Model):
    status=models.CharField(max_length=5, choices=PORT_STATUS, default='HTTP')

i want to add and option to select one of the choices or entering my own input. any ideas?

Django forms are very picky when it comes to what is a valid choice, which is a good thing. It prevents malicious values from being passed in via manipulated POST data.

As such, you won't be able to programmatically add a choice, for one, because your choices are a hard-coded tuple, and two, because whatever choice you add won't be part of the choices the form considers valid when clean is run, because the value wasn't there when the form was initialized.

Instead, consider making the choices a separate model, and provide a separate form field to add a record for that model, so that next time, the choice will be present.

Django forms can have a mixture of model and non-model fields, so use that to add the input field. You could also control the visibility of the extra field via JavaScript. It's probably possible to combine all of this into a multi-widget for a custom field.

# theoretical code, not tested

class PortStatus(models.Model):
    status = models.CharField(max_length=100)

    def __unicode__(self):
        return self.status


# I prefer singular model names

class Result(models.Model):
    status = models.ForeignKey(PortStatus, blank=True)


class ResultsForm(forms.ModelForm):

    class Meta:
        model = Result

    extra_choice = forms.CharField(max_length=100, required=False)

    def __init__(self, *args, **kwargs):
        super(ResultsForm, self).__init__(*args, **kwargs)
        self.save_new_status = False

    def clean(self):
        cleaned_data = self.cleaned_data()
        status = cleaned_data.get('status')
        extra_choice = cleaned_data.get('extra_choice')

        if status and extra_choice:
            raise forms.ValidationError("Can't specify both")

        if not status and not extra_choice:
            raise forms.ValidationError('Make a selection or add status')

        if not status and extra_choice:
            # make sure extra_choice isn't already in choices
            if PostStatus.objects.filter(
                status__iexact=extra_choice).count() > 0:
                    raise forms.ValidationError('Status present, etc')
            else:
                self.save_new_status = True

        return cleaned_data

    def save(self, commit=True)
        instance = super(ResultsForm, self).save(commit=False)
        if self.save_new_status:
            new_status = PostStatus.objects.create(
                status=self.cleaned_data.get('extra_choice'))
            self.status = new_status
        if commit:
            instance.save()
        return instance

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM