django: exclude certain form elements based on a condition

后端 未结 2 1113
闹比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 <input type="hidden" .../>.

    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.
    
    0 讨论(0)
  • 2021-01-18 08:58

    You can do what you need by adding your own init where you can pass in the id when you instantiate the form class:

    class ProfileForm(ModelForm):
        def __init__(self, team_id, *args, **kwargs):
            super(ProfileForm, self).__init__(*args, **kwargs)
    
            this_team = Team.objects.get(pk=team_id)
    
            teams = Team.objects.order_by('total_points')
            count = 0
            for team in teams:
                if team.pk == this_team.pk:
                    break
                count += 1
    
            now = datetime.datetime.now().weekday()
            if now >= count:
                # show driver_one, driver_two, driver_three
            else:
                # do not show driver_one, driver_two, driver_three
    
    class Meta:
        model = Team
    
    #views.py
    def my_view(request, team_id):
        profile_form = ProfileForm(team_id, request.POST or None)
        #more code here
    

    Hope that helps you out.

    0 讨论(0)
提交回复
热议问题