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)