In Django, when you want to code a form+view+HTML to create a new record for a model, online guides all steer you to the direct approach. That is, use a ModelForm
and a CreateView
to expose all the fields and let the user specify each value directly.
But what if -- for purposes of usability and data integrity -- you need an abstraction layer between the displayed form elements, and the record which is ultimately inserted in the model? Say for example,
As a novice in Django I'm having a very hard time working out even a basic approach for this sort of use case. I've spent days fiddling with JavaScript event handling and storing calculated values in read-only form fields, but it seems like there should be a much cleaner approach.
(Note: the above bullet points are just generalized examples of the sort of functionality I need, not the actual application requirements I'm trying to code.)
Maybe this is not what you were hoping for, but my answer is: The abstraction layer "between user input and the database" are the form and the view.
If the usage of default ModelForm
and CreateView
is too basic for you, then you can overwrite their behaviour to your liking via inheritance.
# models.py
class MyModel(models.Model):
field1 =
field2 =
field3 =
field4 =
field5 =
class OtherModelOne(models.Model):
field7 =
field9 =
class OtherModelTwo(models.Model):
field1 =
field2 =
# forms.py
class MyModelForm(forms.ModelForm):
# task 1 & 2
choice_of_other_models1 = forms.ModelChoiceField(queryset=OtherModelOne.objects.all())
choice_of_other_models2 = forms.ModelChoiceField(queryset=OtherModelOne.objects.all())
# task 3
textfield1 = forms.CharField()
textfield2 = forms.CharField()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# don't display field1, field2, field3 and field4 and disable userinput for it
field1 = self.fields.get('field1')
field1.disabled = True
field1.widget = HiddenInput
# [...]
def clean(self):
cleaned_data = super().clean()
# task 2
cleaned_data['field2'] = cleaned_data.get('choice_of_other_models1').field7
cleaned_data['field3'] = cleaned_data.get('choice_of_other_models2').field9
# task 3
cleaned_data['field4'] = len(cleaned_data.get('textfield1')) + len(cleaned_data.get('textfield2'))
return cleaned_data
I know it might be a lot to take, so lets tackle this in words:
You have your model and two other models. You want to create MyModel
. For its creation, as defined in your tasks, you actually do not need to visualize field1 to field4 because they are dependend on other factors and get not assigned directly.
Therefore you disable and hide them for userinput. You could do that already in the model with hidden=True
but above you'll find how to do it in the forms __init__()
method.
It is a ModelForm, so field5 and field6 can assigned directly by the user as you know already to do. But since all the other fields are related on parameters external to MyModel
you want to add more fields manually.
These are here called choice_of_other_models1
and textfield1
and so on.
Inside of the clean
method you can assign and redefine and modify all of your data inside of the dictionary cleaned_data
.
Remember: If you need this functionality in multiple views it is better to put all that logic inside of the form. If on the other hand you need a more general creation of MyModel
and this logic is only needed for this specific view, then of course you should put this logic to your view.
This decision has to be done on reusuability.
I understand that my code above is not a copy paste and it works solution, but I hope I could make my point clear: The abstraction layer you are talking about are the forms and the views. By inheritance it is reusuable and configurable to your needs.
Try to tackle one of your examples. I'm happy to help further in case you then hit a roadblock with specific (potentially buggy) code.