You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

创建Django迁移时ForeignKey缺失必填参数on_delete的问题

Django迁移报错:ForeignKey缺少on_delete参数的解决及代码优化建议

我是Python新手,最近在用Python+Django+PostgreSQL搭建项目,已经在models.py里定义了对应的数据表模型类,但执行迁移命令时遇到了报错,具体错误信息如下:

File "..\database\models.py", line 40, in <module>
    class group(models.Model):
File "database\models.py", line 41, in group
    user_id = models.ForeignKey(user)
TypeError: __init__() missing 1 required positional argument: 'on_delete'

我的models.py代码如下:

from django.db import models
from datetime import date  # 假设已导入date模块

class user(models.Model):
    STATUS_CHOICES = (
        ('active','Active'),
        ('not active', 'Not active')
    )
    user_id= models.AutoField(primary_key=True)
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    email = models.EmailField(max_length=70, null=True, blank=True, unique=True)
    phone = models.CharField( max_length=17, blank=True)
    gender = models.CharField(max_length=50)
    region = models.CharField(max_length=50)
    password = models.CharField(widget=models.PasswordInput)
    active = models.CharField(max_length=50, choices=STATUS_CHOICES, default='not active')
    register_date = models.DateField(default=date.today)

    def _str_(self):
        return self.user_id

class train(models.Model):
    train_CHOICES = (
        ('intercity', 'Intercity'),
        ('sprinter', 'Sprinter')
    )
    train_id= models.AutoField(primary_key=True)
    train_type = models.CharField(max_length=50, choices=train_CHOICES, default='intercity')

    def _str_(self):
        return self.train_id

class group(models.Model):
    user_id = models.ForeignKey(user)
    group_id = models.AutoField(primary_key=True)
    trip_id = models.ForeignKey(trip)
    created_date = models.DateField(default=date.today)
    started_at = models.DateTimeField()

    def _str_(self):
        return self.group_id

排查思路与解决提示

1. 先解决最直接的on_delete参数问题

从Django 2.0版本开始,ForeignKey字段必须指定on_delete参数,用来定义当关联对象被删除时,当前对象的处理逻辑。常用选项有:

  • models.CASCADE:级联删除,关联对象(比如user)被删除时,所有关联的group对象也会被删除,适合强关联场景。
  • models.PROTECT:阻止删除关联对象,若尝试删除仍有group关联的user,会抛出错误,避免误删。
  • models.SET_NULL:将外键字段设为NULL,需同时设置null=True,适合允许关联对象为空的场景。

修改group模型的外键字段示例:

class group(models.Model):
    user_id = models.ForeignKey(user, on_delete=models.CASCADE)
    group_id = models.AutoField(primary_key=True)
    # 注意:trip模型必须已定义,否则会报错
    trip_id = models.ForeignKey(trip, on_delete=models.CASCADE)
    # ... 其他字段不变

2. 修正模型类名的命名规范

Python的PEP8规范要求类名用大驼峰命名法(每个单词首字母大写),这也是Django的官方惯例,能避免和内置名称冲突,提升可读性:

  • 把user改成User
  • train改成Train
  • group改成Group
  • 外键引用的地方也要同步修改,比如models.ForeignKey(User)

3. 修复__str__方法的错误

你现在写的是_str_(单下划线),正确的魔法方法是__str__(双下划线),且该方法必须返回字符串类型,不能直接返回整数(比如self.user_id是整数类型的AutoField),否则会报错。修改示例:

class User(models.Model):
    # ... 其他字段
    def __str__(self):
        # 返回用户姓名+邮箱,比单纯返回ID更友好
        return f"{self.first_name} {self.last_name} ({self.email})"

class Train(models.Model):
    # ... 其他字段
    def __str__(self):
        return f"Train {self.train_id} - {self.train_type}"

class Group(models.Model):
    # ... 其他字段
    def __str__(self):
        return f"Group {self.group_id}"

4. 修正password字段的错误

models.CharField并没有widget参数,widget是Django表单(Form)类的参数。而且直接存储明文密码极不安全,建议:

  • 做用户认证时,继承Django自带的AbstractUser模型,它内置了安全的密码处理、用户权限等功能:
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    # 添加自定义字段
    STATUS_CHOICES = (
        ('active','Active'),
        ('not active', 'Not active')
    )
    gender = models.CharField(max_length=50)
    region = models.CharField(max_length=50)
    active = models.CharField(max_length=50, choices=STATUS_CHOICES, default='not active')
    register_date = models.DateField(default=date.today)

    def __str__(self):
        return f"{self.username} - {self.email}"
  • 若一定要自定义密码字段,要使用make_password加密存储,验证时用check_password:
from django.contrib.auth.hashers import make_password, check_password

class User(models.Model):
    # ... 其他字段
    password = models.CharField(max_length=255)

    def save(self, *args, **kwargs):
        # 保存前加密密码
        self.password = make_password(self.password)
        super().save(*args, **kwargs)

5. 确保trip模型已定义

你的group模型引用了trip外键,但当前代码里没有trip模型的定义,必须确保该模型存在(要么在当前models.py里定义,要么从其他app导入),否则迁移时会报错。

6. 检查模块导入

确保已导入所需模块,比如from datetime import date,否则date.today会抛出NameError。

修改完成后,重新执行迁移命令:

python manage.py makemigrations
python manage.py migrate

内容的提问来源于stack exchange,提问作者AlisonVilmoreV

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:15:54