Need to remove/change primary_key on model

I have an issue with my original design and need some help.

I have an Employee object that has a OneToOneField relationship to the User object.

My original plan for how a staff member would create a new Employee was:

  1. Login to Django Admin area
  2. Create new User
  3. Go back into main website application
  4. Create new Employee with form, choosing the newly created User in the username field

However, this has proved to be cumbersome and I need to improve the design.
The new idea for a staff member creating an Employee is:

  1. Go into main website application
  2. Create new Employee with form
  3. Use signals.py to automatically create a new User and assign it to the username field on the Employee object

My issue is that when I try to add a new Employee through the form, I cannot even get to the signals.py code to create the new User. This is because the Employee object throws an error that the username field is required, obviously due to the primary_key=True

models.py

class Employee():
    username = models.OneToOneField(User, on_delete=models.RESTRICT, primary_key=True)
    first_name = models.CharField(max_length=50, blank=True, default='')
    last_name = models.CharField(max_length=50, blank=True, default='')

What is my best option at this point?

I can try something like removing the primary_key=True from the Employee object, and allowing it to be blank:

username = models.OneToOneField(User, on_delete=models.RESTRICT, blank=True, null=True)

But that gives the following error in the terminal/console:

It is impossible to add a non-nullable field ‘id’ to employee without specifying a default. This is because the database needs something to populate existing rows.
Please select a fix:
1-Provide a one-off default now (will be set on all existing rows with a null value for this column)
2-Quit and manually define a default value in models.py.

It makes sense that this error would occur, because if I remove the primary key on that table, it needs to know what makes the rows unique.

To further compound the issue, this application is already deployed in production, so I can’t just wipe out the database and start fresh.

How can I remove the requirement for that username field, or circumvent it to be able to automatically create the new User upon creation of the new Employee record?

Don’t use signals here. Add the code in the view that creates the new Employee to create the new User and populate the username field with your newly created User before saving the new Employee.

Okay, thanks, Ken!

Here’s my CBV for adding an Employee:

class EmployeeAdd(UserAccessMixin, SuccessMessageMixin, generic.CreateView):
    permission_required = 'employees.add_employee'

    model = Employee
    form_class = EmployeeForm
    template_name = 'generic_add.html'
    success_message = model._meta.verbose_name + " added successfully."

    def get_context_data(self, **kwargs):
        # Call the base implementation first to get a context
        context = super(EmployeeAdd, self).get_context_data(**kwargs)
        context['obj_name'] = self.model._meta.verbose_name
        return context

    def get_success_url(request):
        return reverse('employees:index')

However, I’m not sure where to add the code to create the User.

I tried in form_valid, but that still didn’t fire before the required fields check.
Would I do it in get_context_data? That doesn’t seem right.

If you are always creating a new Employee and creating the related new User here, then do not include the username field in the form. (The person using that form isn’t selecting a user, they’re creating a new one, so there’s nothing to select and therefor nothing that needs to be verified at that point.)

Then, you would be able to add that code into the form_valid method.

Thank you, again.

I realized what I was missing was to exclude the username field in my forms.py file. :man_facepalming:

forms.py

class EmployeeForm(ModelForm):
    class Meta:
        model = Employee
        exclude = ['username']

That’s why it wouldn’t allow me to get to the form_valid code when saving, even though I no longer had the username field on the form.