简体   繁体   中英

django.db.utils.IntegrityError: FOREIGN KEY constraint failed in django

i keep on getting the above error on creating a new user model when creating users using postman here is my code.

from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
from django.core.validators import RegexValidator
from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver
from rest_framework.authtoken.models import Token
from wallet.models import Transfer, Payment, Transaction, Wallet


class UserManager(BaseUserManager):
    use_in_migrations = True

    def create_user(self, name, phone, password,pin_code,confirm_pin,expected_delivery_month,email=None):
        user = self.model(name=name,phone=phone, email=email,pin_code=pin_code,confirm_pin=confirm_pin,
            expected_delivery_month=expected_delivery_month
        )
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self,name,phone,password,pin_code,expected_delivery_month,email=None, ):
        user = self.create_user(name,phone,password,email,pin_code,expected_delivery_month)

        user.is_admin = True
        user.save(using=self._db)
        return user


class User(AbstractBaseUser):
    name = models.CharField(max_length=120)
    phone_regex = RegexValidator(regex=r'^\+?1?\d{9,15}$',
                                 message="Phone number must be entered in the format +256706626855. Up to 10 digits allowed.")
    phone = models.CharField('Phone', validators=[
                             phone_regex], max_length=17, unique=True, null=False)
    email = models.EmailField(
        verbose_name='email address', max_length=255, unique=True, null=True)
    is_admin = models.BooleanField(default=False)
    #now i need to add the pin coz its what am going to base on for the user creattion as the OTP
    pin_code = models.IntegerField(blank=False,unique=True)#vital here
    confirm_pin = models.IntegerField(blank=False)#this field here is to help in the confirmation of the pin

    expected_delivery_month = models.CharField(blank=False,default='january',max_length=20)
    objects = UserManager()

    USERNAME_FIELD = 'phone'
    REQUIRED_FIELDS = ['name','pin_code']

    
    def __str__(self):
        return self.name


    class Meta:
        unique_together = ("phone","pin_code")#since i need each phone number to have a unique pin code

    @property
    def is_staff(self):
        "Is the user a member of staff?"
        return self.is_admin

    def has_perm(self,perm,obj=None):
        """Does the user have aspecific permission"""
        return True

    def has_module_perms(self,app_label):
        """Does the user have permissions to view thw app `app_label` """
        return True

@receiver(post_save, sender=User)
def create_wallet(sender, instance, **kwargs):
    """
        Create a wallet for every new user
    """
    Token.objects.create(user=instance)
    transfer = Transfer(
        transferred_to=instance, transferred_from=instance,transfer_reason='Initial Transfer',amount=0
    )
    transfer.save()
    transaction = Transaction(transfer_id=transfer,amount=transfer.amount)
    transaction.save()
    wallet = Wallet(balance=0, owner=instance, latest_transaction=transaction)
    wallet.save()


however on carrying out some tests in development using Postman i keep on getting the error below.

 return self.connection.commit()
django.db.utils.IntegrityError: FOREIGN KEY constraint failed
[07/Apr/2021 09:19:04] "POST /user/register/%0A HTTP/1.1" 500 19194
Performing system checks...

i tried making some changes by adding db_constraint=False in the models related to the User model however still in vain.the following are the models with a foreignKey rxnship to the user model.

import uuid
from django.conf import settings
from django.db import models
from django.utils import timezone
from django.utils.translation import gettext_lazy as _

# Create your models here.

class Payment(models.Model):
    Statuses = (
        ('PENDING', 'Pending'),
        ('COMPLETE', 'Complete'),
        ('FAILED', 'Failed'),
    )
    Categories = (
        ('TOP_UP','Top Up'),
        ('WITHDRAW','Withdraw')
    )
    id = models.UUIDField(primary_key=True, default=uuid.uuid4,editable=False)
    status = models.CharField(max_length=2, choices=Statuses,default="PENDING")
    transaction_ref = models.UUIDField(default=uuid.uuid4)
    paid_at = models.DateTimeField(null=True)
    amount = models.IntegerField()
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING,db_constraint=False)
    category = models.CharField(max_length=2,choices=Categories,default="TOP_UP")

    def __str__(self) -> str:
        return str(self.id)

class Transfer(models.Model):
    id = models.UUIDField(primary_key=True,default=uuid.uuid4,editable=False)
    transferred_to = models.ForeignKey(
        settings.AUTH_USER_MODEL,on_delete=models.DO_NOTHING,related_name='transferred_to',db_constraint=False)
    transferred_from = models.ForeignKey(
        settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING,related_name='transferred_from',db_constraint=False)
    amount = models.IntegerField()
    transfer_reason = models.CharField(max_length=200)

    def __str__(self) -> str:
        return str(self.id)

class Transaction(models.Model):
    id=models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    amount = models.IntegerField(editable=False)
    payment_id = models.ForeignKey(Payment,blank=True, on_delete=models.CASCADE, null=True)
    transfer_id = models.ForeignKey(Transfer, blank=True,null=True, on_delete=models.CASCADE)

    def __str__(self) -> str:
        return str(self.id)

class Wallet(models.Model):
    id=models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    balance = models.IntegerField()
    owner = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,db_constraint=False)
    latest_transaction = models.ForeignKey(Transaction, on_delete=models.CASCADE)
    created_at = models.DateTimeField(default=timezone.now,editable=False)

    class Meta:
        ordering = ['created_at']

    def __str__(self) -> str:
        return str(self.id)

notice the db_constraint=False i have added on every model with a ForeignKey rxnship to the user model. Note: Am using my User model as the custom user model, and using sqlite3 as my database

here is the test as in postman

{
    "name":"shifa",
    "phone":"0705674532",
    "pin_code":"2011",
    "confirm_pin":"2011",
    "expected_delivery_month":"December"
}

its a post request and returns the following error.

IntegrityError at /user/register/

FOREIGN KEY constraint failed

Request Method: POST
Request URL: http://127.0.0.1:8000/user/register/%0A
Django Version: 2.1
Python Executable: /home/utopia/pregweb/bin/python
Python Version: 3.6.8
Python Path: ['/home/utopia/pregweb/pregnew', '/usr/lib/python36.zip', '/usr/lib/python3.6', '/usr/lib/python3.6/lib-dynload', '/home/utopia/pregweb/lib/python3.6/site-packages']
Server time: Wed, 7 Apr 2021 09:19:04 +0000
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'rest_framework.authtoken',
 'rest_framework',
 'user',
 'wallet']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']


Traceback:

File "/home/utopia/pregweb/lib/python3.6/site-packages/django/db/backends/base/base.py" in _commit
  239.                 return self.connection.commit()

The above exception (FOREIGN KEY constraint failed) was the direct cause of the following exception:

Try to create a transaction like (signal part):

transaction = Transaction(transfer_id=transfer.id,amount=transfer.amount)

I have added id to transfer.

There are at least two problems here.

First, if you create a few DB entities at once, you need to wrap this block in a transaction.atomic .

Second, this model.

class Transaction(models.Model):
    id=models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    amount = models.IntegerField(editable=False)
    payment_id = models.ForeignKey(Payment,blank=True, on_delete=models.CASCADE, null=True)
    transfer_id = models.ForeignKey(Transfer, blank=True,null=True, on_delete=models.CASCADE)

Has nullable fk to Payment and Transfer .

That's how you create it

transfer = Transfer(
        transferred_to=instance, transferred_from=instance,transfer_reason='Initial Transfer',amount=0
    )
    transfer.save()

So you either need to add default=None on these fields and run migrations or pass None on creation.

Third, don't add entity_id in your model. If you create

 transfer = models.ForeignKey(Transfer...

Django will automatically add transfer_id on your model and that's what will be in the DB.

docs

Behind the scenes, Django appends "_id" to the field name to create its database column name. In the above example, the database table for the Car model will have a manufacturer_id column. (You can change this explicitly by specifying db_column) However, your code should never have to deal with the database column name, unless you write custom SQL. You'll always deal with the field names of your model object.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM