django_debug_toolbar and Docker

11k views Asked by At

So I got docker and Django to work locally, first by building an image from a Dockerfile, then using Fig to get postgres image, link it to the base image, and run the localserver. Everything works just fine, except for django_debug_toolbar. For some reason it just won't show up. Have the dockerhost ip as well in the internal_ips. Can anyone help me out with it? Docker is running on mac via boot2docker.

Thanks!

My settings:

init.py

import os

if 'DEBUG' not in os.environ or not os.environ['DEBUG']:
    from .local import *
else:
    pass

base.py

""" common and global settings """

from sys import path
from os import environ
from os.path import abspath, basename, dirname, join, normpath
from django.core.exceptions import ImproperlyConfigured
import dj_database_url


def get_env_variable(var_name):
    try:
        return environ[var_name]
    except KeyError:
        error_msg = "Set the environment variable" % var_name
        raise ImproperlyConfigured(error_msg)

# Paths
DJANGO_ROOT = dirname(dirname(abspath(__file__)))
SITE_ROOT = dirname(DJANGO_ROOT)
SITE_NAME = basename(DJANGO_ROOT)
# End Paths


# URLs
MEDIA_ROOT = normpath(join(SITE_ROOT, 'media'))
MEDIA_URL = "/media/"

STATIC_ROOT = normpath(join(SITE_ROOT, 'assets'))
STATIC_URL = "/static/"

STATICFILES_DIRS = (
    normpath(join(SITE_ROOT, 'static')),
)

STATICFILES_FINDERS = (
    'django.contrib.staticfiles.finders.FileSystemFinder',
    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
)

ROOT_URLCONF = '%s.urls' % SITE_NAME

path.insert(0, join(DJANGO_ROOT, 'apps'))  # add apps folder to system path
# End URLs


# Database
# example:  postgres://joris:luna@localhost/bitbybit
DATABASES = {'default': dj_database_url.config(
   default='postgres://postgres@db:5432/postgres')}
# End Database

# Templates
TEMPLATE_CONTEXT_PROCESSORS = (
    'django.contrib.auth.context_processors.auth',
    'django.core.context_processors.debug',
    'django.core.context_processors.i18n',
    'django.core.context_processors.media',
    'django.core.context_processors.static',
    'django.core.context_processors.tz',
    'django.contrib.messages.context_processors.messages',
    'django.core.context_processors.request',
)

TEMPLATE_LOADERS = (
   'django.template.loaders.filesystem.Loader',
   'django.template.loaders.app_directories.Loader',
)

TEMPLATE_DIRS = (
    normpath(join(SITE_ROOT, 'templates')),
)
# End Templates


# SECURITY WARNING: keep the secret key used in production secret!
# make it unique and store it as an environment variable
SECRET_KEY = r"d%g7_h6cz=xbhs*5-i+e$c7mns*s)^_+#^8n@^-2dno@uie-z9"


# Application
DJANGO_APPS = (
   'django.contrib.admin',
   'django.contrib.auth',
   'django.contrib.contenttypes',
   'django.contrib.sessions',
   'django.contrib.messages',
   'django.contrib.staticfiles',
)

LOCAL_APPS = (
   'home',
)

INSTALLED_APPS = DJANGO_APPS + LOCAL_APPS

MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'debug_toolbar.middleware.DebugToolbarMiddleware',
)

WSGI_APPLICATION = '%s.wsgi.application' % SITE_NAME
# End Application


# Internationalization
LANGAUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True
# End Internationalization

Local.py

from .base import *

# Debug config
DEBUG = True
TEMPLATE_DEBUG = DEBUG
# End Debug config

# Hosts
ALLOWED_HOSTS = ['127.0.0.1', 'localhost']
# End Hosts


# Django Debug Toolbar config
INSTALLED_APPS += (
    'debug_toolbar', )

INTERNAL_IPS = ('127.0.0.1', 'localhost')
# End Django Debug Toolbar config
10

There are 10 answers

2
Joris On

Solved. Checked the value for REMOTE_ADDR in request headers and added that to INTERNAL_IPS.

2
Ouss ziat On

Using the configuration SHOW_TOOLBAR_CALLBACK woked for me

def show_toolbar(request):
        return True

DEBUG_TOOLBAR_CONFIG = {
    'SHOW_TOOLBAR_CALLBACK': show_toolbar,
}

I hope that helped :)

0
rxvt On

As people have mentioned, you can use a callback to determine if the debug toolbar should appear or not. I use this together with an environment variable passed in via docker-compose.

settings.py

def show_toolbar_callback(*args, **kwargs):
    if os.getenv("LOCAL_DEV"):
        return True
    return False


DEBUG_TOOLBAR_CONFIG = {"SHOW_TOOLBAR_CALLBACK": show_toolbar_callback}

docker-compose.yaml

services: 
  django:
    container_name: "django"
    build: "./"
    ports: 
      - "8000:8000"
    volumes:
      - ".:/opt/service"
    environment: 
      LOCAL_DEV: "true"
    depends_on: 
      - "db"
1
Ryne Everett On

Here's a hacky way that doesn't require injecting the address into the container. I'm not sure how it would work if your container is connected to multiple networks and could break if docker changed the way they assign addresses (though that doesn't seem likely).

import subprocess

route = subprocess.Popen(('ip', 'route'), stdout=subprocess.PIPE)
network = subprocess.check_output(
    ('grep', '-Po', 'src \K[\d.]+\.'), stdin=route.stdout).decode().rstrip()
route.wait()
network_gateway = network + '1'
INTERNAL_IPS = [network_gateway]
4
Matthias Kestenholz On

You could just make INTERNAL_IPS an object which contains everything. This is what I do:

if DEBUG:
    # `debug` is only True in templates if the vistor IP is in INTERNAL_IPS.
    INTERNAL_IPS = type(str('c'), (), {'__contains__': lambda *a: True})()

Of course you should never do this on a production host!

Explanation:

The type function (three arguments variant: https://docs.python.org/3/library/functions.html#type) creates a new class which in this case only has a __contains__ method (https://docs.python.org/3/reference/datamodel.html#object.contains) -- contains is used to implement membership tests, meaning that this method is called when running e.g. "if ip in INTERNAL_IPS". The contains method itself would probably be clearer if written as "def __contains__(self):\n return True". The newly created class is immediately instantiated (the final "()") and assigned to INTERNAL_IPS

0
Mojtaba Jahannia On

1- run this command in the directory docker-compose or Dockerfile and get NETWORK ID:

sudo docker network list

and view list all network

NETWORK ID     NAME                DRIVER    SCOPE
556e2e7123er   admin_default       bridge    local
aq3033sc09c1   bridge              bridge    local
1983cf1x2cb9   cockroach           bridge    local
0cc1be23b543   flask               bridge    local

2- run this command:

ip addr

.
.

8: br-556e2e7123er: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether **:**:**:**:**:** brd ff:ff:ff:ff:ff:ff
    inet 162.21.0.1/16 brd 172.22.255.255 scope global br-556e2e7123er
       valid_lft forever preferred_lft forever

.
.

3- get your IP

NETWORK ID : 556e2e7123er == 8: br-556e2e7123er => ip 162.21.0.1

4- add ip to INTERNAL_IPS = [..., 162.21.0.1, ...] ‍‍‍‍‍‍‍‍‍‍

1
Mahmoud On

The simplest solution is to use gethostbyname_ex instead of gethostbyname

INTERNAL_IPS = ['127.0.0.1', '10.0.2.2' ]
if DEBUG:    
   import socket
   hostname, _, ips =socket.gethostbyname_ex(socket.gethostname())
   INTERNAL_IPS += [ip[:-1] + '1' for ip in ips]
4
Jordi On

If you would like to do this programatically and not copy/pasting your container IP, I'd suggest you do like the django-cookiecutter folks. In your local settings file:

INTERNAL_IPS = ['127.0.0.1', ]
import socket

# tricks to have debug toolbar when developing with docker
ip = socket.gethostbyname(socket.gethostname())
INTERNAL_IPS += [ip[:-1] + '1']

For reference, this is the link to the django-cookiecutter local.py settings file.

2
Lukasz Dynowski On

An IP address that allowed me to display Django Debug Toolbar was, the Gateway IP of my docker container (container that runs Django app).

To obtain IP address of the gateway run this command.

# replace django_container_name with your container name!
docker inspect django_container_name | grep -e '"Gateway"'
# "Gateway": "172.18.0.1",

Next, add this Getway IP to settings.py -so your configuration might look like the one below.

INSTALLED_APPS = (
    ...
    'debug_toolbar',
    ...
)

MIDDLEWARE = [
    ...
    'debug_toolbar.middleware.DebugToolbarMiddleware',
    ...
]

INTERNAL_IPS = ['172.18.0.1']

And of course add __debug__ to urls.py

import debug_toolbar
from django.conf import settings
from django.urls import include, path

urlpatterns = [
    ...
    path('__debug__/', include(debug_toolbar.urls)),
]

Just follow djdt installation guide!

0
ncoghlan On

Using the accepted answer at https://unix.stackexchange.com/questions/87468/is-there-an-easy-way-to-programmatically-extract-ip-address I was able to get this to work by passing the address of the host's Docker bridge to the docker run command as an environment variable:

-e "DOCKER_HOST=$(ip -4 addr show docker0 | grep -Po 'inet \K[\d.]+')"

With that set, the following two lines in settings.py detected it and allowed the host to see the toolbar:

if "DOCKER_HOST" in os.environ:
    INTERNAL_IPS = [os.environ["DOCKER_HOST"]]