Django应用存在循环依赖,导致外键模型无法迁移
我有两个Django应用,它们之间互相有外键关系。
一个是api应用,另一个叫blog。
这是我blog应用的models.py文件:
class Post(models.Model):
uuid = models.UUIDField(default=uuid.uuid4, primary_key=False, unique=True)
title = models.CharField(max_length=600)
authors = models.ManyToManyField('Author', related_name='authors')
tags = models.ManyToManyField('Tag', related_name='tags')
date_published = models.DateField(auto_now_add=True)
# store table of contents as JSON
table_of_contents = models.TextField(default=dict)
# s3 link to thumbnail
thumbnail = models.URLField(max_length=300)
# each post belongs to a blog
blog = models.ForeignKey('api.Blog', on_delete=models.CASCADE, default=0)
slug = models.SlugField(max_length=100, default='')
# store markdown as text
content = models.TextField(default='')
# link to post
url = models.URLField(default='')
# for json serialization
def as_dict(self):
return {
"title": self.title,
"slug": self.slug,
"authors": [ author.as_dict() for author in self.authors.all() ],
"tags": [ tag.as_dict() for tag in self.tags.all() ],
"date_published": "01/01/2001",
"table_of_contents": self.table_of_contents,
"thumbnail": self.thumbnail,
"id": str(self.uuid),
}
def __str___(self):
return self.title
class Author(models.Model):
name = models.CharField(max_length=100)
blog = models.ForeignKey('api.Blog', on_delete=models.CASCADE, default=0)
def as_dict(self):
return {
"name": self.name,
"profile": self.name
}
def __str__(self):
return self.name
class Tag(models.Model):
name = models.CharField(max_length=50)
blog = models.ForeignKey('api.Blog', on_delete=models.CASCADE, default=0)
def as_dict(self):
return {
"name": self.name
}
def __str__(self):
return self.name
这是我"api"应用的models.py文件:
class Blog(models.Model):
# Foreign key to the associated tenant in case we need to changve schema_name
tenant = models.ForeignKey('Client', on_delete=models.CASCADE, related_name="tenant_blog", default=0)
class Templates(models.TextChoices):
SAAS = 'SAAS'
CHANGELOG = 'CHANGELOG'
HELP = 'HELP'
DEFAULT = 'DEFAULT'
PERSONAL = 'PERSONAL'
class CTAS(models.TextChoices):
DEFAULT = 'DEFAULT'
NEWSLETTER = 'NEWSLETTER'
uuid = models.UUIDField(default=uuid.uuid4, unique=True, primary_key=False)
schema_name = models.CharField(default='', max_length=20)
useCustomDomain = models.BooleanField(default=False)
customDomain = models.CharField(max_length=100, default="")
landing_header = models.CharField(max_length=100, default="")
landing_subheader = models.CharField(max_length=100, default="")
# choose template from the list
template = models.CharField(max_length=18, choices=Templates.choices, default=Templates.DEFAULT)
url = models.URLField(max_length=300, default='')
name = models.CharField(max_length=100, default='')
owner = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, related_name='blogs')
posts = models.ManyToManyField('blog.Post', related_name='posts')
featuredPosts = models.ManyToManyField('blog.Post', related_name='featured')
authors = models.ManyToManyField('blog.Author', related_name="blog_authors")
tags = models.ManyToManyField("blog.Tag", related_name="blog_tags")
# call to action (newsletter or sign up)
ctaType = models.CharField(max_length=50, choices=CTAS.choices, default=CTAS.DEFAULT)
# display options
defaultModeLight = models.BooleanField(default=True)
showThemeToggle = models.BooleanField(default=True)
showSpotlight = models.BooleanField(default=True)
showFooter = models.BooleanField(default=True)
showPoweredByBadge = models.BooleanField(default=True)
showCTA = models.BooleanField(default=True)
page_views_used = models.IntegerField(default=0)
# Is the blog up/accesible to the public?
isActive = models.BooleanField(default=True)
你可以看到这两个应用都有外键,互相依赖,这就造成了循环依赖的问题。
当我运行makemigrations
和migrate
时,出现了relation "blog_author" does not exist
的错误。
我已经尝试过只删除外键字段,进行迁移,然后再加回来并重新迁移,但还是遇到同样的relation "blog_author" does not exist
错误……
我该怎么做才能让Django顺利迁移,而不出现这个错误呢?
1 个回答
回答你的问题:
当你在一个对象上定义了一个 ForeignKey
关系时,Django 会自动创建一种方法,让你可以从其他模型访问这些关系。因为你在 Post
、Author
和 Tag
上定义了 ForeignKey
,所以你不需要在 Blog
上再定义 ManyToMany
关系。
如果你有一个 Blog
对象,你可以通过 blog.[模型名称]_set
来访问与这个博客相关的帖子、作者和标签。例如,要访问与某个博客相关的所有帖子,你可以使用 blog.author_set.filter...
。
如果你想给这些属性指定名称,可以在 ForeignKey
字段中使用 related_name
字段。
class Author(models.Model):
blog = models.ForeignKey('api.Blog', on_delete=models.CASCADE, default=0, related_name="authors")
这样的话,如果你有博客对象,就可以直接使用 blog.authors.filter...
。
一些与问题无关的小建议:
- 与其给
ForeignKey
设置默认值为 0,不如让这个字段可以为空:
blog = models.ForeignKey('api.Blog', on_delete=models.CASCADE, null=True, related_name="authors")
或者,你可以同时去掉 null
和 default
,这样当你尝试添加没有博客的作者/帖子/标签时,就会出现错误(如果每个作者/帖子/标签都必须关联一个博客的话)。
- 你在帖子、标签和作者之间的
ManyToMany
关系中的related_name
字段设置是错误的。related_name
是用来从 另一个 模型访问这个关系的。所以,不应该是:
class Post(models.Model):
tags = models.ManyToManyField('Tag', related_name='tags')
而应该是:
class Post(models.Model):
tags = models.ManyToManyField('Tag', related_name='posts') # This means if you have a `Tag` object, you can do `tag.posts` to get all the associated `Post`s
关于你的属性名称,我建议要么全部使用蛇形命名(snake case),要么全部使用驼峰命名(camel case),而不是两者混合(你现在就是这样)。我更喜欢使用蛇形命名,因为大多数 Django/Python 的属性都是这样。
对于你的
table_of_contents
JSON 属性,应该使用JSONField
(可以在 这里 阅读更多)而不是TextField
。