UNIQUE constraint failed: members_activemember.member_id onetoonefield Django

Я получаю это сообщение об ошибке. Я не уверен, почему? В нем говорится, что оно появляется после сохранения моей формы из функции обновления в моем файле views.py.

Ошибка интеграции:

IntegrityError at /update/8
UNIQUE constraint failed: members_activemember.member_id
Request Method: POST
Request URL:    https://topxgym.pythonanywhere.com/update/8
Django Version: 4.1
Exception Type: IntegrityError
Exception Value:    
UNIQUE constraint failed: members_activemember.member_id
Exception Location: /home/topxgym/.virtualenvs/env/lib/python3.9/site-packages/django/db/backends/sqlite3/base.py, line 357, in execute
Raised during:  members.views.update
Python Executable:  /usr/local/bin/uwsgi
Python Version: 3.9.5
Python Path:    
['/var/www',
 '.',
 '',
 '/var/www',
 '/usr/local/lib/python39.zip',
 '/usr/local/lib/python3.9',
 '/usr/local/lib/python3.9/lib-dynload',
 '/home/topxgym/.virtualenvs/env/lib/python3.9/site-packages',
 '/home/topxgym/.virtualenvs/env/topxgym']
Server time:    Tue, 23 Aug 2022 00:55:22 +0300

TrackBack

Это две мои модели Member и ActiveMember. Здесь используется поле OneToOneField, потому что у члена может быть только одно членство, дата активации, дата окончания и статус. Но я подозреваю, что ошибка Unique возникает из-за поля OneToOneField, потому что обновление смешивает идентификаторы, возможно? И члену назначается несколько членств? Это мое лучшее предположение. models.py

class Member(models.Model):
    full_name = models.CharField(max_length=125, unique=True)    
    email = models.EmailField(max_length=125, blank=True, null=True)
    phone = models.CharField(max_length=20)
    detail = models.CharField(max_length=256, blank=True, null=True)
    image = models.ImageField(max_length= 256, upload_to='media', null=True, blank=True)
    date_created = models.DateTimeField(default=django.utils.timezone.now)

    class Meta:
        verbose_name_plural = "All Members"

    def __str__(self):
        return str(f"{self.full_name}")
    
    def save(self, *args, **kwargs):
        # delete old file when replacing by updating the file
        try:
            this = Member.objects.get(id=self.id)
            if this.image != self.image:
                this.image.delete(save=False)
        except: pass # when new photo then we do nothing, normal case          
        super(Member, self).save(*args, **kwargs)


class ActiveMember(models.Model):
    member = models.OneToOneField(Member, on_delete=models.CASCADE, related_name='is_member')
    start_date = models.DateField(default=django.utils.timezone.now)
    end_date = models.DateField(default=django.utils.timezone.now)
    status = models.CharField(max_length=2, choices=(('1','Active'), ('2','Inactive')), default = '1', blank=True, null=True)
    
    def __str__(self): 
        return str(f"{self.member}")

Согласно сообщению об ошибке. Проблема возникает после вызова form.save(). Самое странное, что код отлично работает, пока вы не добавляете новых членов и не обновляете/удаляете, затем через некоторое время он ломается. Я подозреваю, что функция обновления неправильно передает ID. Возможно, я получаю ActiveMember ID, а не Member ID? Или это не имеет значения. Является ли Django достаточно умным, чтобы связать эти две функции? views.py update method

@login_required(login_url='authenticate/admin_login')
def update(request, id):
    member = ActiveMember.objects.get(pk=id)
    if request.method == 'POST':
        form = ActiveMemberForm(request.POST, instance=member)
        if form.is_valid():
            # save form data to variables
            member = form.cleaned_data['member']
            start_date = form.cleaned_data['start_date']
            status = form.cleaned_data['status']

            # send WhatsApp message to inform user his membership has been activated.
            if  member.phone is not None and status == '1':
                msg = WhatsApp(member.full_name, member.phone, start_date)
                msg.send_message('customer_active')

            # save form to database.   
            form.save()
            # redirect to the success message
            return render(request, 'members/update.html', {
                'form': form,
                'success': True,
            })
    else:
        form = ActiveMemberForm(instance=member)
    return render(request, 'members/update.html', {
        'form': form, 
        'member': member,
        })

по запросу файл forms.py

class MemberForm(ModelForm):
    def __init__(self, *args, **kwargs):
        super(MemberForm, self).__init__(*args, **kwargs)
        self.fields['image'].required = False
        self.fields['email'].required = False
        self.fields['date_created'].disabled = True

    class Meta:
        model = Member
        fields = (
            'full_name',
            'email',
            'phone',
            'image',
            'detail',
            'date_created',
            )


class ActiveMemberForm(ModelForm):

    def __init__(self, *args, **kwargs):
        super(ActiveMemberForm, self).__init__(*args, **kwargs)
        pass

    class Meta:
        model = ActiveMember
        fields = (
            'member',
            'start_date',
            'end_date',
            'status',
        )
        widgets = {
            'start_date': widgets.DateInput(attrs={'type': 'date'}),
            'end_date': widgets.DateInput(attrs={'type': 'date'}),
        }


    def clean(self):
        start_date = self.cleaned_data['start_date']
        end_date = self.cleaned_data['end_date']
        if start_date < timezone.now().date():
            raise ValidationError('Please enter a valid start date!')
        if end_date < timezone.now().date() or end_date < start_date:
            raise ValidationError('Please enter a valid end date!')
        return self.cleaned_data
    

В начале функции обновления представления вы присваиваете локальной member переменной ActiveMember экземпляр:

member = ActiveMember.objects.get(pk=id)

Затем вы передаете member в конструктор формы. Позже в коде вы присваиваете ActiveMember.member переменной member, которая сама является экземпляром Member:

member = form.cleaned_data['member']

Просто попытка в темноте, но там могут быть подкручены идентификаторы. Тем не менее, вы всегда должны санировать SomeModel.objects.get(pk) с блоком try except, как показано в документации . Это защитит вас от ошибок.

Вернуться на верх