0

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})
Vémundr
  • 395
  • 1
  • 7
  • 17
  • 1
    what you are looking for is `commit=false` in the form validation in views.py. have a look here https://stackoverflow.com/questions/12848605/django-modelform-what-is-savecommit-false-used-for. Post your views.py maybe I can show you what I mean. – hansTheFranz Jul 12 '17 at 14:17
  • @hansTheFranz thanks. I actually got it working the way I wanted, code above. If there are any enhancements possible, please let me know. – Vémundr Jul 13 '17 at 14:34

1 Answers1

0

In this case is better don't use the forms'save method, try this

if request.method == 'POST':
    form = ItemForm(request.POST)
    # check whether it's valid:
    if form.is_valid():
        post = request.POST
        set_name_eng= post['name_eng']
        set_name_de= post['name_de']
        set_on_sale = post['on_sale']

        #now here we create the anothers objects
        a = Localization(de=set_name_de, eng=set_name_eng)
        a.save()

        b = Item(name=a, on_sale=sale)
        b.save()

I didn't understand the last part, but I think you need to organize better your models and forms, Let me know and I'll try to help you

Mauricio Cortazar
  • 4,049
  • 2
  • 17
  • 27
  • Thanks for the comment. Why is this method better then doing that with the form's save()? – Vémundr Jul 13 '17 at 11:37
  • in this case, you need to modify data from another models and not just the modelform, but for example in larges modelform is very useful save(), also you can mix both methods with .save(commit=False) – Mauricio Cortazar Jul 13 '17 at 18:38