Fixed #15888 -- Made tablename argument of createcachetable optional
Thanks Aymeric Augustin for the report and the documentation and Tim Graham for the review.
This commit is contained in:
parent
b600bb7e08
commit
1e8eadc94e
|
@ -1,8 +1,6 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.cache import get_cache
|
|
||||||
from django.core.cache.backends.db import BaseDatabaseCache
|
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.db.backends.sqlite3.creation import DatabaseCreation
|
from django.db.backends.sqlite3.creation import DatabaseCreation
|
||||||
|
|
||||||
|
@ -55,10 +53,7 @@ class SpatiaLiteCreation(DatabaseCreation):
|
||||||
interactive=False,
|
interactive=False,
|
||||||
database=self.connection.alias)
|
database=self.connection.alias)
|
||||||
|
|
||||||
for cache_alias in settings.CACHES:
|
call_command('createcachetable', database=self.connection.alias)
|
||||||
cache = get_cache(cache_alias)
|
|
||||||
if isinstance(cache, BaseDatabaseCache):
|
|
||||||
call_command('createcachetable', cache._table, database=self.connection.alias)
|
|
||||||
|
|
||||||
# Get a cursor (even though we don't need one yet). This has
|
# Get a cursor (even though we don't need one yet). This has
|
||||||
# the side effect of initializing the test database.
|
# the side effect of initializing the test database.
|
||||||
|
|
|
@ -1,32 +1,50 @@
|
||||||
from optparse import make_option
|
from optparse import make_option
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.core.cache import get_cache
|
||||||
from django.core.cache.backends.db import BaseDatabaseCache
|
from django.core.cache.backends.db import BaseDatabaseCache
|
||||||
from django.core.management.base import LabelCommand, CommandError
|
from django.core.management.base import BaseCommand, CommandError
|
||||||
from django.db import connections, router, transaction, models, DEFAULT_DB_ALIAS
|
from django.db import connections, router, transaction, models, DEFAULT_DB_ALIAS
|
||||||
from django.db.utils import DatabaseError
|
from django.db.utils import DatabaseError
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
|
|
||||||
|
|
||||||
class Command(LabelCommand):
|
class Command(BaseCommand):
|
||||||
help = "Creates the table needed to use the SQL cache backend."
|
help = "Creates the tables needed to use the SQL cache backend."
|
||||||
args = "<tablename>"
|
|
||||||
label = 'tablename'
|
|
||||||
|
|
||||||
option_list = LabelCommand.option_list + (
|
option_list = BaseCommand.option_list + (
|
||||||
make_option('--database', action='store', dest='database',
|
make_option('--database', action='store', dest='database',
|
||||||
default=DEFAULT_DB_ALIAS, help='Nominates a database onto '
|
default=DEFAULT_DB_ALIAS, help='Nominates a database onto '
|
||||||
'which the cache table will be installed. '
|
'which the cache tables will be installed. '
|
||||||
'Defaults to the "default" database.'),
|
'Defaults to the "default" database.'),
|
||||||
)
|
)
|
||||||
|
|
||||||
requires_model_validation = False
|
requires_model_validation = False
|
||||||
|
|
||||||
def handle_label(self, tablename, **options):
|
def handle(self, *tablenames, **options):
|
||||||
db = options.get('database')
|
db = options.get('database')
|
||||||
|
self.verbosity = int(options.get('verbosity'))
|
||||||
|
if len(tablenames):
|
||||||
|
# Legacy behavior, tablename specified as argument
|
||||||
|
for tablename in tablenames:
|
||||||
|
self.create_table(db, tablename)
|
||||||
|
else:
|
||||||
|
for cache_alias in settings.CACHES:
|
||||||
|
cache = get_cache(cache_alias)
|
||||||
|
if isinstance(cache, BaseDatabaseCache):
|
||||||
|
self.create_table(db, cache._table)
|
||||||
|
|
||||||
|
def create_table(self, database, tablename):
|
||||||
cache = BaseDatabaseCache(tablename, {})
|
cache = BaseDatabaseCache(tablename, {})
|
||||||
if not router.allow_migrate(db, cache.cache_model_class):
|
if not router.allow_migrate(database, cache.cache_model_class):
|
||||||
return
|
return
|
||||||
connection = connections[db]
|
connection = connections[database]
|
||||||
|
|
||||||
|
if tablename in connection.introspection.table_names():
|
||||||
|
if self.verbosity > 0:
|
||||||
|
self.stdout.write("Cache table '%s' already exists." % tablename)
|
||||||
|
return
|
||||||
|
|
||||||
fields = (
|
fields = (
|
||||||
# "key" is a reserved word in MySQL, so use "cache_key" instead.
|
# "key" is a reserved word in MySQL, so use "cache_key" instead.
|
||||||
models.CharField(name='cache_key', max_length=255, unique=True, primary_key=True),
|
models.CharField(name='cache_key', max_length=255, unique=True, primary_key=True),
|
||||||
|
@ -63,3 +81,5 @@ class Command(LabelCommand):
|
||||||
(tablename, force_text(e)))
|
(tablename, force_text(e)))
|
||||||
for statement in index_output:
|
for statement in index_output:
|
||||||
curs.execute(statement)
|
curs.execute(statement)
|
||||||
|
if self.verbosity > 1:
|
||||||
|
self.stdout.write("Cache table '%s' created." % tablename)
|
||||||
|
|
|
@ -356,13 +356,7 @@ class BaseDatabaseCreation(object):
|
||||||
interactive=False,
|
interactive=False,
|
||||||
database=self.connection.alias)
|
database=self.connection.alias)
|
||||||
|
|
||||||
from django.core.cache import get_cache
|
call_command('createcachetable', database=self.connection.alias)
|
||||||
from django.core.cache.backends.db import BaseDatabaseCache
|
|
||||||
for cache_alias in settings.CACHES:
|
|
||||||
cache = get_cache(cache_alias)
|
|
||||||
if isinstance(cache, BaseDatabaseCache):
|
|
||||||
call_command('createcachetable', cache._table,
|
|
||||||
database=self.connection.alias)
|
|
||||||
|
|
||||||
# Get a cursor (even though we don't need one yet). This has
|
# Get a cursor (even though we don't need one yet). This has
|
||||||
# the side effect of initializing the test database.
|
# the side effect of initializing the test database.
|
||||||
|
|
|
@ -131,12 +131,19 @@ createcachetable
|
||||||
|
|
||||||
.. django-admin:: createcachetable
|
.. django-admin:: createcachetable
|
||||||
|
|
||||||
Creates a cache table named ``tablename`` for use with the database cache
|
Creates the cache tables for use with the database cache backend. See
|
||||||
backend. See :doc:`/topics/cache` for more information.
|
:doc:`/topics/cache` for more information.
|
||||||
|
|
||||||
The :djadminopt:`--database` option can be used to specify the database
|
The :djadminopt:`--database` option can be used to specify the database
|
||||||
onto which the cachetable will be installed.
|
onto which the cachetable will be installed.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.7
|
||||||
|
|
||||||
|
It is no longer necessary to provide the cache table name or the
|
||||||
|
:djadminopt:`--database` option. Django takes this information from your
|
||||||
|
settings file. If you have configured multiple caches or multiple databases,
|
||||||
|
all cache tables are created.
|
||||||
|
|
||||||
dbshell
|
dbshell
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
|
|
@ -305,6 +305,11 @@ Management Commands
|
||||||
``use_natural_primary_keys`` arguments for ``serializers.serialize()``, allow
|
``use_natural_primary_keys`` arguments for ``serializers.serialize()``, allow
|
||||||
the use of natural primary keys when serializing.
|
the use of natural primary keys when serializing.
|
||||||
|
|
||||||
|
* It is no longer necessary to provide the cache table name or the
|
||||||
|
:djadminopt:`--database` option for the :djadmin:`createcachetable` command.
|
||||||
|
Django takes this information from your settings file. If you have configured
|
||||||
|
multiple caches or multiple databases, all cache tables are created.
|
||||||
|
|
||||||
Models
|
Models
|
||||||
^^^^^^
|
^^^^^^
|
||||||
|
|
||||||
|
|
|
@ -159,22 +159,18 @@ particularly temporary.
|
||||||
Database caching
|
Database caching
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
To use a database table as your cache backend, first create a cache table in
|
Django can store its cached data in your database. This works best if you've
|
||||||
your database by running this command::
|
got a fast, well-indexed database server.
|
||||||
|
|
||||||
$ python manage.py createcachetable [cache_table_name]
|
To use a database table as your cache backend:
|
||||||
|
|
||||||
...where ``[cache_table_name]`` is the name of the database table to create.
|
* Set :setting:`BACKEND <CACHES-BACKEND>` to
|
||||||
(This name can be whatever you want, as long as it's a valid table name that's
|
``django.core.cache.backends.db.DatabaseCache``
|
||||||
not already being used in your database.) This command creates a single table
|
* Set :setting:`LOCATION <CACHES-LOCATION>` to ``tablename``, the name of
|
||||||
in your database that is in the proper format that Django's database-cache
|
the database table. This name can be whatever you want, as long as it's
|
||||||
system expects.
|
a valid table name that's not already being used in your database.
|
||||||
|
|
||||||
Once you've created that database table, set your
|
In this example, the cache table's name is ``my_cache_table``::
|
||||||
:setting:`BACKEND <CACHES-BACKEND>` setting to
|
|
||||||
``"django.core.cache.backends.db.DatabaseCache"``, and
|
|
||||||
:setting:`LOCATION <CACHES-LOCATION>` to ``tablename`` -- the name of the
|
|
||||||
database table. In this example, the cache table's name is ``my_cache_table``::
|
|
||||||
|
|
||||||
CACHES = {
|
CACHES = {
|
||||||
'default': {
|
'default': {
|
||||||
|
@ -183,14 +179,36 @@ database table. In this example, the cache table's name is ``my_cache_table``::
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Creating the cache table
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
The database caching backend uses the same database as specified in your
|
Before using the database cache, you must create the cache table with this
|
||||||
settings file. You can't use a different database backend for your cache table.
|
command::
|
||||||
|
|
||||||
Database caching works best if you've got a fast, well-indexed database server.
|
python manage.py createcachetable
|
||||||
|
|
||||||
Database caching and multiple databases
|
This creates a table in your database that is in the proper format that
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
Django's database-cache system expects. The name of the table is taken from
|
||||||
|
:setting:`LOCATION <CACHES-LOCATION>`.
|
||||||
|
|
||||||
|
If you are using multiple database caches, :djadmin:`createcachetable` creates
|
||||||
|
one table for each cache.
|
||||||
|
|
||||||
|
If you are using multiple databases, :djadmin:`createcachetable` observes the
|
||||||
|
``allow_migrate()`` method of your database routers (see below).
|
||||||
|
|
||||||
|
Like :djadmin:`migrate`, :djadmin:`createcachetable` won't touch an existing
|
||||||
|
table. It will only create missing tables.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.7
|
||||||
|
|
||||||
|
Before Django 1.7, :djadmin:`createcachetable` created one table at a time.
|
||||||
|
You had to pass the name of the table you wanted to create, and if you were
|
||||||
|
using multiple databases, you had to use the :djadminopt:`--database`
|
||||||
|
option. For backwards compatibility, this is still possible.
|
||||||
|
|
||||||
|
Multiple databases
|
||||||
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
If you use database caching with multiple databases, you'll also need
|
If you use database caching with multiple databases, you'll also need
|
||||||
to set up routing instructions for your database cache table. For the
|
to set up routing instructions for your database cache table. For the
|
||||||
|
|
|
@ -20,7 +20,7 @@ from django.core import management
|
||||||
from django.core.cache import get_cache
|
from django.core.cache import get_cache
|
||||||
from django.core.cache.backends.base import (CacheKeyWarning,
|
from django.core.cache.backends.base import (CacheKeyWarning,
|
||||||
InvalidCacheBackendError)
|
InvalidCacheBackendError)
|
||||||
from django.db import router, transaction
|
from django.db import connection, router, transaction
|
||||||
from django.core.cache.utils import make_template_fragment_key
|
from django.core.cache.utils import make_template_fragment_key
|
||||||
from django.http import (HttpResponse, HttpRequest, StreamingHttpResponse,
|
from django.http import (HttpResponse, HttpRequest, StreamingHttpResponse,
|
||||||
QueryDict)
|
QueryDict)
|
||||||
|
@ -829,6 +829,14 @@ def custom_key_func(key, key_prefix, version):
|
||||||
return 'CUSTOM-' + '-'.join([key_prefix, str(version), key])
|
return 'CUSTOM-' + '-'.join([key_prefix, str(version), key])
|
||||||
|
|
||||||
|
|
||||||
|
@override_settings(
|
||||||
|
CACHES={
|
||||||
|
'default': {
|
||||||
|
'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
|
||||||
|
'LOCATION': 'test cache table',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
class DBCacheTests(BaseCacheTests, TransactionTestCase):
|
class DBCacheTests(BaseCacheTests, TransactionTestCase):
|
||||||
|
|
||||||
available_apps = ['cache']
|
available_apps = ['cache']
|
||||||
|
@ -837,7 +845,7 @@ class DBCacheTests(BaseCacheTests, TransactionTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
# Spaces are used in the table name to ensure quoting/escaping is working
|
# Spaces are used in the table name to ensure quoting/escaping is working
|
||||||
self._table_name = 'test cache table'
|
self._table_name = 'test cache table'
|
||||||
management.call_command('createcachetable', self._table_name, verbosity=0, interactive=False)
|
management.call_command('createcachetable', verbosity=0, interactive=False)
|
||||||
self.cache = get_cache(self.backend_name, LOCATION=self._table_name, OPTIONS={'MAX_ENTRIES': 30})
|
self.cache = get_cache(self.backend_name, LOCATION=self._table_name, OPTIONS={'MAX_ENTRIES': 30})
|
||||||
self.prefix_cache = get_cache(self.backend_name, LOCATION=self._table_name, KEY_PREFIX='cacheprefix')
|
self.prefix_cache = get_cache(self.backend_name, LOCATION=self._table_name, KEY_PREFIX='cacheprefix')
|
||||||
self.v2_cache = get_cache(self.backend_name, LOCATION=self._table_name, VERSION=2)
|
self.v2_cache = get_cache(self.backend_name, LOCATION=self._table_name, VERSION=2)
|
||||||
|
@ -845,7 +853,6 @@ class DBCacheTests(BaseCacheTests, TransactionTestCase):
|
||||||
self.custom_key_cache2 = get_cache(self.backend_name, LOCATION=self._table_name, KEY_FUNCTION='cache.tests.custom_key_func')
|
self.custom_key_cache2 = get_cache(self.backend_name, LOCATION=self._table_name, KEY_FUNCTION='cache.tests.custom_key_func')
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
from django.db import connection
|
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
cursor.execute('DROP TABLE %s' % connection.ops.quote_name(self._table_name))
|
cursor.execute('DROP TABLE %s' % connection.ops.quote_name(self._table_name))
|
||||||
connection.commit()
|
connection.commit()
|
||||||
|
@ -858,14 +865,29 @@ class DBCacheTests(BaseCacheTests, TransactionTestCase):
|
||||||
self.perform_cull_test(50, 18)
|
self.perform_cull_test(50, 18)
|
||||||
|
|
||||||
def test_second_call_doesnt_crash(self):
|
def test_second_call_doesnt_crash(self):
|
||||||
with six.assertRaisesRegex(self, management.CommandError,
|
stdout = six.StringIO()
|
||||||
"Cache table 'test cache table' could not be created"):
|
management.call_command(
|
||||||
management.call_command(
|
'createcachetable',
|
||||||
'createcachetable',
|
stdout=stdout
|
||||||
self._table_name,
|
)
|
||||||
verbosity=0,
|
self.assertEqual(stdout.getvalue(),
|
||||||
interactive=False
|
"Cache table '%s' already exists.\n" % self._table_name)
|
||||||
)
|
|
||||||
|
def test_createcachetable_with_table_argument(self):
|
||||||
|
"""
|
||||||
|
Delete and recreate cache table with legacy behavior (explicitly
|
||||||
|
specifying the table name).
|
||||||
|
"""
|
||||||
|
self.tearDown()
|
||||||
|
stdout = six.StringIO()
|
||||||
|
management.call_command(
|
||||||
|
'createcachetable',
|
||||||
|
self._table_name,
|
||||||
|
verbosity=2,
|
||||||
|
stdout=stdout
|
||||||
|
)
|
||||||
|
self.assertEqual(stdout.getvalue(),
|
||||||
|
"Cache table '%s' created.\n" % self._table_name)
|
||||||
|
|
||||||
def test_clear_commits_transaction(self):
|
def test_clear_commits_transaction(self):
|
||||||
# Ensure the database transaction is committed (#19896)
|
# Ensure the database transaction is committed (#19896)
|
||||||
|
@ -896,6 +918,14 @@ class DBCacheRouter(object):
|
||||||
return db == 'other'
|
return db == 'other'
|
||||||
|
|
||||||
|
|
||||||
|
@override_settings(
|
||||||
|
CACHES={
|
||||||
|
'default': {
|
||||||
|
'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
|
||||||
|
'LOCATION': 'my_cache_table',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
class CreateCacheTableForDBCacheTests(TestCase):
|
class CreateCacheTableForDBCacheTests(TestCase):
|
||||||
multi_db = True
|
multi_db = True
|
||||||
|
|
||||||
|
@ -905,13 +935,16 @@ class CreateCacheTableForDBCacheTests(TestCase):
|
||||||
router.routers = [DBCacheRouter()]
|
router.routers = [DBCacheRouter()]
|
||||||
# cache table should not be created on 'default'
|
# cache table should not be created on 'default'
|
||||||
with self.assertNumQueries(0, using='default'):
|
with self.assertNumQueries(0, using='default'):
|
||||||
management.call_command('createcachetable', 'cache_table',
|
management.call_command('createcachetable',
|
||||||
database='default',
|
database='default',
|
||||||
verbosity=0, interactive=False)
|
verbosity=0, interactive=False)
|
||||||
# cache table should be created on 'other'
|
# cache table should be created on 'other'
|
||||||
# one query is used to create the table and another one the index
|
# Queries:
|
||||||
with self.assertNumQueries(2, using='other'):
|
# 1: check table doesn't already exist
|
||||||
management.call_command('createcachetable', 'cache_table',
|
# 2: create the table
|
||||||
|
# 3: create the index
|
||||||
|
with self.assertNumQueries(3, using='other'):
|
||||||
|
management.call_command('createcachetable',
|
||||||
database='other',
|
database='other',
|
||||||
verbosity=0, interactive=False)
|
verbosity=0, interactive=False)
|
||||||
finally:
|
finally:
|
||||||
|
|
Loading…
Reference in New Issue