IntegrityError, NOT NULL constraint failed: contest_contestant.contest_id при обновлении записи

У меня есть две модели Contest и Contestant, пользователи могут начать конкурс, а другие пользователи могут зарегистрироваться в качестве участника в любом конкурсе...

models.py
class Contest(models.Model):
    contest_title = models.CharField(max_length=30)
    contest_post = models.CharField(max_length=100)
    created_on = models.DateTimeField(auto_now_add=True)
    updated_on = models.DateTimeField(auto_now=True)

    

class Contestant(models.Model):
    contest = models.ForeignKey(Contest, on_delete=models.CASCADE, related_name='participating_contest')
    contestant_name = models.CharField(max_length=10, blank=True, null=True)
    votes = models.IntegerField(default=0)
    created_on = models.DateTimeField(auto_now_add=True)
    updated_on = models.DateTimeField(auto_now=True)

Поклонники любого конкурсанта могут отдать неограниченное количество голосов за своего любимого конкурсанта, каждый раз, когда поклонник голосует, я хочу увеличивать Contestant.votes с количеством отданных голосов, я пытался использовать FBV, но это слишком сложно для моего уровня django, теперь я использую CBV, но я получаю IntegrityError, говоря IntegrityError at /contests/contestant/2/ NOT NULL constraint failed: contest_contestant.contest_id

Эта ошибка не имеет смысла, потому что contest_id уже был присвоен участнику при создании, и я даже могу подтвердить это из базы данных

view.py

class ContestantCreateView(CreateView):
    model = Contestant
    fields = ['contestant_name']

    def form_valid(self, form):
        form.instance.contest_id = self.kwargs.get('pk')
        return super().form_valid(form)

логика голосования находится в side contestant detailView, который запускается POST-запросом из формы

views.py

class ContestantDetail(DetailView, FormMixin):
    model = Contestant
    context_object_name = 'contestants'
    template_name = 'contest/contestant_detail.html'
    form_class = VoteForm

    def get_success_url(self):
        return reverse('contest:contestant-detail', kwargs={'pk': self.object.pk})

    def get_context_data(self, *args, **kwargs):
        context = super(ContestantDetail, self).get_context_data(*args, **kwargs)
        context['vote_contestant'] = Contestant.objects.get(pk=self.kwargs.get('pk'))
        return context

    def post(self, request, *args, **kwargs):
        form = self.get_form()
        self.object = self.get_object()

        if form.is_valid():
            return self.form_valid(form)
        else:
            return self.form_invalid(form)

    def form_valid(self, form, *args, **kwargs):
        contestant = Contestant.objects.get(pk=self.kwargs['pk'])
        vote_count = form.cleaned_data['votes']
        contestant.votes += vote_count
        form.save()
        messages.success(self.request, f'You have successfully casted your vote.')
        return super().form_valid(form)



forms.py

class VoteForm(forms.ModelForm):
    class Meta:
        model = Contestant
        fields = ['votes']

html form template

<form action="" method="post">
                {% csrf_token %}
                {% include 'contest/bs4_form.html' with form=form %}

                <button type="submit">Vote Now</button>
            </form>

ниже представлен мой urls.py

    path('contestant/<int:pk>/new/', views.ContestantCreateView.as_view(), name='new-contestant'),
    path('contestant/<int:pk>/edit/', views.ContestantUpdateView.as_view(), name='edit-contestant'),
    path('contestant/<int:pk>/', views.ContestantDetail.as_view(), name='contestant-detail'),

Используя form.save(), вы сохраняете форму, поскольку вы не передали форме экземпляр, это означает, что form.save() попытается создать новую запись. Таким образом, вам следует опустить form.save() из метода form_valid(…):

from django.db.models import F

def form_valid(self, form, *args, **kwargs):
    contestant = Contestant.objects.get(pk=self.kwargs['pk'])
    vote_count = form.cleaned_data['votes']
    contestant.votes = F('votes') + vote_count
    contestant.save()
    # no form.save()
    messages.success(self.request, f'You have successfully casted your vote.')
    return super().form_valid(form)
Вернуться на верх