I have two models in separate apps and I’d like to add an HStoreField
for each of them.
Should I:
a) Add an HStoreExtension
operation to both migrations
b) Add the extension to only one of them (and make sure the one with the operation runs first)
c) not use HStoreExtension
at all and manage my database extensions separately
With option a), I run into an issue when I try to revert one of the migrations because Django tries to delete the extension from the database but it’s being used in the other app.
With option b) the same problem is still there but it only manifests if I try to revert the migration for the one app that declares the operation.
With option c) I can apply/revert migrations to my heart’s content but I’m now faced with managing extensions manually for all my databases (locally, in production, on staging servers and more annoyingly for the test database as well).
No matter which option I choose it seems I run into problems. Is that just unavoidable or is there a fourth option I haven’t considered?
Thanks
If you do (b) and ensure dependencies
in the second migration points to the first one in the other app, that should work fine. If you try reverse the first one, it should automatically try reverse the second one too.
Here are a couple of other options, assuming its harmless to leave the extension loaded when no field uses it:
d) Subclass HStoreExtension
and override its “reverse” method to be a no-op.
e) Load the extension in a custom database backend prepare_database
method instead, à la postgis.
HTH!
I ended up going with option e) which seems like the least bad to me and was the easiest to turn on and off in my settings (extension creation was only really a problem for me when running tests).
Here’s the code I ended up with:
from django.contrib.postgres.operations import (
HStoreExtension,
TrigramExtension,
UnaccentExtension,
)
from django.db.backends.postgresql.base import DatabaseWrapper as PgDatabaseWrapper
class DatabaseWrapper(PgDatabaseWrapper):
"""
A postgres database wrapper that auto-installs extension just after
the database has been created.
It's a bit of a hack, but it's the least bad solution I found at the time
of writing.
"""
AUTOINSTALLED_EXTENSIONS = (
HStoreExtension,
TrigramExtension,
UnaccentExtension,
)
def prepare_database(self):
super().prepare_database()
for ext in self.AUTOINSTALLED_EXTENSIONS:
self._install_extension(ext)
def _install_extension(self, extension):
with self.schema_editor() as editor:
extension().database_forwards(
app_label=None,
schema_editor=editor,
from_state=None,
to_state=None,
)
I still think it’s weird that Django tries to delete the extension as a reverse operation, but at least now I have a workaround.
Thanks for your help!