python - Django 3.1 - "OperationalError: no such table"在进行或应用迁移之前使用 ORM 类模型时

标签 python django django-models orm migration

TL;DR 如果我有一个要为其创建迁移的 Django 模型,我可以运行迁移并在数据库中创建表,然后我可以继续在代码中引用该模型。现在,当我将代码合并到 master 中时,它将包含迁移文件和引用 Django 模型的代码。当我将其拉到新机器(例如 staging/prod)上时,我将希望在这个新环境中运行迁移,以便拥有更新的数据库。正确的?但此操作似乎不可能,因为引用模型的代码将希望表已经存在,甚至只是执行迁移本身,否则迁移命令将失败。如何解决这个问题?

问题

每当我在 products/models.py 中创建一个新的模型类(products 是我的 Django 应用程序的名称)时,我无法引用此类模型(例如 >产品模型)在进行并运行迁移之前的任何方法或类中。否则,如果我在代码中引用新模型,然后然后运行以下命令/过程中的任意,我会收到OperationalError:没有这样的表错误:

  1. python manage.py runserver
  2. python manage.py makemigrations [应用程序名称]
  3. python manage.py 迁移
  4. python manage.py showmigrations
  5. 删除所有现有迁移并尝试使用 python manage.py makemigrations [appname] 重新生成它们
  6. 删除当前的db.sqlite3文件;删除所有现有迁移并尝试使用 python manage.py makemigrations [appname]
  7. 重新生成它们

错误如下所示:

...
  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
                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

真正的问题

这种行为可能看起来很正常,但如果您考虑一下,开发人员如何在生产中发布新模型并同时在代码中使用(假设如果您引用创建的模型,则运行迁移将会失败)模型到代码中)?

我发现了类似的问题(我在下面链接了),但它们似乎都与现实世界的大规模生产项目无关。类似问题的唯一“解决方案”是:

  1. 注释掉最终与数据库中尚不存在的新模型交互的代码(层/链中的所有函数)
  2. (创建并)运行迁移
  3. 恢复评论

这似乎非常手动,对于实际生产应用程序来说不可行。我可以理解问题是否与本地开发有关,我可以在其中

  1. 创建模型,
  2. 快速进行并运行迁移
  3. 然后开始编码并使用该模型。

但是,如何在现实生产场景中实现这一点呢?我的意思是,如何在创建模型并在代码中使用该模型的情况下发布单个代码 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

类似问题

以下问题是我看过的类似问题,但除了一些开发流程解决方案之外,我没有找到关于如何一次性解决此问题的实质性答案。

  1. django migration no such table 1 - unclear as to how not to execute code on import
  2. django migration no such table 2 - no answers as of 18/03/2021
  3. django migration no such table 3 - does not work in my case

最佳答案

您在类声明中写道:

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/

相关文章:

python - 是否可以从 Django 中的 concat 内部获取显示名称?

python - 从 mpf 转换为 Sympy Float 而不会丢失精度

html - 未找到 'delete_task/{{todo.id}}' 的反向。 'delete_task/{{todo.id}}' 不是有效的 View 函数或模式名称

django - add() 为 ManyToManyField 指定一个中间模型

Python Django 在保存时编码法语重音字符,但 Django 管理可以正常工作

python - Amazon Lex 中响应卡按钮中的超链接

Django 多嵌套内联表单集

javascript - 返回 QuerySet.values() 或 value_list() 中方法的结果?

python - Django 对过滤后的查询集的所有相关对象求和

python - 如何从列表列表或元组列表中删除包含某些单词的列表?