作为一名使用Django框架开发过多个项目的工程师,我深知数据库迁移和模型创建是每个Django开发者必须掌握的核心技能。今天我将分享从零开始设置数据库并创建第一个模型的完整流程,包含大量官方文档中没有提及的实战技巧。
在Django项目中,数据库迁移是一个分两步走的过程:
bash复制python manage.py migrate
这个命令会执行所有未应用的迁移(migrations),将模型同步到数据库中。第一次运行时,它会创建Django内置应用(如auth、admin等)所需的数据库表。这里有几个关键细节需要注意:
重要提示:在开发环境中,我习惯先使用SQLite进行快速原型开发,等模型稳定后再切换到MySQL或PostgreSQL。这样可以避免频繁修改生产数据库结构带来的麻烦。
迁移完成后,你会看到类似如下的输出:
code复制Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
...
假设我们正在开发一个投票应用(polls),典型的模型定义如下:
python复制# polls/models.py
from django.db import models
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
def __str__(self):
return self.question_text
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
def __str__(self):
return self.choice_text
模型设计时需要考虑的几个关键点:
django.db.models.Model的子类on_delete行为__str__方法不是必须的,但能极大提升后台管理界面的可读性创建或修改模型后,需要生成迁移文件:
bash复制python manage.py makemigrations polls
这个命令会检测模型变化并生成迁移脚本。输出类似:
code复制Migrations for 'polls':
polls/migrations/0001_initial.py
- Create model Question
- Create model Choice
经验分享:我习惯在团队协作时给迁移文件添加有意义的名称,如
0002_add_user_profile.py,这可以通过makemigrations --name参数实现。
在正式应用迁移前,可以先查看生成的SQL:
bash复制python manage.py sqlmigrate polls 0001
这个命令不会真正执行迁移,只是展示Django将要执行的SQL语句。输出示例:
sql复制BEGIN;
--
-- Create model Question
--
CREATE TABLE "polls_question" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"question_text" varchar(200) NOT NULL,
"pub_date" datetime NOT NULL
);
...
COMMIT;
调试技巧:当迁移出现意外行为时,查看原始SQL能帮助快速定位问题。我曾经遇到过Django自动生成的索引名称与数据库限制冲突的情况,就是通过这个方法发现的。
最后一步是执行实际的数据库变更:
bash复制python manage.py migrate
输出会显示应用的迁移:
code复制Operations to perform:
Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
Applying polls.0001_initial... OK
迁移完成后,可以通过Django shell快速测试模型:
bash复制python manage.py shell
然后在shell中操作:
python复制from polls.models import Question, Choice
from django.utils import timezone
q = Question(question_text="What's new?", pub_date=timezone.now())
q.save()
q.choice_set.create(choice_text="Not much", votes=0)
实用技巧:我更喜欢使用
shell_plus(来自django-extensions),它能自动导入所有模型,节省大量时间。
为了通过admin界面管理数据,需要先创建超级用户:
bash复制python manage.py createsuperuser
按照提示输入用户名、邮箱和密码后,就可以访问/admin界面了。要让我们的模型出现在admin中,还需要在polls/admin.py中注册:
python复制from django.contrib import admin
from .models import Question
admin.site.register(Question)
安全提示:在生产环境中,一定要使用强密码并定期更换。我曾见过因为使用简单密码导致的管理员账户被黑案例。
当多人协作时,可能会遇到迁移冲突。解决方法包括:
python manage.py makemigrations --merge创建合并迁移python manage.py migrate --fake跳过某些迁移不同数据库支持的字段类型可能有差异:
跨平台建议:如果项目需要支持多种数据库,避免使用数据库特有字段,或者在代码中做好兼容处理。
select_related和prefetch_related优化关联查询defer()和only()延迟加载不常用的字段python复制# 不好的写法:N+1查询问题
for choice in Choice.objects.all():
print(choice.question.question_text)
# 好的写法:使用select_related
for choice in Choice.objects.select_related('question'):
print(choice.question.question_text)
当需要修改已有模型时:
RunPython操作编写自定义数据迁移ALTER TABLE的零停机策略除了基本字段,模型中可以添加业务逻辑方法:
python复制class Question(models.Model):
# ... 字段定义 ...
def was_published_recently(self):
now = timezone.now()
return now - datetime.timedelta(days=1) <= self.pub_date <= now
Django支持三种继承方式:
通过信号可以在保存、删除等操作前后添加自定义逻辑:
python复制from django.db.models.signals import pre_save
from django.dispatch import receiver
@receiver(pre_save, sender=Question)
def validate_question(sender, instance, **kwargs):
if not instance.question_text.endswith('?'):
instance.question_text += '?'
注意事项:信号处理函数应该尽量简单,避免复杂的业务逻辑。我曾经因为信号处理函数中调用了一个耗时操作,导致批量保存性能急剧下降。
在settings.py中配置连接池:
python复制DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'mydatabase',
'CONN_MAX_AGE': 300, # 5分钟连接保持
}
}
在CI/CD流程中加入迁移检查:
yaml复制# .github/workflows/django.yml
jobs:
test:
steps:
- run: python manage.py makemigrations --check --dry-run
定期检查:
python manage.py showmigrations)我在实际项目中发现,良好的模型设计和迁移策略可以节省大量后期维护成本。特别是在团队协作中,清晰的迁移历史和规范的模型变更流程至关重要。