django: exclude certain form elements based on a condition

后端 未结 2 1151
闹比i
闹比i 2021-01-18 08:36

I have some form fields that I want to include/exclude based on whether or not a certain condition is met. I know how to include and exclude form elements, but I am having d

2条回答
  •  刺人心
    刺人心 (楼主)
    2021-01-18 08:53

    This is actually fairly straightforward (conditional field settings) - here's a quick example:

    from django.forms import Modelform
    from django.forms.widgets import HiddenInput
    
    class SomeForm(ModelForm):
    
        def __init__(self, *args, **kwargs):
            # call constructor to set up the fields. If you don't do this 
            # first you can't modify fields.
            super(SomeForm, self).__init__(*args, **kwargs)
    
            try:
                # make somefunc return something True
                # if you can change the driver.
                # might make sense in a model?
                canchangedriver = self.instance.somefunc()                          
            except AttributeError:
                # unbound form, what do you want to do here?
                canchangedriver = True # for example?
    
            # if the driver can't be changed, use a input=hidden
            # input field.
            if not canchangedriver:
                self.fields["Drivers"].widget = HiddenInput()
    
        class Meta:
            model = SomeModel
    

    So, key points from this:

    • self.instance represents the bound object, if the form is bound. I believe it is passed in as a named argument, therefore in kwargs, which the parent constructor uses to create self.instance.
    • You can modify the field properties after you've called the parent constructor.
    • widgets are how forms are displayed. HiddenInput basically means .

    There is one limitation; I can tamper with the input to change a value if I modify the submitted POST/GET data. If you don't want this to happen, something to consider is overriding the form's validation (clean()) method. Remember, everything in Django is just objects, which means you can actually modify class objects and add data to them at random (it won't be persisted though). So in your __init__ you could:

    self.instance.olddrivers = instance.drivers.all()
    

    Then in your clean method for said form:

    def clean(self):
        # validate parent. Do this first because this method
        # will transform field values into model field values.
        # i.e. instance will reflect the form changes.
        super(SomeForm, self).clean()
    
        # can we modify drivers?
        canchangedriver = self.instance.somefunc() 
    
        # either we can change the driver, or if not, we require 
        # that the two lists are, when sorted, equal (to allow for 
        # potential non equal ordering of identical elements).
    
        # Wrapped code here for niceness
        if (canchangedriver or 
                       (sorted(self.instance.drivers.all()) == 
                        sorted(self.instance.olddrivers))):  
            return True
        else:
            raise ValidationError() # customise this to your liking.
    

提交回复
热议问题