Opened 11 months ago

Last modified 2 weeks ago

#30760 new Bug

Django admin "view on site" button doesn't respect database routers

Reported by: zooqooo Owned by: nobody
Component: contrib.contenttypes Version: 2.2
Severity: Normal Keywords: Admin, Routers
Cc: Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

I'm using the default admin view 'django.contrib.admin'.

Here is an excerpt from my app_1.models.py

Code highlighting:

class SomeModel(models.Model):
  name = models.CharField(max_length=50)
  slug_name = models.SlugField(max_length=75, unique=True)    
  #Some more fields

  def get_absolute_url(self):
      return reverse("siteSomeModel", kwargs={"slug": self.slug_name})

Here is an excerpt from my site.routers.py

Code highlighting:

class App_1_DataBaseRouter():
  label = "app_1"
  db = "app_1_db"

  def db_for_read(self, model, **hints):
      if model._meta.app_label == self.label:
          return self.db
      return None

  def db_for_write(self, model, **hints):
      if model._meta.app_label == self.label:
          return self.db
      return None

  def allow_relation(self, obj1, obj2, **hints):
      if obj1._meta.app_label == self.label and obj2._meta.app_label == self.label:
          return True
      if self.label not in [obj1._meta.app_label, obj2._meta.app_label]:
          return None
      return False

  def allow_migrate(self, db, app_label, model_name=None, **hints):
      if app_label == self.label:
          return db == self.db
      if db == self.db:
          return False
      return None

when I run in the shell it works

>>>python manage.py shell
>>>from app_1.models import SomeModel
>>>ob=SomeModel.objects.get(name='Some Name')
>>>ob.get_absolute_url()
app1/SomeModel/some-name

But when I click on view on site in the admin view I get

OperationalError at /admin/r/18/1/

no such table: app_1_SomeModel

And I can see with Django-debug-toolbar that this is because it is connected to the default database instead of app_1_db. I have no other issues with routing in the admin app or elsewhere

Change History (4)

comment:1 Changed 11 months ago by zooqooo

Keywords: Routers added; Router removed

comment:2 Changed 11 months ago by Simon Charette

Component: contrib.admincontrib.contenttypes
Triage Stage: UnreviewedAccepted

The issue seems to be in ContentType.get_object_for_this_type which forces the use of self._state.db.

https://github.com/django/django/blob/72ebe85a269aab4bdb3829de4846b41f90973c5d/django/contrib/contenttypes/models.py#L175

I assume your ContentType read database is different from your SomeModel read one?

The using call has been around since multi-db support was added in https://github.com/django/django/commit/ff60c5f9de3e8690d1e86f3e9e3f7248a15397c8#diff-90160d336c65903293180fa2d4a55664R102 10 years ago.

Not sure how to solve this one because routing and contenttypes issues are tricky but tentatively accepting.

comment:3 in reply to:  2 Changed 9 months ago by Rodrigo

JIC, have you defined the models' admins as stated in https://docs.djangoproject.com/en/2.2/topics/db/multi-db/#exposing-multiple-databases-in-django-s-admin-interface ?

Replying to Simon Charette:

The issue seems to be in ContentType.get_object_for_this_type which forces the use of self._state.db.

https://github.com/django/django/blob/72ebe85a269aab4bdb3829de4846b41f90973c5d/django/contrib/contenttypes/models.py#L175

self._state.db seems good to me as it stores in which database the object is located

I assume your ContentType read database is different from your SomeModel read one?

It also seems to me that here is where the problem is, because the admin does not support mult-db out of the box.

JIC, @zooqoo, have you defined the models' admins as stated in https://docs.djangoproject.com/en/2.2/topics/db/multi-db/#exposing-multiple-databases-in-django-s-admin-interface ?

comment:4 Changed 2 weeks ago by Roger Gammans

The root cause of this though, the db forcing in ContentType.get_object_for_this_type( ) effects otehr users of ContentTypes, so its' a content types isses , not really ad admin issue.

The simple fix is to remove the using(), part of the call chain, which was added in for multi db support, but it's not clear , at least to me, why it was considered necessary. This which will work in this case and in the single db case.

Arguably it might cause a problem in some other cases, but in general I can't see ay strong arguments for overriding the db router.

I propose at the least we should have a version of get_object_for_this_type() , and get_alL_objects_for_this_type() which don't include the using clause, however, we may wish to leave the default behaviour as is for backward compatiblity.

Whether that two new functions or we use a kwarg, I don't have s trong opinion myself on.

Note: See TracTickets for help on using tickets.
Back to Top