Django form init method failing

I have an user add form view that asks for information and saves a new user to the db. When I try saving a new user record, I get the following error.

TypeError at /user/add/
__init__() missing 1 required positional argument: 'customer'
Request Method: GET
Request URL:    http://localhost:8000/user/add/
Django Version: 3.0.5
Exception Type: TypeError
Exception Value:    
__init__() missing 1 required positional argument: 'customer'
Exception Location: D:Job DocumentsDDSAuthenticationProjectenvlibsite-packagesdjangoviewsgenericedit.py in get_form, line 33
Python Executable:  D:Job DocumentsDDSAuthenticationProjectenvScriptspython.exe
Python Version: 3.9.0
Python Path:    
['D:\Job Documents\DDS\AuthenticationProject\auth',
 'C:\Users\bratca\AppData\Local\Programs\Python\Python39\python39.zip',
 'C:\Users\bratca\AppData\Local\Programs\Python\Python39\DLLs',
 'C:\Users\bratca\AppData\Local\Programs\Python\Python39\lib',
 'C:\Users\bratca\AppData\Local\Programs\Python\Python39',
 'D:\Job Documents\DDS\AuthenticationProject\env',
 'D:\Job Documents\DDS\AuthenticationProject\env\lib\site-packages']
Server time:    Sun, 15 Aug 2021 08:05:30 +0000

I am using customer object to differentiate users, therefore I want the form to know by whom it’s being filled such that a new created user will have the same customer object as the creator.

This error does not show exactly where is my bug in the code. Instead it shows some error in the actual django code, generic things that I am using.

class AddUserFormView(FormView):
    form_class = TbUserAddForm
    template_name = 'users/add_user_modal.html'
    success_message = "Account has been created! The user is able to log in."

    def post(self, request):
        form = self.form_class(customer=request.user.customer,
                               data=request.POST, files=request.FILES)
        if form.is_valid():
            user_face_img_md5 = Image.open(
                request.FILES['user_face_img_md5'].file)
            user_head_img_md5 = Image.open(
                request.FILES['user_head_img_md5'].file)
            obj = form.save(commit=False)
            response = addUserApi(obj.__dict__,
                                  user_face_img_md5,
                                  user_head_img_md5)
            print(f"Request Response [Add User] --> {response.text}")
            # if response.text['status'] != 45001:
            #     messages.error(f'Something went wrong. {response.text}')
            return redirect('add-user')
        else:
            messages.error(request, form.errors)
            return redirect('add-user')
class TbUserAddForm(CustomUserCreationForm):
    email = forms.EmailField()
    user_head_img_md5 = forms.ImageField(label='Avatar', required=True)
    user_face_img_md5 = forms.ImageField(
        label='Face Recognition Image', required=True)

    def __init__(self, customer, *args, **kwargs):
        super().__init__(*args, **kwargs)
        print(f'USER ADD FORM CUSTOMER -> {customer}')
        self.customer = customer
        if self.customer:
            self.fields['role'].queryset = TbRole.objects.all().filter(
                customer=self.customer)
            self.fields['department'].queryset = TbDepartment.objects.all().filter(
                customer=self.customer)

    class Meta:
        model = TbUser
        fields = [...]

path('add/', views.AddUserFormView.as_view(), name='add-user'),

The user is not saved by django but the addUserApi() sends a request to another server. Therefore sometimes user is being created because addUserApi has worked. But with the same error thrown.

Answer

You should override the .get_form_kwargs(…) method [Django-doc] to pass data to the form you construct in both the GET and POST request:

from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.messages.views import SuccessMessageMixin
from django.urls import reverse_lazy

class AddUserFormView(LoginRequiredMixin, SuccessMessageMixin, FormView):
    form_class = TbUserAddForm
    template_name = 'users/add_user_modal.html'
    success_message = 'Account has been created! The user is able to log in.'
    success_url = reverse_lazy('add-user')

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

    def form_valid(self, form):
        user_face_img_md5 = Image.open(self.request.FILES['user_face_img_md5'].file)
        user_head_img_md5 = Image.open(self.request.FILES['user_head_img_md5'].file)
        obj = form.save(commit=False)
        response = addUserApi(
            obj.__dict__,
            user_face_img_md5,
            user_head_img_md5
        )
        print(f'Request Response [Add User] --> {response.text}')
        return super().form_valid(form)

Usually one does not override the post and/or get method of a class-based view, since that contains most of the boilerplate code. Likely it is sufficient here to override the .form_valid(…) method [Django-doc] to specify what should happen in case the form is valid.


Note: You can limit views to a class-based view to authenticated users with the LoginRequiredMixin mixin [Django-doc].


Note: One can make use of the SuccessMessageMixin mixin to a view to add a success message in case the form was valid.