Issue with multiple database backends

62 views
Skip to first unread message

Javier Buzzi

unread,
Apr 14, 2021, 6:53:29 AM4/14/21
to Django developers (Contributions to Django itself)
I have a model with a really long name think `ThisIsAReallyLongModelNameThatIsAlsoVeryOld`. To complicate things further I have a Oracle instance without archival data designated connection name `old` and a new postgres instance with the connection name `default`.

Issue i've found is that if i try and call the model `ThisIsAReallyLongModelNameThatIsAlsoVeryOld.objects.using('old').count()` i get an error saying that the table `APP_THISISAREALLYLONGMODEL5300` does not exist, when it should be using `APP_THISISAREALLYLONGMODEL5BD6` instead. By the way when I use postgres, it works as expected: `ThisIsAReallyLongModelNameThatIsAlsoVeryOld.objects.using('default').count()`

This is because the default connection is used when the model is instantiated, and then its used from that moment on.


This is an issue that seems to go back to the beginning of Django.

Is this something known? Is this something we can fix?

Thanks,


================= here be dragons ===================
as a temporary hack i did this for now, sorry for the formatting, google groups didn't let me format it the way i wanted.


from django.db.models.sql import compiler
from django.db.backends.utils import truncate_name


class ConnectionAwareAlias(dict):
def __init__(self, query, connection):
self.query = query
self.connection = connection
self['*'] = '*'

def __contains__(self, item):
model = self.query.model
db_table = "%s_%s" % (model._meta.app_label, model._meta.model_name)
if item == db_table:
if not self.get(item):
db_table = truncate_name(db_table, self.connection.ops.max_name_length())
self[item] = db_table

return True

return self.get(item) is not None


class NewSQLCompiler(compiler.SQLCompiler):
def __init__(self, query, connection, using):
super(NewSQLCompiler, self).__init__(query, connection, using)
self.quote_cache = ConnectionAwareAlias(query, connection)

compiler.SQLCompiler = NewSQLCompiler

Adam Johnson

unread,
Apr 14, 2021, 7:11:22 AM4/14/21
to django-d...@googlegroups.com
Hi

This seems like a genuine bug, Django should not assume that all backends have the same max table name length. Please file a ticket.

You can workaround this right now by defining db_table on your model. You might need two model classes, sharing details via inheritance, for the different backends though.

I can see two solutions, either:
  • truncate to the minimum max length across all models
  • remove the truncation behaviour and instead have a system check error triggered when a table name is too long for at least one backend. This would allow users to be explicit about the fix, would not change table names when adding/changing database backends, and would remove the somewhat undesirable/confusing hash at the end of table names.
Either looks like they'd need to go through the deprecation pathway.

Thanks,

Adam


--
You received this message because you are subscribed to the Google Groups "Django developers (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/95d2dd47-a635-4c6d-86ce-8dde8e37e536n%40googlegroups.com.

Shai Berger

unread,
Apr 14, 2021, 7:35:22 AM4/14/21
to 'Django developers (Contributions to Django itself)
Hi,

On Wed, 14 Apr 2021 12:10:56 +0100
"'Adam Johnson' via Django developers (Contributions to Django
itself)" <django-d...@googlegroups.com> wrote:

> Hi
>
> This seems like a genuine bug, Django should not assume that all
> backends have the same max table name length. Please file a ticket.
>

Right, but it may be a simpler bug than the one you have in mind.

> >
> > Issue i've found is that if i try and call the model
> > `ThisIsAReallyLongModelNameThatIsAlsoVeryOld.objects.using('old').count()`
> > i get an error saying that the table
> > `APP_THISISAREALLYLONGMODEL5300` does not exist, when it should be
> > using `APP_THISISAREALLYLONGMODEL5BD6` instead.

So, you get two different name truncations -- one if the model is
instantiated with Oracle as the default connection (presumably that's
the 5BD6 one), and another if it is instantiated with Postgres and
later used via Oracle. I'd venture as far as guessing that the
difference is that only in one of the cases, the name is turned to
uppercase before shortening; this is how it was in a similar old case,
solved years ago (there, the names of auto-generated through-tables for
ManyToManyField were shortened in a different way than
explicitly-created models, and hilarious breakage ensued when anyone
wanted to add a field to a through table).

I suspect the only fix needed is to make the two ways match
(in favor of the "instantiated on Oracle" way). I doubt it would even
require a deprecation.

HTH,
Shai.

Javier Buzzi

unread,
Apr 14, 2021, 11:05:36 AM4/14/21
to Django developers (Contributions to Django itself)
Reply all
Reply to author
Forward
0 new messages