Django 2.1 NOT NULL constraint when setting ForeignKey

时间:2018-12-30 01:06:47

标签: django django-forms

I am setting FK to an object from a previous form submission, but I can't seem to figure out why I am getting a null constraint error. I have scoured SO and reddit for similar issues, but I think that I am missing something simple here (or I just can't wrap my brain around the FK concept).

error:

Internal Server Error: /district/
Traceback (most recent call last):
  File "/root/areports/venv/lib/python3.6/site-packages/django/db/backends/utils.py", line 85, in _execute
    return self.cursor.execute(sql, params)
  File "/root/areports/venv/lib/python3.6/site-packages/django/db/backends/sqlite3/base.py", line 296, in execute
    return Database.Cursor.execute(self, query, params)
sqlite3.IntegrityError: NOT NULL constraint failed: reports_attendance_entry.event_id

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/root/areports/venv/lib/python3.6/site-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
  File "/root/areports/venv/lib/python3.6/site-packages/django/core/handlers/base.py", line 126, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/root/areports/venv/lib/python3.6/site-packages/django/core/handlers/base.py", line 124, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/root/areports/reports/views.py", line 39, in new_district_view
    new_district.save()
  File "/root/areports/venv/lib/python3.6/site-packages/django/forms/models.py", line 458, in save
    self.instance.save()
  File "/root/areports/venv/lib/python3.6/site-packages/django/db/models/base.py", line 718, in save
    force_update=force_update, update_fields=update_fields)
  File "/root/areports/venv/lib/python3.6/site-packages/django/db/models/base.py", line 748, in save_base
    updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
  File "/root/areports/venv/lib/python3.6/site-packages/django/db/models/base.py", line 831, in _save_table
    result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)
  File "/root/areports/venv/lib/python3.6/site-packages/django/db/models/base.py", line 869, in _do_insert
    using=using, raw=raw)
  File "/root/areports/venv/lib/python3.6/site-packages/django/db/models/manager.py", line 82, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/root/areports/venv/lib/python3.6/site-packages/django/db/models/query.py", line 1136, in _insert
    return query.get_compiler(using=using).execute_sql(return_id)
  File "/root/areports/venv/lib/python3.6/site-packages/django/db/models/sql/compiler.py", line 1289, in execute_sql
    cursor.execute(sql, params)
  File "/root/areports/venv/lib/python3.6/site-packages/django/db/backends/utils.py", line 100, in execute
    return super().execute(sql, params)
  File "/root/areports/venv/lib/python3.6/site-packages/django/db/backends/utils.py", line 68, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
  File "/root/areports/venv/lib/python3.6/site-packages/django/db/backends/utils.py", line 77, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "/root/areports/venv/lib/python3.6/site-packages/django/db/backends/utils.py", line 85, in _execute
    return self.cursor.execute(sql, params)
  File "/root/areports/venv/lib/python3.6/site-packages/django/db/utils.py", line 89, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "/root/areports/venv/lib/python3.6/site-packages/django/db/backends/utils.py", line 85, in _execute
    return self.cursor.execute(sql, params)
  File "/root/areports/venv/lib/python3.6/site-packages/django/db/backends/sqlite3/base.py", line 296, in execute
    return Database.Cursor.execute(self, query, params)
django.db.utils.IntegrityError: NOT NULL constraint failed: reports_attendance_entry.event_id

models.py

class Event_Profile(models.Model):
    content_area        = models.CharField(max_length=120) #max_length = required
    event_date          = models.DateField(default=datetime.date.today) # Need to make this more flexible
    event_location      = models.CharField(max_length=120)
    workshop_number     = models.CharField(max_length=15)
    cost_per_person     = models.DecimalField(max_digits=10,decimal_places=2)
    #def __str__(self):
        #return self.name

# Create an attendance listing for an existing event
class Attendance_Entry(models.Model):
    event               = models.ForeignKey('Event_Profile', on_delete=models.CASCADE)
    district_name       = models.CharField(max_length=120)
    number_registered   = models.PositiveIntegerField()
    number_of_noshows   = models.PositiveIntegerField()
    #def __str__(self):
        #return self.name

forms.py

# Create a new event with the below fields
class New_Event_Form(forms.ModelForm):
    content_area         = forms.ModelChoiceField(
                                    queryset=Content_Area.objects.all(),
                                    required=True)
    event_date           = forms.DateField(
                                    label = 'Event Date',
                                    required=True,
                                    initial=datetime.datetime.today().strftime('%Y-%m-%d'))
    event_location       = forms.ChoiceField(
                                    choices = location_list,
                                    required=True,
                                    widget = forms.RadioSelect())
    workshop_number      = forms.CharField(
                                    required=False)
    cost_per_person      = forms.DecimalField(
                                    required=True,
                                    decimal_places=2,
                                    label='Cost/Person',
                                    initial="0.00")
    class Meta:
        model = Event_Profile
        fields = [
            'content_area',
            'event_date',
            'event_location',
            'workshop_number',
            'cost_per_person'
        ]

# Add a district to a corresponding event and carry over the id from the event listing to event_id
class New_District_Form(forms.ModelForm):
    district_name       = forms.ModelChoiceField(
                                    queryset = District.objects.all(),
                                    required=True)
    number_registered   = forms.IntegerField(
                                    help_text = 'Input the total number registered for this workshop / event.',
                                    required = True,
                                    label = "Number Registered",
                                    validators=[MinValueValidator(0)])
    number_of_noshows   = forms.IntegerField(
                                    help_text = 'Input the total number of no-shows from this district.',
                                    label = "Number No-Shows")
    class Meta:
        model = Attendance_Entry
        fields = [
            'district_name',
            'number_registered',
            'number_of_noshows'
        ]
    # Validation to make certain that the reg value is not greater than no-shows
    def clean(self):
        cleaned_data = super().clean()
        registered = cleaned_data.get("number_registered")
        noshows = cleaned_data.get("number_of_noshows")

        if registered and noshows:
            # Only do something if both fields are valid so far.
            if noshows > registered:
                # If the number of no-shows is greater than registrants, then raise a non_field_error
                raise forms.ValidationError(
                    "The number of registered participants cannot exceed "
                    "the number of no-shows for the event."
                )

views.py

from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse
from .forms import New_Event_Form, New_District_Form
from .models import Event_Profile, Attendance_Entry


def entry_create_view(request):
    form = New_Event_Form(request.POST or None)
    if form.is_valid():
        entry = form.save()
        # if the form data passes validation, save the form and add id to request session
        request.session['event_id'] = entry.id
        # reverse to a new view - add a district view
        return HttpResponseRedirect(reverse('new_district_view'))
    else:
        # used as a signal to indicate an invalid form submission attempt
        print('Invalid')
        context = {
                'form': form,
        }
        return render(request, "entry_create.html", context)


def new_district_view(request):
    # Need to get the event details from the associated event entry
    try:
        profile = Event_Profile.objects.get(id = request.session['event_id'])
    except:
        print("Couldn't create the event")
    try:
        districts = Attendance_Entry.objects.all().filter(event_id = request.session['event_id'])
    except:
        districts = []
    new_district = New_District_Form(request.POST or None)
    if new_district.is_valid():
        new_district.save(commit=False)
        new_district.event = profile
        new_district.save()
        new_district = New_District_Form()
        context = {
            "new_district": new_district,
            "profile": profile,
            "districts": districts
        }
        return render(request, "new_district.html", context)
    else:
        print('Invalid')
        context = {
            "new_district": new_district,
            "profile": profile,
            "districts": districts
        }
        return render(request, "new_district.html", context)

# Junk view assignment until I can replace it with a proper menu
def home_view(request):
    return render(request, "home.html", {})

1 个答案:

答案 0 :(得分:0)

我假设您没有从clean方法返回任何内容。您需要这样做:

def clean(self):
    cleaned_data = super().clean()
    registered = cleaned_data.get("number_registered")
    noshows = cleaned_data.get("number_of_noshows")

    if registered and noshows:
        # Only do something if both fields are valid so far.
        if noshows > registered:
            # If the number of no-shows is greater than registrants, then raise a non_field_error
            raise forms.ValidationError(
                "The number of registered participants cannot exceed "
    return cleaned_data  # <-- Here

更新

实际上问题出在这里

    new_district.save(commit=False)  # <- here
    new_district.event = profile
    new_district.save()

应该是这样的:

    instance = new_district.save(commit=False)
    instance.event = profile
    instance.save()

发生的事情是,表单的save(...)函数返回实例(已保存或未保存)。您需要将其存储在变量中,并在需要时向其添加更多属性,然后再次保存以将其提交到数据库。