|
我需要编程Django给定 在应用()中非托管 模型生成CREATE TABLE语句managed = False- x; e) I( ^5 [( ]
因为我在使用旧数据库,我不想创建迁移和使用它sqlmigrate。
& v6 t7 s2 m) L K& q, e该./manage.py sql命令对这个目的很有用,但已经存在了Django 1.8中删除。7 _4 W% J/ x3 A- O- Q: ]
你知道其他选择吗?
( u2 ^% S5 v* E& l ) s& H) @ k, F3 K4 u
解决方案: 1 g G" |) N& \! P7 x
我对此案作出了完整的回答,这可能暗示了这一点。" c& L% Y# ?5 ^5 Z* |6 h+ V- W
假设你有一个外部数据库表,你决定把它当作Django访问模型时,将其描述为非托管模型(Meta: managed =False)。在未来,您需要能够在代码中创建它,例如,使用本地数据库进行某些测试。Django非托管模型不会迁移,因此不会在测试数据库中创建。Django
1 i4 G, i( i5 q' ~' i" hAPI解决这个问题而不使用原始问题SQL-
( S) c7 [- [1 m O4 BSchemaEditor。请参考以下更完整的例子,但作为一个简短的答案,您可以使用它:9 D% i( S; b* t n( c( N7 T
from django.db import connections with connections['db_to_create_a_table_in'].schema_editor() as schema_editor: schema_editor.create_model(YourUnmanagedModelClass)实例:
$ k" k$ F( C' w% r# your_app/models/your_model.pyfrom django.db import modelsclass 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): """reventing data modification.""" pass def delete(self,*args,**kwargs): """reventing data deletion.""" pass假设你现在需要能够通过Django例如,在某些测试中创建此模型。
& f7 N) _2 ?3 Q) U3 A# your_app/tests/some_test.py# This will allow to access the `SchemaEditor` for the DBfrom django.db import connectionsfrom django.test import TestCasefrom your_app.models.your_model import IntegrationViewclass 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): """repares `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', ))使示例更加完整…因为我们使用多个数据库(default和external_db),Django将尝试在两个数据库上运行迁移进行测试。到目前为止,数据库设置中没有选项可以阻止这种情况。因此,我们必须使用自定义DB测试路由器。, d9 t* a- M7 F& O9 Z
# your_app/tests/base.pyclass 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并根据上述情况提供测试设置文件示例:8 u. Y" s |( w" _
# settings/test.pyDATABASES = default ENGINE': 'django.db.backends.oracle NAME': 'db_name USER': 'username HOST': 'localhost PASSWORD': 'password PORT # 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` DBDATABASE_ROUTERS = ['your_app.tests.base.PreventMigrationsDBRouter',]希望这个细节,新的Django用户友好示例可以帮助某人,节省他们的时间。 |
|