TL;DR 如果我有一个要为其创建迁移的 Django 模型,我可以运行迁移并在数据库中创建表,然后我可以继续在代码中引用该模型。现在,当我将代码合并到 master 中时,它将包含迁移文件和引用 Django 模型的代码。当我将其拉到新机器(例如 staging/prod)上时,我将希望在这个新环境中运行迁移,以便拥有更新的数据库。正确的?但此操作似乎不可能,因为引用模型的代码将希望表已经存在,甚至只是执行迁移本身,否则迁移命令将失败。如何解决这个问题?
问题
每当我在 products/models.py
中创建一个新的模型类(products
是我的 Django 应用程序的名称)时,我无法引用此类模型(例如 >产品
模型)在进行并运行迁移之前的任何方法或类中。否则,如果我在代码中引用新模型,然后然后运行以下命令/过程中的任意,我会收到OperationalError:没有这样的表
错误:
python manage.py runserver
python manage.py makemigrations [应用程序名称]
python manage.py 迁移
python manage.py showmigrations
- 删除所有现有迁移并尝试使用
python manage.py makemigrations [appname] 重新生成它们
- 删除当前的db.sqlite3文件;删除所有现有迁移并尝试使用
python manage.py makemigrations [appname]
重新生成它们
错误如下所示:
...
File "/Users/my_user/Work/django_proj_1/config/urls.py", line 21, in <module>
path('', include('products.urls')),
...
File "/Users/my_user/Work/django_proj_1/products/urls.py", line 1, in <module>
from products.api_views import ProductList3
File "/Users/my_user/Work/django_proj_1/products/api_views.py", line 7, in <module>
class ProductList3(ListAPIView):
File "/Users/my_user/Work/django_proj_1/products/api_views.py", line 8, in ProductList3
queryset = get_products()
File "/Users/my_user/Work/django_proj_1/products/data_layer/product_data_layer.py", line 4, in get_products
all_prods = list(Product.objects.values())
...
File "/Users/my_user/.local/share/virtualenvs/django_proj_1-a2O6RBaf/lib/python3.8/site-packages/django/db/backends/sqlite3/base.py", line 413, in execute
return Database.Cursor.execute(self, query, params)
django.db.utils.OperationalError: no such table: products_product
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
真正的问题
这种行为可能看起来很正常,但如果您考虑一下,开发人员如何在生产中发布新模型并同时在代码中使用(假设如果您引用创建的模型,则运行迁移将会失败)模型到代码中)?
我发现了类似的问题(我在下面链接了),但它们似乎都与现实世界的大规模生产项目无关。类似问题的唯一“解决方案”是:
- 注释掉最终与数据库中尚不存在的新模型交互的代码(层/链中的所有函数)
- (创建并)运行迁移
- 恢复评论
这似乎非常手动,对于实际生产应用程序来说不可行。我可以理解问题是否与本地开发有关,我可以在其中
- 创建模型,
- 快速进行并运行迁移
- 然后开始编码并使用该模型。
但是,如何在现实生产场景中实现这一点呢?我的意思是,如何在创建模型并在代码中使用该模型的情况下发布单个代码 PR,并确保在发布此类代码时可以在生产计算机上成功运行迁移?
我的问题的更多背景
假设我在 Django 中有以下名为 products
的应用程序,其中包含以下文件:
...
|_products/ # one of the Django apps in the project
|
|_ models.py # where I defined the Product model
|_ data_layer/ # a python module to separate all DB logic
| |_ __init__.py
| |_ product_data_layer.py # file where I put the function that references the Product model
|_ api_views.py # file where I call a function from the data layer
|_ urls.py # file where I create an API endpoint that references api_views.py
|
...
上述文件中的代码:
# products/urls.py content
from products.api_views import ProductList3 # THORWS ERROR
from django.urls import path
urlpatterns = [
path('api/v1/products', ProductList3)
]
# products/api_views.py content
from rest_framework.generics import ListAPIView
from products.serializers import GenericProductSerializer
from products.data_layer.product_data_layer import get_products
class ProductList3(ListAPIView): # THORWS ERROR
queryset = get_products() # THORWS ERROR
serializer_class = GenericProductSerializer
# products/data_layer/product_data_layer.py content
from products.models import Product
def get_products():
all_prods = list(Product.objects.values()) # THORWS ERROR (main problem)
# all the logic I need
return all_prods
# products/models.py content
from django.db import models
class Product(models.Model):
product_name = models.CharField(max_length=100)
product_description = models.TextField('Main Product Description')
def __str__(self):
return self.product_name
类似问题
以下问题是我看过的类似问题,但除了一些开发流程解决方案之外,我没有找到关于如何一次性解决此问题的实质性答案。
最佳答案
您在类声明中写道:
queryset = get_products()
这个函数有这样一行:
all_prods = list(Product.objects.values())
这里发生了什么?直接在类中编写的任何内容都会在创建类时执行。如果它是类的方法,则不会对其进行求值。因此,在创建类时会调用 get_products()
。接下来调用 list(Product.objects.values())
,在查询集上调用 list
强制对查询进行评估。现在显然您正在进行迁移,但表尚不存在,从而导致错误。
首先,您的这段代码是糟糕的设计,即使表Product
发生更改,您的 View 仍然只会显示在第一个查询中获得的产品当服务器启动时。这显然是无意的,也不是您想要做的。
解决办法是什么?显然你的查询应该驻留在它实际应该在的地方。大多数情况下,在具有获取查询集的 Django 类中,有一个名为 get_queryset
的方法可以完成此任务,因此您应该覆盖该方法:
class ProductList3(ListAPIView):
serializer_class = GenericProductSerializer
def get_queryset(self):
return get_products() # More sensible to write it here.
# If concerned about overriding something you can use the below which would be equivalent
# self.queryset = get_products()
# return super().get_queryset()
关于python - Django 3.1 - "OperationalError: no such table"在进行或应用迁移之前使用 ORM 类模型时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66684069/