Python 如何在Django中以编程方式为给定模型生成CREATETABLE SQL语句?

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

我需要在Django应用程序中以编程方式为给定的非托管模型生成CREATE TABLE语句(
managed=False

因为我正在处理遗留数据库,所以我不想创建迁移并使用
sqlmigrate

/manage.py sql
命令在这方面很有用,但它已在Django 1.8中删除


你知道有什么替代方案吗?

不幸的是,似乎没有简单的方法可以做到这一点,但为了你的运气,我刚刚成功地为你制作了一个工作片段,挖掘django迁移丛林的内部

只是:

  • 将代码保存到
    get\u sql\u create\u table.py
    (示例中)
  • do
    $export DJANGO\u SETTINGS\u MODULE=yourproject.SETTINGS
  • 使用
    python get\u sql\u create\u table.py yourapp.yourmodel启动脚本
  • 它应该输出你需要的东西

    希望有帮助

    导入django django.setup() 从django.db.migrations.state导入ModelState 从django.db.migrations导入操作 从django.db.migrations.migration导入迁移 从django.db导入连接 从django.db.migrations.state导入项目状态 def get_create_sql_for_模型(模型): 模型状态=模型状态。来自模型(模型) #使用CreateModel操作创建假迁移 cm=operations.CreateModel(name=model\u state.name,fields=model\u state.fields) 迁移=迁移(“假迁移”,“应用程序”) migration.operations.append(cm) #让迁移框架认为项目处于初始状态 state=ProjectState() #通过绑定到连接的模式编辑器获取SQL 连接=连接['default'] 使用connection.schema_编辑器(collect_sql=True,atomic=migration.atomic)作为schema_编辑器: state=migration.apply(state,schema\u编辑器,collect\u sql=True) #返回CREATETABLE语句 返回“\n”.join(模式编辑器.sql) 如果名称=“\uuuuu main\uuuuuuuu”: 导入导入库 导入系统 如果len(系统argv)<2: 打印(“用法:{}”。格式(sys.argv[0])) 系统出口(100) app,model_name=sys.argv[1]。拆分(“.”) models=importlib.import_模块(“{}.models.format(app)) model=getattr(models,model\u name) rv=获取\u创建\u sql\u用于\u模型(模型) 打印(rv)
    正如建议的那样,我为这个案例发布了一个完整的答案,这个问题可能暗示了这一点

    假设您有一个外部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。通过对文档进行更多搜索,我使用了我指出的方法。