Katharina
Katharina

Reputation: 59

Django: CreateView object with dynamic form fields not saved

I set up a working CreateView. However, when I made one of my modelform fields ('erlaubte_pruefer') dynamic, the object is not saved anymore.

I've tried many solutions suggested in other posts, e.g. - get user variable in get_form_kwargs(), - limit the field choices in get_form() instead of forms.py - don't use CreateView

but I just can't make it work. I don't get an error or exception, the form is only rendered again.

I would highly appreciate your input. Thank you!

forms.py

class Checklisten_Reinigung_Form(forms.ModelForm):

    class Meta:
        model = Checklisten_Reinigung
        fields = ['okay',
              'raum_verbindung',
              'ausfuehrer',
              'erlaubte_pruefer'
              ]

    # user should only choose from those objects that were created by himself
    def __init__(self, user, *args, **kwargs):
        super(Checklisten_Reinigung_Form, self).__init__(*args, **kwargs)
        self.fields['erlaubte_pruefer'].queryset = 
                   Pruefer.objects.filter(firmenzugehoerigkeit=user)

views.py

class Checklisten_Reinigung_Create_View(LoginRequiredMixin, CreateView):
    template_name = 'checklisten/checklisten_form.html'

    def get_context_data(self, **kwargs):
        context = super(Checklisten_Reinigung_Create_View, 
                        self).get_context_data(**kwargs)
        context['mymodel'] = Checklisten_Reinigung()
        return context

    # if I leave out get_form() the object is successfully saved
    # but the user's choice is not limited

    def get_form(self, form_class=None):
        form = Checklisten_Reinigung_Form(user=self.request.user)
        return form

    def form_valid(self, form):
        self.object = form.save(commit=False)

        try:
            self.object.pruefende_firma = self.request.user
            self.object.bezeichnung = self.object.bezeichnung
            self.object.ausfuehrer = form.cleaned_data['ausfuehrer']
            self.object.erlaubte_pruefer = 
                  form.cleaned_data['erlaubte_pruefer']
            self.object.okay = form.cleaned_data['okay']
            self.object.raum_verbindung= form.cleaned_data['raum_verbindung']
            self.object.save()


            return HttpResponseRedirect(self.get_success_url())

        except:
            messages.error(self.request, 'Es ist ein Fehler aufgetreten.')
            return self.render_to_response(self.get_context_data(form=form(user=self.request.user)))

    def get_success_url(self):
        messages.success(self.request, 'Checkliste erfolgreich gespeichert.')
        return reverse('checkliste-startseite')

EDIT:

Thank you, @Daniel Rosemann for your reply. I included your code into mine but get this error:

Traceback:

File "C:\Users\Mars\Anaconda3\lib\site-packages\django\core\handlers\exception.py" in inner
  34.             response = get_response(request)

File "C:\Users\Mars\Anaconda3\lib\site-packages\django\core\handlers\base.py" in _get_response
  126.                 response = self.process_exception_by_middleware(e, request)

File "C:\Users\Mars\Anaconda3\lib\site-packages\django\core\handlers\base.py" in _get_response
  124.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "C:\Users\Mars\Anaconda3\lib\site-packages\django\views\generic\base.py" in view
  68.             return self.dispatch(request, *args, **kwargs)

File "C:\Users\Mars\Anaconda3\lib\site-packages\django\contrib\auth\mixins.py" in dispatch
  52.         return super().dispatch(request, *args, **kwargs)

File "C:\Users\Mars\Anaconda3\lib\site-packages\django\views\generic\base.py" in dispatch
  88.         return handler(request, *args, **kwargs)

File "C:\Users\Mars\Anaconda3\lib\site-packages\django\views\generic\edit.py" in get
  168.         return super().get(request, *args, **kwargs)

File "C:\Users\Mars\Anaconda3\lib\site-packages\django\views\generic\edit.py" in get
  133.         return self.render_to_response(self.get_context_data())

File "C:\Users\Mars\Desktop\morecooking_now\morecooking_now\checklisten\views.py" in get_context_data
  372.         context = super(Checklisten_Reinigung_Create_View, self).get_context_data(**kwargs)

File "C:\Users\Mars\Anaconda3\lib\site-packages\django\views\generic\edit.py" in get_context_data
  66.             kwargs['form'] = self.get_form()

File "C:\Users\Mars\Anaconda3\lib\site-packages\django\views\generic\edit.py" in get_form
  32.             form_class = self.get_form_class()

File "C:\Users\Mars\Anaconda3\lib\site-packages\django\views\generic\edit.py" in get_form_class
  93.                 model = self.get_queryset().model

File "C:\Users\Mars\Anaconda3\lib\site-packages\django\views\generic\detail.py" in get_queryset
  73.                         'cls': self.__class__.__name__

Exception Type: ImproperlyConfigured at /checklisten/reinigung/neu/
Exception Value: Checklisten_Reinigung_Create_View is missing a QuerySet. Define Checklisten_Reinigung_Create_View.model, Checklisten_Reinigung_Create_View.queryset, or override Checklisten_Reinigung_Create_View.get_queryset().

Thank you again!

Upvotes: 0

Views: 2020

Answers (1)

Daniel Roseman
Daniel Roseman

Reputation: 599866

You have changed the signature of the form so that the first positional argument is now user, not data. But you've defined get_form to only ever pass user, and never data; so the form never gets any data and therefore can never be valid.

Make user a kwarg instead:

def __init__(self, *args, **kwargs):   # no user here
    user = kwargs.pop('user', None)
    super(Checklisten_Reinigung_Form, self).__init__(*args, **kwargs)
    self.fields['erlaubte_pruefer'].queryset = 
               Pruefer.objects.filter(firmenzugehoerigkeit=user)

and in the view, remove your definition of get_form and instead define get_form_kwargs to pass the user:

def get_form_kwargs(self):
    kwargs = super().get_form_kwargs()
    kwargs['user'] = self.request.user
    return kwargs

Note also, you're doing too much work in form_valid; the call to form.save will already have set the fields in the form, such as "erlaubte_pruefer" and "okay", so you don't need to set them manually.

(And finally, it is absolutely no use at all to blindly catch all errors and just return a vague "an error has occurred" message. That hides the logging that will tell you what happened, and doesn't give the user any useful information. Only catch the errors you know you will deal with; remove that try/except and let Django show the default error page.)

Upvotes: 1

Related Questions