If we have a modelForm with some fields not directly corresponding to the model, how do we have the form process them in a custom way, while saving the rest of fields as by default?
For example, we have a model for an item that supports multilingual descriptions. The models are:
class Item(models.Model):
name = models.ForeignKey(Localization)
on_sale = models.BooleanField(default=False)
class Localization(models.Model):
de = models.TextField(blank=True, null=True, verbose_name='de')
eng = models.TextField(blank=True, null=True, verbose_name='eng')
The form to add/edit an Item looks like that:
class ItemForm(forms.ModelForm):
id = forms.CharField(widget=forms.HiddenInput(), max_length=128, label='')
name_eng = forms.CharField(widget=forms.TextInput(attrs={'style': 'width:200px;'}), label='eng')
name_de = forms.CharField(widget=forms.TextInput(attrs={'style': 'width:200px;'}), label='de')
on_sale = forms.CharField(widget=forms.CheckboxInput(), label='on_sale', )
class Meta:
model = Item
fields = ('id', 'on_sale',)
Now what saving this form should do, is for a new Item - create Localization object with the two name fields, then create an Item object with on_sale field, linked to Localization object. For an existing Item - edit the corresponding Localization object and then on_sale field of the Item itself.
I did the task with a separate function, that processes the custom fields from the request separately, but having it all done by the form's save() method looks better. Or am I wrong?
PS I'm sorry to be asking an evidently worn question, but I simply fail to do what I want with other examples.
Update:
I actually got it working the way I wanted with the help of the hints from here. Code goes as that, please let me know if it can be optimized.
class NameForm(forms.ModelForm):
# id = forms.CharField(widget=forms.HiddenInput(), max_length=128, label='')
id = forms.CharField(widget=forms.TextInput(attrs={'style': 'width:200px;'}), label='id', required=False)
name_eng = forms.CharField(widget=forms.TextInput(attrs={'style': 'width:200px;'}), label='eng')
name_de = forms.CharField(widget=forms.TextInput(attrs={'style': 'width:200px;'}), label='de')
gender = forms.CharField(widget=forms.CheckboxInput(), label='gender', required=False)
class Meta:
model = Name
fields = ('id', 'gender',)
def save(self):
instance = super(NameForm, self).save(commit=False)
obj_id = self.cleaned_data['id']
if obj_id:
instance_bd = Name.objects.get(pk=obj_id)
loc = instance_bd.name
loc.de = self.cleaned_data['name_de']
loc.eng = self.cleaned_data['name_eng']
loc.save()
instance.id = obj_id
else:
loc = Localization(de=self.cleaned_data['name_de'], eng=self.cleaned_data['name_eng'])
loc.save()
instance.name = loc
instance.save()
return instance
The view is simply
@login_required
def admin_lists(request):
if request.method == 'POST':
form = NameForm(request.POST)
if form.is_valid():
form.save()
forms = {'name': NameForm()}
return render(request, 'admin/lists.html', {'forms': forms})