Django form data not persisting in database after submission

I’m having trouble with a Django application where form data gets submitted but never saves to the database. The setup works like this - users first select some criteria which generates a list of items, then they can modify values for those items and submit. I can see the POST data coming through correctly, but nothing gets stored in MySQL.

Here’s my model for tracking employee payroll data:

class PayrollRecord(models.Model):
    period_month = models.IntegerField()
    period_year = models.IntegerField()
    company = models.ForeignKey(Company, on_delete=models.DO_NOTHING)
    worker = models.ForeignKey(Worker, on_delete=models.CASCADE, null=True, default=None)
    salary = models.IntegerField()
    total_pay = models.IntegerField()
    working_days = models.IntegerField()
    attendance_days = models.IntegerField()
    calc_a = models.DecimalField(max_digits=10, decimal_places=2)
    calc_b = models.DecimalField(max_digits=10, decimal_places=2)
    calc_difference = models.DecimalField(max_digits=10, decimal_places=2)
    calc_final = models.DecimalField(max_digits=10, decimal_places=2)

    def save(self, *args, **kwargs):
        self.pk = f"{self.period_month}.{self.period_year}"
        super().save(*args, **kwargs)

My view handles the form processing:

class CreatePayrollView(FormView):
    template_name = 'create_payroll.html'
    form_class = PayrollEntryForm
    model = PayrollRecord

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        prev_month, current_year, days_count = get_month_info()
        context['prev_month'] = prev_month
        context['current_year'] = current_year
        context['days_count'] = days_count
        context['companies'] = Company.objects.all()
        company_id = self.request.POST.get('company')
        if company_id:
            workers = fetch_company_workers(company_id)
            context['workers'] = workers
        return context

    def form_valid(self, form):
        period_month = form.cleaned_data['period_month']
        period_year = form.cleaned_data['period_year']
        company = form.cleaned_data['company']
        workers = form.cleaned_data['worker']
        salary = form.cleaned_data['salary']
        total_pay = form.cleaned_data['total_pay']
        working_days = form.cleaned_data['working_days']
        attendance_days = form.cleaned_data['attendance_days']

        for worker in workers:
            PayrollRecord.objects.create(
                period_month=period_month,
                period_year=period_year,
                company=company,
                worker=worker,
                salary=salary,
                total_pay=total_pay,
                working_days=working_days,
                attendance_days=attendance_days
            )
        return redirect('worker_list')

The form definition:

class PayrollEntryForm(forms.ModelForm):
    period_month = forms.IntegerField()
    period_year = forms.IntegerField()
    company = forms.ModelChoiceField(queryset=Company.objects.all(), required=True)
    worker = forms.ModelMultipleChoiceField(queryset=Worker.objects.filter(active=True), required=True, widget=forms.CheckboxSelectMultiple)
    salary = forms.IntegerField()
    total_pay = forms.IntegerField()
    working_days = forms.IntegerField()
    attendance_days = forms.IntegerField()

    class Meta:
        model = PayrollRecord
        fields = ('period_month', 'period_year', 'company', 'worker', 'salary', 'total_pay', 'working_days', 'attendance_days')

No errors show up in Django logs or MySQL logs. What could be preventing the data from saving?

Had the same problem - it was a transaction rollback issue. Your form_valid method looks fine, but you’re probably getting a silent exception during save. The custom save method messing with the primary key is definitely broken, but here’s the real issue: you’re not handling calc_a, calc_b, calc_difference, and calc_final anywhere in your form or view. These fields have no defaults and aren’t nullable, so Django can’t create the records. Either add these fields to your form or set default values in the model. Also wrap your PayrollRecord.objects.create calls in try-except blocks to catch exceptions that are getting swallowed. I wasted hours on this exact thing before I realized required fields were missing from my form.

Your custom save method is the problem. You’re setting self.pk = f"{self.period_month}.{self.period_year}" which creates a string primary key, but Django expects an integer for the default AutoField. When you save multiple records with the same period_month and period_year, you’re creating duplicate primary keys - that’s why it’s failing silently or throwing integrity errors. I ran into this exact issue when I was messing with save methods without understanding how primary keys work. Just remove that custom save method and let Django handle the primary key automatically. If you need unique period tracking, use a unique_together constraint in your model’s Meta class instead of hijacking the primary key. Also run python manage.py showmigrations to make sure your migrations actually applied and your table structure matches your model.

Your model has non-nullable fields without defaults that aren’t getting populated. When PayrollRecord.objects.create() runs, Django can’t insert records because calc_a, calc_b, calc_difference, and calc_final are required DecimalFields with no values. The broken primary key assignment in your save method makes it worse by creating constraint violations. I’ve seen this exact thing when migrating legacy payroll systems - you need proper exception handling around the create calls. Wrap your PayrollRecord.objects.create() in a try-except block and log the actual exception. You’ll likely see IntegrityError or ValidationError messages showing exactly which fields are broken. Fix it by either calculating those decimal values in your view before saving, or add sensible defaults to those calculation fields in your model.

check your django settings for database transaction handling. you probably have autocommit disabled or some other weird config issue. that pk override in save() is completely broken too - you can’t use a string as primary key when django expects an integer. add some print statements in form_valid to see if it’s even hitting those create calls.

Your model has required fields (calc_a, calc_b, calc_difference, calc_final) that aren’t getting set when you create records. Django can’t save because these decimal fields don’t have default values and you’re not providing any.

That custom save method is broken too. You can’t set a string as the primary key when Django expects an integer.

This whole setup needs automation though. I’ve handled similar payroll workflows and they turn into a mess with all the form handling and validation.

Skip wrestling with Django forms and database headaches. Set up a workflow in Latenode to handle your entire payroll pipeline. It’ll pull employee data, calculate those fields automatically, validate everything, and save to your database without the custom Django complexity.

Latenode handles multi-step processes where users select criteria, generate lists, modify values, and submit - with proper error handling and data persistence built in. You get visual workflow monitoring so you can see exactly where things break instead of debugging silent failures.

I moved our payroll processing to automated workflows and eliminated these data persistence problems completely. The visual interface makes tracking your data way easier.

The Problem:

Your Django application’s form submissions are not saving to the database, despite POST data appearing correctly. The issue is silent; no errors are logged in Django or MySQL. This suggests a problem with data validation or handling within your form_valid method and potentially related to your custom save method in the PayrollRecord model.

:thinking: Understanding the “Why” (The Root Cause):

The core issue is a combination of factors:

  1. Missing Required Fields: Your PayrollRecord model includes several DecimalFields (calc_a, calc_b, calc_difference, calc_final) that are non-nullable and lack default values. objects.create() fails silently because these fields are not provided in your form_valid method. Django’s internal validation fails, but the error is not propagated to the user interface because FormView does not handle these errors properly.

  2. Broken Primary Key Override: Your custom save method in the PayrollRecord model attempts to set the primary key (pk) to a string (f"{self.period_month}.{self.period_year}"). Django’s default AutoField expects an integer primary key. This leads to either silent failures (if database constraints are not enforced properly) or explicit IntegrityError exceptions. Multiple records with the same period_month and period_year will lead to duplicate key attempts, resulting in failure.

  3. Inefficient Use of objects.create(): Manually creating PayrollRecord instances inside a loop using objects.create() is less efficient and less error-tolerant than leveraging the form.save() method. The latter handles validation and exception handling more effectively.

:gear: Step-by-Step Guide:

  1. Fix the Missing Fields and Use form.save(): Modify your form_valid method to utilize the form.save() method and address the missing fields. This addresses the missing field issue, improves efficiency, and streamlines error handling. Since you’re likely using calculated fields, calculate calc_a, calc_b, calc_difference, and calc_final within your form_valid before saving.

    class CreatePayrollView(FormView):
        # ... other code ...
    
        def form_valid(self, form):
            # Calculate your decimal fields here
            form.instance.calc_a = calculate_calc_a(...) # Replace ... with your calculations
            form.instance.calc_b = calculate_calc_b(...)
            form.instance.calc_difference = calculate_calc_difference(...)
            form.instance.calc_final = calculate_calc_final(...)
    
            form.save() #Handles saving and validation correctly
            return redirect('worker_list')
    
  2. Remove the Custom save Method: Eliminate the custom save method from your PayrollRecord model. Django’s default primary key generation is far more robust and reliable. If you need uniqueness based on period_month and period_year, add a UniqueTogether constraint to your model’s Meta class:

    class PayrollRecord(models.Model):
        # ... other fields ...
    
        class Meta:
            unique_together = (('period_month', 'period_year', 'company', 'worker'),) # ensures uniqueness across all relevant fields
    
  3. Run Migrations: After making model changes, run migrations to update your database schema:

    python manage.py makemigrations
    python manage.py migrate
    
  4. Implement Robust Error Handling (Optional but Recommended): Although form.save() handles much of this automatically, you can add explicit error handling around the form.save() call for extra robustness:

    try:
        form.save()
    except IntegrityError as e:
        # Handle database integrity errors (e.g., duplicate key)
        messages.error(self.request, f"Database error: {e}")  # Assuming you have Django messages
        return self.form_invalid(form)
    except Exception as e:
        # Handle other exceptions during save
        messages.error(self.request, f"An error occurred: {e}")
        return self.form_invalid(form)
    

:mag: Common Pitfalls & What to Check Next:

  • Database Connection: Verify your database connection settings in settings.py are correct and that the database server is running.
  • Data Type Mismatches: Double-check that the data types in your form fields precisely match the data types in your model fields.
  • Transaction Management: Although less likely in this case due to the use of form.save(), check that database transactions are properly handled in your Django settings (this may need adjustment if you have custom transaction management).

:speech_balloon: Still running into issues? Share your (sanitized) config files, the exact command you ran, and any other relevant details. The community is here to help!

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.