sandermander
sandermander

Reputation: 73

ValueError: save() prohibited to prevent data loss due to unsaved related object form - Create model with form obj as fk

I try to do the following

class CaseCreateView(BSModalCreateView):
template_name = 'orders/create_case_modal.html'
form_class = NewCaseModal
success_message = 'Success: Case was created.'
success_url = reverse_lazy('scan_to_cart')

def form_valid(self, form):
    case_num = random_string_generator()
    obj = form.save(commit=False)
    obj.user = self.request.user
    obj.case_number = case_num
    mess_obj = MessageEntry.objects.create(user=obj.user, message_fk=obj, mess_obj=obj.initial_memo)
    return super(CaseCreateView, self).form_valid(form)

Which gives me the following error.

    ValueError: save() prohibited to prevent data loss due to unsaved related object 'message_fk'.

This is a bootstrap_modal_forms window where I save a form. I want to create an object with a FK field (message_fk) hooked to the form obj.

I can't save() the obj before assign it to the message_fk, because in that case the form saves twice. (don't know why)

I never worked with class based views before and can't find a way to do this properly.

Upvotes: 1

Views: 468

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476967

You first need to save the obj, beore you can link a MessageEntry to it. If your oj is not saved, it has no pk assigned to id, and hence linking another object to it, is not possible.

I can't save() the obj before assign it to the message_fk, because in that case the form saves twice.

The reason that happens is because the super(CaseCreateView, self).form_valid(form) will save the form. But we do not need to call super(CaseCreateView, self).form_valid(form) here, we can do what the FormMixin.form_valid method [Django-doc] does: redirect to get_success_url() result [Django-doc]:

from django.views.generic.edit import FormMixin

class CaseCreateView(BSModalCreateView):
    template_name = 'orders/create_case_modal.html'
    form_class = NewCaseModal
    success_message = 'Success: Case was created.'
    success_url = reverse_lazy('scan_to_cart')

    def form_valid(self, form):
        case_num = random_string_generator()
        self.object = obj = form.save(commit=False)
        obj.user = self.request.user
        obj.case_number = case_num
        obj.save()
        mess_obj = MessageEntry.objects.create(user=obj.user, message_fk=obj, mess_obj=obj.initial_memo)
        return FormMixin.form_valid(self, form)

Upvotes: 2

Related Questions