Initial Value in modelForm through init

42 Views Asked by At

It could be that I'm trying to solve this issue the wrong way. But if someone has a good idea of how to solve this I'm all ears for it.

What I want to achieve. I have a model with a Charfield, that I populate with data from a field in another model. For different reasons a ForeignField is not an option for me. So instead I define the data so select from the Init process of my model form. It works well to establish a list to pick from. I have tried to simplify the code below.

class myModel(model.Models)
   troop = models.CharField(......)
   stuff = models.CharField(......)
   camp = models.SlugField(......)
   def __str__(self):
        return self.troop

class mySecondModel(model.Models)
   name = models.CharField(......)
   other_stuff = models.CharField(......)
   slug = models.SlugField(......)
   camp = models.SlugField(......)
   def __str__(self):
        return self.slug

class View_Troop(UpdateView):
    template_name = 'troop.html'
    model = myModel
    form_class = myModel_form

    def get_form_kwargs(self):
        id_ = self.kwargs.get('id')           #//'id' = troop #
        obj= myModel.objects.filter(troop=id_)
        kwargs = super(View_Troop, self).get_form_kwargs()
        kwargs.update({'troop': obj.troop})
        kwargs.update({'camp': obj.camp})
        return kwargs


class myModel_form(forms.ModelForm)
    stuff = ModelChoiceField(queryset= mySecondModel.objects.all())

    class Meta:
        model = myModel
        fields = '__all__'
      
    def __init__(self, *args, **kwargs):
        troop = kwargs.pop('troop')
        camp = kwargs.pop('camp')
        variable_1= .... function based on troop value
        super(myModel_form, self).__init__(*args, **kwargs)  
        self.fields['stuff'].queryset=mySecondModel.objects.filter(other_stuff__contains= variable_1, camp=camp)
        self.fields['stuff'].label_from_instance = lambda obj: "%s - %s" % (obj.name, obj.other_stuff)
        

But I have not figured out how to pre-select the current value (initial) in the form. Any hints?

2

There are 2 best solutions below

1
McPherson On

You preselect a field value with initial argument to the form instance. In the UpdateView, override get_initial method that returns a dictionary of the field_name:value.

def get_initial(self):
    return {"stuff": "whatever value from your select options"} 

Or, if the initial value is always the same, you can simply provide an initial attribute to the UpdateView:

class View_Troop(UpdateView):
    initial = {"stuff": "value"} 
    ... 

I'm curious though, why isn't a ForeignKey field an option for you? This seems a good place for a ForeignKey. As the docs states:

Note that choices can be any sequence object – not necessarily a list or tuple. This lets you construct choices dynamically. But if you find yourself hacking choices to be dynamic, you’re probably better off using a proper database table with a ForeignKey. choices is meant for static data that doesn’t change much, if ever.

0
Jonas On

OK I think I figured it out.

I move the whole field classification into the init method.

class myModel_form(forms.ModelForm)

    class Meta:
        model = myModel
        fields = '__all__'
      
    def __init__(self, *args, **kwargs):
        troop = kwargs.pop('troop')
        camp = kwargs.pop('camp')
        variable_1= .... function based on troop value
        super(myModel_form, self).__init__(*args, **kwargs)  
        self.fields['stuff']=ModelfChoiceField(queryset=mySecondModel.objects.filter(other_stuff__contains= variable_1, camp=camp))
        self.initial['stuff']= ....whatever I want...         
        self.fields['stuff'].label_from_instance = lambda obj: "%s - %s" % (obj.name, obj.other_stuff)

Now the question if it worth the effort. Probably easier to use ForeignKey in the model and then in the save() process create a duplicate field that is a CharField.