Django: exclude certain form elements based on a condition

I have some form fields that I want to include / exclude based on whether any condition is met. I know how to include and exclude form elements, but I find it difficult to do this when I want these elements to be displayed based on the result of the function.

Here is my form:

class ProfileForm(ModelForm): # this_team = get Team instance from team.id passed in # how? def draft_unlocked(self): 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 

What I'm trying to do is based on the position of the total points, the team should not change their driver until the specified day. As in the case, the last team in the standings can add / reset the driver on Monday, the second - the last team can add / reset on Tuesday and so on ...

So, the first problem is how can I get an instance of Team inside the form itself from the given id. And how do I enable / exclude based on the result of draft_unlocked ().

Or maybe there is a better way to do all this?

Thanks to everyone.

+6
source share
2 answers

This is actually quite simple (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, the key points from this:

  • self.instance represents a related object if the form is connected. I believe that it is passed as a named argument, so in kwargs , which the parent constructor uses to create self.instance .
  • You can change the properties of a field after calling the parent constructor.
  • Widgets are ways to display forms. HiddenInput basically means <input type="hidden" .../> .

There is one limitation; I can interfere with the input to change the value if I change the POST / GET data. If you do not want this to happen, something that needs to be considered overrides the form validation method (clean ()). Remember that everything in Django is just objects, which means that you can actually modify the objects of the class and add data to them at random (this will not be saved). So in __init__ you could:

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

Then in your pure method for the specified form:

 def clean(self): # validate parent. Do this first because this method # will transform field values into model field values. # ie 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. 
+6
source

You can do what you need by adding your own init, where you can pass the identifier when instantiating 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 this helps you.

+2
source

Source: https://habr.com/ru/post/888712/


All Articles