简体   繁体   中英

Django - ModelForm Dynamic field update

I'm trying to update certain fields a ModelForm, these fields are not fixed. (I have only tutor that is autopopulated by the view)

Model:

class Session(models.Model):
  tutor = models.ForeignKey(User)
  start_time = models.DateTimeField()
  end_time = models.DateTimeField()
  status = models.CharField(max_length=1)

Form:

class SessionForm(forms.ModelForm):
  class Meta:
    model = Session
    exclude = ['tutor']

For a given session sometimes I need to update only end_time , sometimes only start_time & end_time .

How can I do that in a view ?


Edit

I have given examples but it's not limited to these examples, the fields I need to update are not predefined, I need to be able to update any field(s)

I've had to do something similar before, and while it isn't exactly pretty, it is quite effective. It involves dynamically creating a type at runtime, and using that type. For some documentation, you can see DynamicModels for django.

Here we go.. your requirements.

  • You want to be able to update a model using a form
  • You want to selectively specify which fields are to be updated at run-time

So, some code:

def create_form(model, field_names):
    # the inner class is the only useful bit of your ModelForm
    class Meta:
        pass
    setattr(Meta, 'model', model)
    setattr(Meta, 'include', field_names)
    attrs = {'Meta': Meta}

    name = 'DynamicForm'
    baseclasses = (forms.ModelForm,)
    form = type('DynamicForm', baseclasses, attrs)
    return form

def my_awesome_view(request):
    fields = ['start_time', 'end_time']
    form = create_form(Session, fields)
    # work with your form!

Put your form fields as nullable and with "clean" methods you can add logic to fields ex:

class SessionForm(forms.ModelForm):
    def clean_end_date(self):
        cd = self.cleaned_data
        if (cd["start_date"] and cd["end_date"]) and cd["end_date"] < cd["start_date"]:
            raise forms.ValidationError("WTF?!")
        if not (cd["start_date"] or cd["end_date"]):
            raise forms.ValidationError("need one date")
        return cd['end_date']

If you want to change value, use a different value to the return statement.

This is what you may need for validation.

If you want, you may copy GET dictionary in your view and update values before instanciate your form

def my_view(request):
    r_data = request.GET.copy()
    r_data.merge(request.POST)

    data = dict([(key, my_function(key, value)) for key, value in r_data.iteritems() if key in MyForm.fields])
    form = MyForm(data=data)
    [...]

hopes it help.

You can invoke form's constructor like this:

class SessionForm(forms.ModelForm):
    class Meta:
        model = Session
        exclude = ['tutor', 'start_time']

    def __init__(self, your_extra_args, *args, **kwargs):
        super(SessionForm, self).__init__(*args, **kwargs)
        if need_end_time_start_time(your_extra_args):
            self.fields['start_time'] = forms.DateTimeField()     

To use this class, you have to pass an argument "your_extra_args" to your form:

session_form = SessionForm('foo')

Your question suggests that you have a requirement that uses same model but different fields are hidden in your ModelForm, based on your needs.

If I understand your question, then you need to Create two different ModelForm based class, both these classes have same model value inside the Meta class. Exclude and include the fields you need. So now you have different ModelForm classes which you can call inside your view logic.

With this you will have two different forms that depend on same model, with different default fields.

With clever url and view design you will be able to use the above idea to serve multiple forms from same model.

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