Django 3.1-"OperationalError:没有这样的表";在进行或应用迁移之前使用ORM类模型时 [英] Django 3.1 - "OperationalError: no such table" when using an ORM Class Model before making or applying a migration

查看:44
本文介绍了Django 3.1-"OperationalError:没有这样的表";在进行或应用迁移之前使用ORM类模型时的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

问题

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

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

错误看起来像这样:

  ...< module>中的文件"/Users/my_user/Work/django_proj_1/config/urls.py",第21行.path('',include('products.urls')),...在< module>中的文件"/Users/my_user/Work/django_proj_1/products/urls.py"第1行中.从products.api_views导入ProductList3< module>中的文件"/Users/my_user/Work/django_proj_1/products/api_views.py",第7行.类ProductList3(ListAPIView):ProductList3中的文件"/Users/my_user/Work/django_proj_1/products/api_views.py",第8行queryset = get_products()get_products中的文件"/Users/my_user/Work/django_proj_1/products/data_layer/product_data_layer.py",第4行all_prods =列表(Product.objects.values())...在执行的文件"/Users/my_user/.local/share/virtualenvs/django_proj_1-a2O6RBaf/lib/python3.8/site-packages/django/db/backends/sqlite3/base.py"中执行返回Database.Cursor.execute(自我,查询,参数)django.db.utils.OperationalError:没有这样的表:products_product^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 

真正的问题

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

我发现了类似的问题(我在下面链接了),但是似乎没有一个问题与现实世界中的大规模生产项目相关.类似问题中唯一的解决方案"是:

  1. 注释掉最终与数据库中尚不存在的新模型进行交互的代码(层/链中的所有功能)
  2. (执行和)运行迁移
  3. 回复评论

这似乎是手动的,对于实际的生产应用程序不可行.我可以理解问题是否出在本地开发中,

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

但是,在实际的生产场景中如何进行呢?我的意思是,如何释放在创建模型以及在代码中使用模型的单个PR PR,并确保在发布此类代码后可以在生产机器上成功运行迁移?/p>

我的问题的更多背景信息

假设我在Django中有一个名为 products 的以下应用,其中包括以下文件:

  ...| _products/#项目中的Django应用之一|| _ models.py#我在其中定义产品模型的地方| _ data_layer/#分离所有数据库逻辑的python模块|| _ __init__.py|| _ product_data_layer.py#文件,在其中放置了引用产品模型的函数| _ api_views.py#文件,我从数据层调用函数| _ urls.py#文件,在其中创建引用api_views.py的API端点|... 

上述文件中的代码:

 #个products/urls.py内容从products.api_views导入ProductList3#严重错误从django.urls导入路径urlpatterns = [路径('api/v1/products',ProductList3)] 

 #products/api_views.py内容从rest_framework.generics导入ListAPIView从products.serializers导入GenericProductSerializer从products.data_layer.product_data_layer导入get_products类ProductList3(ListAPIView):#严重错误queryset = get_products()#严重错误serializer_class = GenericProductSerializer 

 #products/data_layer/product_data_layer.py内容从products.models导入产品def get_products():all_prods = list(Product.objects.values())#THORWS错误(主要问题)#我需要的所有逻辑返回all_prods 

 #个products/models.py内容从django.db导入模型类Product(models.Model):product_name = models.CharField(max_length = 100)product_description = models.TextField('主要产品描述')def __str __():返回self.product_name 

类似的问题

以下问题与我看过的问题类似,但是除了一些开发过程解决方案之外,我没有找到有关如何一劳永逸地解决此问题的实质性答案.

  1. django迁移没有这样的表1-不清楚为如何在导入时不执行代码
  2. django迁移没有这样的表2-截至2021年3月18日没有答案
  3. django迁移没有这样的表3-在我的案子

解决方案

您在类声明中编写:

  queryset = get_products() 

此函数有这一行:

  all_prods = list(Product.objects.values()) 

这是怎么回事?创建类时,将直接执行在类内部直接编写的所有内容.如果它是该类的方法,则不会进行评估.因此,在创建类时会调用 get_products().下一个 list(Product.objects.values())会被调用,其中在queryset上调用 list 会强制对查询进行评估.现在显然您正在进行迁移,并且表不存在,但给您一个错误.

首先,即使表 Product 进行了更改,您的此代码也是设计错误,您的视图仍将仅显示在第一个查询中获得的产品服务器启动时.显然这是意想不到的,而不是您想要执行的操作.

解决方案是什么?显然,您的查询应位于实际应有的位置.通常在Django中,类具有获取查询集的方法是执行此任务的名为 get_queryset 的方法,因此您应该重写该方法:

  class ProductList3(ListAPIView): queryset = get_products()serializer_class = GenericProductSerializerdef get_queryset(self):return get_products()#在这里写起来更明智.#如果您担心要覆盖的内容,可以使用下面的等价形式#self.queryset = get_products()#返回super().get_queryset() 

TL;DR If I have a Django model for which I create a migration, I can run the migration and create the table in the DB, then I can proceed to reference the model in the code. Now, when I merge the code into master, it will both contain the migration files and the code that references the Django model. When I pull this onto a new machine (e.g. staging/prod), I will want to run the migrations in this new environment so as to have an updated DB. correct? This operation seems impossible though because the code that references the model will want the table to already exist even to just perform the migration itself, otherwise the migration command fails. How to solve this issue?

The Problem

Whenever I create a new model class inside products/models.py (products is the name of my Django app), I cannot reference such model (e.g. Product model) in any method or class before having made and run the migrations. Otherwise, if I reference the new model in the code and then I run any of the following commands/procedures, I receive the OperationalError: no such table error:

  1. python manage.py runserver
  2. python manage.py makemigrations [appname]
  3. python manage.py migrate
  4. python manage.py showmigrations
  5. Delete all existing migrations and try to regenerate them with python manage.py makemigrations [appname]
  6. Delete current db.sqlite3 file; delete all existing migrations and try to regenerate them with python manage.py makemigrations [appname]

The error looks like this:

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

The Real Question

This behavior may seem normal, but if you think about it, how would a developer make a release in production where a new model is created and also used in the code simultaneously (given that running migrations will fail if you referenced the created model into the code)?

I found similar questions (which I linked below), but none of them seems actually relevant for a real-world large scale production project. The only 'solution' in the similar questions is to:

  1. comment out the code (all functions in the layer/chain) that end up interacting with the new model that does not exist in the DB yet
  2. (make and) run the migrations
  3. revert the comments

This seems pretty manual and not feasible for real-world production applications. I can understand if the issue was for local development, in which I can

  1. create a model,
  2. quickly make and run the migrations
  3. and then start coding and use that model.

However, how is it possible to go about this in a real-world production scenario? By this I mean, how is it possible to release a single PR of code where I create a model as well as use the model in the code and make sure I can successfully run the migration on the production machine upon release of such code?

More Context of my problem

Let's assume I have the following app in Django called products with, among others, the following files:

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

Code inside the above files:

# 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

Similar questions

The following questions are similar questions that I looked at, but besides some dev-process solutions, I found no substantial answer on how to tackle this issue once for all.

  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

解决方案

You write in your class declaration:

queryset = get_products()

And this function has this line:

all_prods = list(Product.objects.values())

What happens here? Anything written directly inside a class is executed when the class is being created. If it were a method of the class then that would not be evaluated. So get_products() is called when the class is being created. Next list(Product.objects.values()) is called where calling list on the queryset forces the query to be evaluated. Now obviously you are making migrations and the tables don't exist yet giving you an error.

Firstly this code of yours is bad design even if there are changes to the table Product your view will still only display the products that were gotten in the first query made when the server was started up. This is obviously unintended and not what you want to do.

What is the solution? Obviously your query should reside somewhere where it is actually meant to be. Mostly in Django with classes that get a queryset there is a method named get_queryset that does this task, so you should be overriding that:

class ProductList3(ListAPIView):
    queryset = get_products()
    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()

这篇关于Django 3.1-"OperationalError:没有这样的表";在进行或应用迁移之前使用ORM类模型时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆