Python 如何在Django中以编程方式为给定模型生成CREATETABLE SQL语句?
我需要在Django应用程序中以编程方式为给定的非托管模型生成CREATE TABLE语句(Python 如何在Django中以编程方式为给定模型生成CREATETABLE SQL语句?,python,sql,django,migration,Python,Sql,Django,Migration,我需要在Django应用程序中以编程方式为给定的非托管模型生成CREATE TABLE语句(managed=False) 因为我正在处理遗留数据库,所以我不想创建迁移并使用sqlmigrate /manage.py sql命令在这方面很有用,但它已在Django 1.8中删除 你知道有什么替代方案吗?不幸的是,似乎没有简单的方法可以做到这一点,但为了你的运气,我刚刚成功地为你制作了一个工作片段,挖掘django迁移丛林的内部 只是: 将代码保存到get\u sql\u create\u tabl
managed=False
)
因为我正在处理遗留数据库,所以我不想创建迁移并使用sqlmigrate
/manage.py sql
命令在这方面很有用,但它已在Django 1.8中删除
你知道有什么替代方案吗?不幸的是,似乎没有简单的方法可以做到这一点,但为了你的运气,我刚刚成功地为你制作了一个工作片段,挖掘django迁移丛林的内部 只是:
get\u sql\u create\u table.py
(示例中)$export DJANGO\u SETTINGS\u MODULE=yourproject.SETTINGS
python get\u sql\u create\u table.py yourapp.yourmodel启动脚本
正如建议的那样,我为这个案例发布了一个完整的答案,这个问题可能暗示了这一点 假设您有一个外部DB表,您决定将其作为Django模型访问,因此将其描述为非托管模型()。 稍后,您需要能够在代码中创建它,例如使用本地数据库进行一些测试。显然,Django不会对非托管模型进行迁移,因此不会在测试数据库中创建它。 这可以通过使用DjangoAPI来解决,而无需求助于原始SQL-。请参见下面更完整的示例,但作为简短的回答,您可以这样使用它:
from django.db import connections
with connections['db_to_create_a_table_in'].schema_editor() as schema_editor:
schema_editor.create_model(YourUnmanagedModelClass)
一个实际例子:
# your_app/models/your_model.py
from django.db import models
class IntegrationView(models.Model):
"""A read-only model to access a view in some external DB."""
class Meta:
managed = False
db_table = 'integration_view'
name = models.CharField(
db_column='object_name',
max_length=255,
primaty_key=True,
verbose_name='Object Name',
)
some_value = models.CharField(
db_column='some_object_value',
max_length=255,
blank=True,
null=True,
verbose_name='Some Object Value',
)
# Depending on the situation it might be a good idea to redefine
# some methods as a NOOP as a safety-net.
# Note, that it's not completely safe this way, but might help with some
# silly mistakes in user code
def save(self, *args, **kwargs):
"""Preventing data modification."""
pass
def delete(self, *args, **kwargs):
"""Preventing data deletion."""
pass
现在,假设您需要能够通过Django创建此模型,例如,用于一些测试
# your_app/tests/some_test.py
# This will allow to access the `SchemaEditor` for the DB
from django.db import connections
from django.test import TestCase
from your_app.models.your_model import IntegrationView
class SomeLogicTestCase(TestCase):
"""Tests some logic, that uses `IntegrationView`."""
# Since it is assumed, that the `IntegrationView` is read-only for the
# the case being described it's a good idea to put setup logic in class
# setup fixture, that will run only once for the whole test case
@classmethod
def setUpClass(cls):
"""Prepares `IntegrationView` mock data for the test case."""
# This is the actual part, that will create the table in the DB
# for the unmanaged model (Any model in fact, but managed models will
# have their tables created already by the Django testing framework)
# Note: Here we're able to choose which DB, defined in your settings,
# will be used to create the table
with connections['external_db'].schema_editor() as schema_editor:
schema_editor.create_model(IntegrationView)
# That's all you need, after the execution of this statements
# a DB table for `IntegrationView` will be created in the DB
# defined as `external_db`.
# Now suppose we need to add some mock data...
# Again, if we consider the table to be read-only, the data can be
# defined here, otherwise it's better to do it in `setUp()` method.
# Remember `IntegrationView.save()` is overridden as a NOOP, so simple
# calls to `IntegrationView.save()` or `IntegrationView.objects.create()`
# won't do anything, so we need to "Improvise. Adapt. Overcome."
# One way is to use the `save()` method of the base class,
# but provide the instance of our class
integration_view = IntegrationView(
name='Biggus Dickus',
some_value='Something really important.',
)
super(IntegrationView, integration_view).save(using='external_db')
# Another one is to use the `bulk_create()`, which doesn't use
# `save()` internally, and in fact is a better solution
# if we're creating many records
IntegrationView.objects.using('external_db').bulk_create([
IntegrationView(
name='Sillius Soddus',
some_value='Something important',
),
IntegrationView(
name='Naughtius Maximus',
some_value='Whatever',
),
])
# Don't forget to clean after
@classmethod
def tearDownClass(cls):
with connections['external_db'].schema_editor() as schema_editor:
schema_editor.delete_model(IntegrationView)
def test_some_logic_using_data_from_integration_view(self):
self.assertTrue(IntegrationView.objects.using('external_db').filter(
name='Biggus Dickus',
))
为了使示例更加完整。。。由于我们正在使用多个DB(default
和external_DB
),Django将尝试在这两个DB上运行迁移以进行测试,而到目前为止,DB设置中没有任何选项可以阻止这种情况。因此,我们必须使用自定义DB路由器进行测试
# your_app/tests/base.py
class PreventMigrationsDBRouter:
"""DB router to prevent migrations for specific DBs during tests."""
_NO_MIGRATION_DBS = {'external_db', }
def allow_migrate(self, db, app_label, model_name=None, **hints):
"""Actually disallows migrations for specific DBs."""
return db not in self._NO_MIGRATION_DBS
以及所述情况的测试设置文件示例:
# settings/test.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.oracle',
'NAME': 'db_name',
'USER': 'username',
'HOST': 'localhost',
'PASSWORD': 'password',
'PORT': '1521',
},
# For production here we would have settings to connect to the external DB,
# but for testing purposes we could get by with an SQLite DB
'external_db': {
'ENGINE': 'django.db.backends.sqlite3',
},
}
# Not necessary to use a router in production config, since if the DB
# is unspecified explicitly for some action Django will use the `default` DB
DATABASE_ROUTERS = ['your_app.tests.base.PreventMigrationsDBRouter', ]
希望这个详细的Django用户友好的新示例能够帮助用户节省时间。什么口味的数据库?像MySQL这样的数据库允许您执行“SHOW CREATE TABLE”语句。我想问一下,当SQL语句是非托管模型时,为什么需要“以编程方式生成”它?如果重点是创建一个开发数据库,则从原始数据库创建一个仅模式的SQL转储(或要求dba这样做),并将其包含在源代码中。如果您的最终目标是创建一个表,例如进行一些测试,请看一看在特定时刻强制Django为您创建它的方法,例如测试类设置,不要自己处理原始SQL。@Nikita你真的应该发布一个答案,这样任何人都可以投票给你这个伟大的答案。如果它有效(我没有测试过),那么与我一年前给出的解决方案相比,它是一个非常好的解决方案;-)@费罗,谢谢:)行。它起作用了。当我需要模拟外部系统的只读视图来测试我的模型时,我偶然发现了这个问题。看着你的答案,我明白了,我忘了考虑使用Django迁移框架API。通过对文档进行更多搜索,我使用了我指出的方法。