#23749 closed Cleanup/optimization (fixed)
Add an example of using database routers in migrations
Reported by: | Alfred Perlstein | Owned by: | |
---|---|---|---|
Component: | Documentation | Version: | 1.7 |
Severity: | Normal | Keywords: | migrations dbrouter |
Cc: | me@… | Triage Stage: | Ready for checkin |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | yes | UI/UX: | no |
Description
It seems that the migration system in 1.7 does not properly query the dbrouter setup when a migrations.RunPython method is invoked.
Running the following command:
python manage.py migrate mycompany --noinput --traceback --database=logs --verbosity=2
Never seems to call the router for the following migration, so as you can see I've added code to parse sys.argv as an experiment which seems to sort-of work, but really looks like the wrong way to do this.
# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models, migrations from applianceUI.mycompany.dbrouter import LogRouter import sys def bkd_default_ports(apps, schema_editor): db = None for arg in sys.argv: if '=' not in arg: continue print "arg: ", arg k, v = arg.split('=', 2) print "k: '%s', v: '%s' " % (k,v) if k == "--database": print arg db = v break print "db is %s" % db router = LogRouter() Blockd = apps.get_model("mycompany", "Blockd") if router.allow_migrate(db, Blockd) is False: return try: bkd = Blockd.objects.order_by("-id")[0] except IndexError: bkd = Blockd.objects.create() if bkd.bkd_ports is "": bkd.bkd_ports = "80, 8080, 3128" bkd.save() class Migration(migrations.Migration): dependencies = [ ('mycompany', '0005_auto_direction_and_indexes'), ] operations = [ migrations.RunPython(bkd_default_ports), ]
The arguments apps and schema_editor didn't give me enough data to make my decision. How can i properly derive that the database is "logs"?
Attachments (1)
Change History (28)
comment:1 by , 10 years ago
comment:2 by , 10 years ago
I think this is a duplicate of #23273 (or at least closely related to). Have you read that ticket?
comment:3 by , 10 years ago
To be honest this is more like #22583.
#23273 is too complex for me to parse, there may be some overlap, but I do not think so.
All that is needed is that we somehow pass the --database=FOO option to the RunPython method as a kwarg so that the router can provide a function to query that... OR that the migration itself can decide.
I am not sure why the name of the db is not given to the RunPython method.
This is a relatively simple request, can this please be fixed?
comment:4 by , 10 years ago
Ah yes, that was the ticket I was looking for but couldn't find - forgot it had been closed. Anyway, wouldn't schema_editor.connection.alias
give you the name of the database? If so, we should likely document the solution.
comment:5 by , 10 years ago
Component: | Migrations → Documentation |
---|---|
Easy pickings: | set |
Summary: | migrations.RunPython broken with dbrouters. → Document how to get the database alias in migrations |
Triage Stage: | Unreviewed → Accepted |
Type: | Bug → Cleanup/optimization |
comment:6 by , 10 years ago
I just verified that schema_editor.connection.alias
does return the current connection in use, even when --database=foobar
is used. We could mention this in the data migration doc and/or the `SchemaEditor` doc. SchemaEditor
doc could use a Properties
section along with the Methods
section.
Edit: Looks like it's already documented in the RunPython
section https://github.com/django/django/blob/aa5ef0d4fc67a95ac2a5103810d0c87d8c547bac/docs/ref/migration-operations.txt#L316-L322
comment:7 by , 10 years ago
Cc: | added |
---|
comment:8 by , 10 years ago
Resolution: | → worksforme |
---|---|
Status: | new → closed |
comment:9 by , 10 years ago
Can someone give me a pointer to how to edit the page
https://docs.djangoproject.com/en/1.7/topics/migrations/#data-migrations
-Or-
https://docs.djangoproject.com/en/1.7/ref/schema-editor/
?
To be perfectly honest the addition to the RunPython section (https://github.com/django/django/blob/aa5ef0d4fc67a95ac2a5103810d0c87d8c547bac/docs/ref/migration-operations.txt#L316-L322) is not something that I would have been able to find via google, nor is it likely I would have been able to understand it.
I'm perfectly willing to update the docs for the migration or schema_editor pages, I just need a pointer how to get started. Is it a wiki I can log into? Or is there a git repo I can clone?
Please advise.
thank you!
-Alfred
comment:10 by , 10 years ago
Resolution: | worksforme |
---|---|
Status: | closed → new |
Please see Writing documentation.
comment:11 by , 10 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
I have created a pull request against documentation here:
comment:12 by , 10 years ago
If this looks OK I would like to document the connection
handle being inside the schema_editor
on another page. Please let me know if this document addition is in the correct path and if so I will look into augmenting the schema_editor
page.
Is it the norm to document member variables? Is there an existing example? The reason I ask is that schema_editor
only has its methods documented and no member variables.
thank you.
comment:13 by , 10 years ago
Tim,
I've submitted a github pull request and tried my best to follow the guide on submitting patches.
I have no spelling errors and no warnings that are in files I have touched.
Can the github pull request be merged at your convenience?
comment:14 by , 10 years ago
Owner: | removed |
---|---|
Status: | assigned → new |
comment:15 by , 10 years ago
Has patch: | set |
---|
comment:16 by , 10 years ago
Patch needs improvement: | set |
---|
follow-up: 18 comment:17 by , 10 years ago
#23879 is somewhat related. See this POC branch: https://github.com/wrwrwr/django/compare/feature/always-test-gis-and-postgres
follow-up: 19 comment:18 by , 10 years ago
Replying to claudep:
#23879 is somewhat related. See this POC branch: https://github.com/wrwrwr/django/compare/feature/always-test-gis-and-postgres
TBH, I don't see any relation to the issue here. It's not about running on a specific vendor, it's about different databases, probably even of the same vendor.
comment:19 by , 10 years ago
Replying to MarkusH:
Replying to claudep:
#23879 is somewhat related. See this POC branch: https://github.com/wrwrwr/django/compare/feature/always-test-gis-and-postgres
TBH, I don't see any relation to the issue here. It's not about running on a specific vendor, it's about different databases, probably even of the same vendor.
+1
comment:20 by , 10 years ago
-
docs/ref/schema-editor.txt
diff --git a/docs/ref/schema-editor.txt b/docs/ref/schema-editor.txt index 54dd3bf..c503296 100644
a b below. 158 158 159 159 .. attribute:: SchemaEditor.connection 160 160 161 A connection object to the database. A useful attribute of the 162 connection is ``alias`` which can be used to determine the name of 163 the database beingaccessed.161 A connection object to the database. A useful attribute of the connection is 162 ``alias`` which can be used to determine the name of the database being 163 accessed. 164 164 165 This in turn is useful when doing data migrations for 166 :ref:`migrations withmultiple database backends <data-migrations-and-multiple-databases>`.165 This in turn is useful when doing data migrations for :ref:`migrations with 166 multiple database backends <data-migrations-and-multiple-databases>`. -
docs/topics/migrations.txt
diff --git a/docs/topics/migrations.txt b/docs/topics/migrations.txt index 746f4d2..a743bf2 100644
a b backwards will raise an exception. 472 472 Data migrations and multiple databases 473 473 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 474 474 475 One of the challenges that can be encountered is figuring out whether 476 or not to run a migration when using multiple databases. 477 478 For example, you may want to **only** run a migration against a 479 particular database. 480 481 In order to do that you can filter inside of your data migration 482 by looking at the ``schema_editor.connection.alias`` attribute. 483 However it is better to have your ``dbrouter`` leverage the same 484 ``allow_migrate`` call by making the following method in your 485 router:: 486 487 class MyRouter(object): 488 489 def allow_migrate(self, db, model): 490 # your typical migration code here 491 492 def runpython_ok(self, apps, schema_editor, model): 493 db = schema_editor.connection.alias 494 if self.allow_migrate(db, model): 495 return True 496 return False 497 498 Then to leverage this in your migrations, do the following:: 499 500 # -*- coding: utf-8 -*- 501 from __future__ import unicode_literals 502 503 from django.db import models, migrations 504 from myappname.dbrouter import MyRouter 505 import sys 506 507 def forwards(apps, schema_editor): 508 # only run this for the correct databases 509 MyModel = apps.get_model("myappname", "MyModel") 510 if not MyRouter().runpython_ok(apps, schema_editor, MyModel): 511 return 512 513 """ 514 This is a global configuration model so there should be only 515 row. 516 Get the first one -or- create the initial one. 517 Only set the default ports if it has not been set by user 518 already. 519 """ 520 try: 521 my_model = MyModel.objects.order_by("-id").first() 522 except IndexError: 523 my_model = MyModel.objects.create() 524 if my_model.my_model_ports == "": 525 my_model.my_model_ports = "80, 8080, 3128" 526 my_model.save() 527 528 def backwards(apps, schema_editor): 529 pass 530 531 class Migration(migrations.Migration): 532 533 dependencies = [ 534 # Dependencies to other migrations 535 ] 536 537 operations = [ 538 migrations.RunPython(forwards, backwards), 539 ] 475 One of the challenges that can be encountered is figuring out whether or not to 476 run a migration when using multiple databases. 477 478 For example, you may want to **only** run a migration against a particular 479 database. 480 481 In order to do that you can check the connection alias inside a ``RunPython`` 482 forwards and backwards operation by looking at the 483 ``schema_editor.connection.alias`` attribute. 484 485 # -*- coding: utf-8 -*- 486 from __future__ import unicode_literals 487 488 from django.db import migrations 489 490 def forwards(apps, schema_editor): 491 if not schema_editor.connection.alias == 'master-db': 492 return 493 # Your migration code goes here 494 495 class Migration(migrations.Migration): 496 497 dependencies = [ 498 # Dependencies to other migrations 499 ] 500 501 operations = [ 502 migrations.RunPython(forwards), 503 ] 504 505 You can also use your database router's ``allow_migrate`` method, but keep in 506 mind that the imported router needs to stay around as long as it is referenced 507 inside a migration: 508 509 .. snippet:: 510 :filename: myapp/dbrouters.py 511 512 class MyRouter(object): 513 514 def allow_migrate(self, db, model): 515 return db == 'default': 516 517 Then, to leverage this in your migrations, do the following:: 518 519 # -*- coding: utf-8 -*- 520 from __future__ import unicode_literals 521 522 from django.db import models, migrations 523 524 from myappname.dbrouters import MyRouter 525 526 def forwards(apps, schema_editor): 527 MyModel = apps.get_model("myappname", "MyModel") 528 if not MyRouter().allow_migrate(schema_editor.connection.alias, MyModel): 529 return 530 # Your migration code goes here 531 532 class Migration(migrations.Migration): 533 534 dependencies = [ 535 # Dependencies to other migrations 536 ] 537 538 operations = [ 539 migrations.RunPython(forwards), 540 ] 540 541 541 542 More advanced migrations 542 543 ~~~~~~~~~~~~~~~~~~~~~~~~
by , 10 years ago
comment:21 by , 10 years ago
Which version of the pull request was this made against? It seems to be done against an older version and will not apply cleanly.
comment:24 by , 10 years ago
Patch needs improvement: | unset |
---|---|
Triage Stage: | Accepted → Ready for checkin |
comment:25 by , 10 years ago
Summary: | Document how to get the database alias in migrations → Add an example of using database routers in migrations |
---|
comment:26 by , 10 years ago
Owner: | set to |
---|---|
Resolution: | → fixed |
Status: | new → closed |
Traceback I get is as follows:
My dbrouter looks like such: