diff options
Diffstat (limited to 'django/core')
-rw-r--r-- | django/core/cache/backends/db.py | 99 | ||||
-rw-r--r-- | django/core/management/__init__.py | 10 | ||||
-rw-r--r-- | django/core/management/base.py | 2 | ||||
-rw-r--r-- | django/core/management/commands/dumpdata.py | 32 | ||||
-rw-r--r-- | django/core/management/commands/flush.py | 10 | ||||
-rw-r--r-- | django/core/management/commands/loaddata.py | 16 | ||||
-rw-r--r-- | django/core/management/commands/syncdb.py | 14 | ||||
-rw-r--r-- | django/core/urlresolvers.py | 43 |
8 files changed, 165 insertions, 61 deletions
diff --git a/django/core/cache/backends/db.py b/django/core/cache/backends/db.py index 3398e6a85b..1300dd4b66 100644 --- a/django/core/cache/backends/db.py +++ b/django/core/cache/backends/db.py @@ -1,7 +1,7 @@ "Database cache backend." from django.core.cache.backends.base import BaseCache -from django.db import connection, transaction, DatabaseError +from django.db import connections, router, transaction, DatabaseError import base64, time from datetime import datetime try: @@ -9,10 +9,31 @@ try: except ImportError: import pickle +class Options(object): + """A class that will quack like a Django model _meta class. + + This allows cache operations to be controlled by the router + """ + def __init__(self, table): + self.db_table = table + self.app_label = 'django_cache' + self.module_name = 'cacheentry' + self.verbose_name = 'cache entry' + self.verbose_name_plural = 'cache entries' + self.object_name = 'CacheEntry' + self.abstract = False + self.managed = True + self.proxy = False + class CacheClass(BaseCache): def __init__(self, table, params): BaseCache.__init__(self, params) - self._table = connection.ops.quote_name(table) + self._table = table + + class CacheEntry(object): + _meta = Options(table) + self.cache_model_class = CacheEntry + max_entries = params.get('max_entries', 300) try: self._max_entries = int(max_entries) @@ -25,17 +46,22 @@ class CacheClass(BaseCache): self._cull_frequency = 3 def get(self, key, default=None): - cursor = connection.cursor() - cursor.execute("SELECT cache_key, value, expires FROM %s WHERE cache_key = %%s" % self._table, [key]) + db = router.db_for_read(self.cache_model_class) + table = connections[db].ops.quote_name(self._table) + cursor = connections[db].cursor() + + cursor.execute("SELECT cache_key, value, expires FROM %s WHERE cache_key = %%s" % table, [key]) row = cursor.fetchone() if row is None: return default now = datetime.now() if row[2] < now: - cursor.execute("DELETE FROM %s WHERE cache_key = %%s" % self._table, [key]) - transaction.commit_unless_managed() + db = router.db_for_write(self.cache_model_class) + cursor = connections[db].cursor() + cursor.execute("DELETE FROM %s WHERE cache_key = %%s" % table, [key]) + transaction.commit_unless_managed(using=db) return default - value = connection.ops.process_clob(row[1]) + value = connections[db].ops.process_clob(row[1]) return pickle.loads(base64.decodestring(value)) def set(self, key, value, timeout=None): @@ -47,56 +73,67 @@ class CacheClass(BaseCache): def _base_set(self, mode, key, value, timeout=None): if timeout is None: timeout = self.default_timeout - cursor = connection.cursor() - cursor.execute("SELECT COUNT(*) FROM %s" % self._table) + db = router.db_for_write(self.cache_model_class) + table = connections[db].ops.quote_name(self._table) + cursor = connections[db].cursor() + + cursor.execute("SELECT COUNT(*) FROM %s" % table) num = cursor.fetchone()[0] now = datetime.now().replace(microsecond=0) exp = datetime.fromtimestamp(time.time() + timeout).replace(microsecond=0) if num > self._max_entries: - self._cull(cursor, now) + self._cull(db, cursor, now) encoded = base64.encodestring(pickle.dumps(value, 2)).strip() - cursor.execute("SELECT cache_key, expires FROM %s WHERE cache_key = %%s" % self._table, [key]) + cursor.execute("SELECT cache_key, expires FROM %s WHERE cache_key = %%s" % table, [key]) try: result = cursor.fetchone() if result and (mode == 'set' or (mode == 'add' and result[1] < now)): - cursor.execute("UPDATE %s SET value = %%s, expires = %%s WHERE cache_key = %%s" % self._table, - [encoded, connection.ops.value_to_db_datetime(exp), key]) + cursor.execute("UPDATE %s SET value = %%s, expires = %%s WHERE cache_key = %%s" % table, + [encoded, connections[db].ops.value_to_db_datetime(exp), key]) else: - cursor.execute("INSERT INTO %s (cache_key, value, expires) VALUES (%%s, %%s, %%s)" % self._table, - [key, encoded, connection.ops.value_to_db_datetime(exp)]) + cursor.execute("INSERT INTO %s (cache_key, value, expires) VALUES (%%s, %%s, %%s)" % table, + [key, encoded, connections[db].ops.value_to_db_datetime(exp)]) except DatabaseError: # To be threadsafe, updates/inserts are allowed to fail silently - transaction.rollback_unless_managed() + transaction.rollback_unless_managed(using=db) return False else: - transaction.commit_unless_managed() + transaction.commit_unless_managed(using=db) return True def delete(self, key): - cursor = connection.cursor() - cursor.execute("DELETE FROM %s WHERE cache_key = %%s" % self._table, [key]) - transaction.commit_unless_managed() + db = router.db_for_write(self.cache_model_class) + table = connections[db].ops.quote_name(self._table) + cursor = connections[db].cursor() + + cursor.execute("DELETE FROM %s WHERE cache_key = %%s" % table, [key]) + transaction.commit_unless_managed(using=db) def has_key(self, key): + db = router.db_for_read(self.cache_model_class) + table = connections[db].ops.quote_name(self._table) + cursor = connections[db].cursor() + now = datetime.now().replace(microsecond=0) - cursor = connection.cursor() - cursor.execute("SELECT cache_key FROM %s WHERE cache_key = %%s and expires > %%s" % self._table, - [key, connection.ops.value_to_db_datetime(now)]) + cursor.execute("SELECT cache_key FROM %s WHERE cache_key = %%s and expires > %%s" % table, + [key, connections[db].ops.value_to_db_datetime(now)]) return cursor.fetchone() is not None - def _cull(self, cursor, now): + def _cull(self, db, cursor, now): if self._cull_frequency == 0: self.clear() else: - cursor.execute("DELETE FROM %s WHERE expires < %%s" % self._table, - [connection.ops.value_to_db_datetime(now)]) - cursor.execute("SELECT COUNT(*) FROM %s" % self._table) + cursor.execute("DELETE FROM %s WHERE expires < %%s" % table, + [connections[db].ops.value_to_db_datetime(now)]) + cursor.execute("SELECT COUNT(*) FROM %s" % table) num = cursor.fetchone()[0] if num > self._max_entries: - cursor.execute("SELECT cache_key FROM %s ORDER BY cache_key LIMIT 1 OFFSET %%s" % self._table, [num / self._cull_frequency]) - cursor.execute("DELETE FROM %s WHERE cache_key < %%s" % self._table, [cursor.fetchone()[0]]) + cursor.execute("SELECT cache_key FROM %s ORDER BY cache_key LIMIT 1 OFFSET %%s" % table, [num / self._cull_frequency]) + cursor.execute("DELETE FROM %s WHERE cache_key < %%s" % table, [cursor.fetchone()[0]]) def clear(self): - cursor = connection.cursor() - cursor.execute('DELETE FROM %s' % self._table) + db = router.db_for_write(self.cache_model_class) + table = connections[db].ops.quote_name(self._table) + cursor = connections[db].cursor() + cursor.execute('DELETE FROM %s' % table) diff --git a/django/core/management/__init__.py b/django/core/management/__init__.py index 32e744374a..85bf324fdc 100644 --- a/django/core/management/__init__.py +++ b/django/core/management/__init__.py @@ -250,15 +250,15 @@ class ManagementUtility(object): """ try: app_name = get_commands()[subcommand] - if isinstance(app_name, BaseCommand): - # If the command is already loaded, use it directly. - klass = app_name - else: - klass = load_command_class(app_name, subcommand) except KeyError: sys.stderr.write("Unknown command: %r\nType '%s help' for usage.\n" % \ (subcommand, self.prog_name)) sys.exit(1) + if isinstance(app_name, BaseCommand): + # If the command is already loaded, use it directly. + klass = app_name + else: + klass = load_command_class(app_name, subcommand) return klass def autocomplete(self): diff --git a/django/core/management/base.py b/django/core/management/base.py index 6761cb69bc..0dc8d0356c 100644 --- a/django/core/management/base.py +++ b/django/core/management/base.py @@ -118,7 +118,7 @@ class BaseCommand(object): # Metadata about this command. option_list = ( make_option('-v', '--verbosity', action='store', dest='verbosity', default='1', - type='choice', choices=['0', '1', '2'], + type='choice', choices=['0', '1', '2', '3'], help='Verbosity level; 0=minimal output, 1=normal output, 2=all output'), make_option('--settings', help='The Python path to a settings module, e.g. "myproject.settings.main". If this isn\'t provided, the DJANGO_SETTINGS_MODULE environment variable will be used.'), diff --git a/django/core/management/commands/dumpdata.py b/django/core/management/commands/dumpdata.py index aaaa5845a5..d2fe6cdd07 100644 --- a/django/core/management/commands/dumpdata.py +++ b/django/core/management/commands/dumpdata.py @@ -16,11 +16,12 @@ class Command(BaseCommand): default=DEFAULT_DB_ALIAS, help='Nominates a specific database to load ' 'fixtures into. Defaults to the "default" database.'), make_option('-e', '--exclude', dest='exclude',action='append', default=[], - help='App to exclude (use multiple --exclude to exclude multiple apps).'), + help='An appname or appname.ModelName to exclude (use multiple --exclude to exclude multiple apps/models).'), make_option('-n', '--natural', action='store_true', dest='use_natural_keys', default=False, help='Use natural keys if they are available.'), ) - help = 'Output the contents of the database as a fixture of the given format.' + help = ("Output the contents of the database as a fixture of the given " + "format (using each model's default manager).") args = '[appname appname.ModelName ...]' def handle(self, *app_labels, **options): @@ -30,11 +31,25 @@ class Command(BaseCommand): indent = options.get('indent',None) using = options.get('database', DEFAULT_DB_ALIAS) connection = connections[using] - exclude = options.get('exclude',[]) + excludes = options.get('exclude',[]) show_traceback = options.get('traceback', False) use_natural_keys = options.get('use_natural_keys', False) - excluded_apps = set(get_app(app_label) for app_label in exclude) + excluded_apps = set() + excluded_models = set() + for exclude in excludes: + if '.' in exclude: + app_label, model_name = exclude.split('.', 1) + model_obj = get_model(app_label, model_name) + if not model_obj: + raise CommandError('Unknown model in excludes: %s' % exclude) + excluded_models.add(model_obj) + else: + try: + app_obj = get_app(exclude) + excluded_apps.add(app_obj) + except ImproperlyConfigured: + raise CommandError('Unknown app in excludes: %s' % exclude) if len(app_labels) == 0: app_list = SortedDict((app, None) for app in get_apps() if app not in excluded_apps) @@ -47,7 +62,8 @@ class Command(BaseCommand): app = get_app(app_label) except ImproperlyConfigured: raise CommandError("Unknown application: %s" % app_label) - + if app in excluded_apps: + continue model = get_model(app_label, model_label) if model is None: raise CommandError("Unknown model: %s.%s" % (app_label, model_label)) @@ -64,6 +80,8 @@ class Command(BaseCommand): app = get_app(app_label) except ImproperlyConfigured: raise CommandError("Unknown application: %s" % app_label) + if app in excluded_apps: + continue app_list[app] = None # Check that the serialization format exists; this is a shortcut to @@ -79,6 +97,8 @@ class Command(BaseCommand): # Now collate the objects to be serialized. objects = [] for model in sort_dependencies(app_list.items()): + if model in excluded_models: + continue if not model._meta.proxy and router.allow_syncdb(using, model): objects.extend(model._default_manager.using(using).all()) @@ -163,4 +183,4 @@ def sort_dependencies(app_list): ) model_dependencies = skipped - return model_list
\ No newline at end of file + return model_list diff --git a/django/core/management/commands/flush.py b/django/core/management/commands/flush.py index 43279c1a13..0ebe0108fb 100644 --- a/django/core/management/commands/flush.py +++ b/django/core/management/commands/flush.py @@ -1,7 +1,7 @@ from optparse import make_option from django.conf import settings -from django.db import connections, transaction, models, DEFAULT_DB_ALIAS +from django.db import connections, router, transaction, models, DEFAULT_DB_ALIAS from django.core.management import call_command from django.core.management.base import NoArgsCommand, CommandError from django.core.management.color import no_style @@ -64,7 +64,13 @@ Are you sure you want to do this? # Emit the post sync signal. This allows individual # applications to respond as if the database had been # sync'd from scratch. - emit_post_sync_signal(models.get_models(), verbosity, interactive, db) + all_models = [] + for app in models.get_apps(): + all_models.extend([ + m for m in models.get_models(app, include_auto_created=True) + if router.allow_syncdb(db, m) + ]) + emit_post_sync_signal(all_models, verbosity, interactive, db) # Reinstall the initial_data fixture. kwargs = options.copy() diff --git a/django/core/management/commands/loaddata.py b/django/core/management/commands/loaddata.py index 5db043e468..cda7daa541 100644 --- a/django/core/management/commands/loaddata.py +++ b/django/core/management/commands/loaddata.py @@ -112,7 +112,7 @@ class Command(BaseCommand): formats = [] if formats: - if verbosity > 1: + if verbosity >= 2: self.stdout.write("Loading '%s' fixtures...\n" % fixture_name) else: sys.stderr.write( @@ -128,7 +128,7 @@ class Command(BaseCommand): fixture_dirs = app_fixtures + list(settings.FIXTURE_DIRS) + [''] for fixture_dir in fixture_dirs: - if verbosity > 1: + if verbosity >= 2: self.stdout.write("Checking %s for fixtures...\n" % humanize(fixture_dir)) label_found = False @@ -141,7 +141,7 @@ class Command(BaseCommand): if p ) - if verbosity > 1: + if verbosity >= 3: self.stdout.write("Trying %s for %s fixture '%s'...\n" % \ (humanize(fixture_dir), file_name, fixture_name)) full_path = os.path.join(fixture_dir, file_name) @@ -158,7 +158,7 @@ class Command(BaseCommand): else: fixture_count += 1 objects_in_fixture = 0 - if verbosity > 0: + if verbosity >= 2: self.stdout.write("Installing %s fixture '%s' from %s.\n" % \ (format, fixture_name, humanize(fixture_dir))) try: @@ -198,7 +198,7 @@ class Command(BaseCommand): return except Exception, e: - if verbosity > 1: + if verbosity >= 2: self.stdout.write("No %s fixture '%s' in %s.\n" % \ (format, fixture_name, humanize(fixture_dir))) @@ -207,7 +207,7 @@ class Command(BaseCommand): if object_count > 0: sequence_sql = connection.ops.sequence_reset_sql(self.style, models) if sequence_sql: - if verbosity > 1: + if verbosity >= 2: self.stdout.write("Resetting sequences\n") for line in sequence_sql: cursor.execute(line) @@ -217,10 +217,10 @@ class Command(BaseCommand): transaction.leave_transaction_management(using=using) if object_count == 0: - if verbosity > 0: + if verbosity >= 1: self.stdout.write("No fixtures found.\n") else: - if verbosity > 0: + if verbosity >= 1: self.stdout.write("Installed %d object(s) from %d fixture(s)\n" % (object_count, fixture_count)) # Close the DB connection. This is required as a workaround for an diff --git a/django/core/management/commands/syncdb.py b/django/core/management/commands/syncdb.py index d1dd49b75e..3c11a263d6 100644 --- a/django/core/management/commands/syncdb.py +++ b/django/core/management/commands/syncdb.py @@ -76,10 +76,12 @@ class Command(NoArgsCommand): ) # Create the tables for each model + if verbosity >= 1: + print "Creating tables ..." for app_name, model_list in manifest.items(): for model in model_list: # Create the model's database table, if it doesn't already exist. - if verbosity >= 2: + if verbosity >= 3: print "Processing %s.%s model" % (app_name, model._meta.object_name) sql, references = connection.creation.sql_create_model(model, self.style, seen_models) seen_models.add(model) @@ -107,12 +109,14 @@ class Command(NoArgsCommand): # Install custom SQL for the app (but only if this # is a model we've just created) + if verbosity >= 1: + print "Installing custom SQL ..." for app_name, model_list in manifest.items(): for model in model_list: if model in created_models: custom_sql = custom_sql_for_model(model, self.style, connection) if custom_sql: - if verbosity >= 1: + if verbosity >= 2: print "Installing custom SQL for %s.%s model" % (app_name, model._meta.object_name) try: for sql in custom_sql: @@ -127,16 +131,18 @@ class Command(NoArgsCommand): else: transaction.commit_unless_managed(using=db) else: - if verbosity >= 2: + if verbosity >= 3: print "No custom SQL for %s.%s model" % (app_name, model._meta.object_name) + if verbosity >= 1: + print "Installing indexes ..." # Install SQL indicies for all newly created models for app_name, model_list in manifest.items(): for model in model_list: if model in created_models: index_sql = connection.creation.sql_indexes_for_model(model, self.style) if index_sql: - if verbosity >= 1: + if verbosity >= 2: print "Installing index for %s.%s model" % (app_name, model._meta.object_name) try: for sql in index_sql: diff --git a/django/core/urlresolvers.py b/django/core/urlresolvers.py index cad57a5d9f..6881bbda9f 100644 --- a/django/core/urlresolvers.py +++ b/django/core/urlresolvers.py @@ -30,6 +30,40 @@ _prefixes = {} # Overridden URLconfs for each thread are stored here. _urlconfs = {} +class ResolverMatch(object): + def __init__(self, func, args, kwargs, url_name=None, app_name=None, namespaces=None): + self.func = func + self.args = args + self.kwargs = kwargs + self.app_name = app_name + if namespaces: + self.namespaces = [x for x in namespaces if x] + else: + self.namespaces = [] + if not url_name: + if not hasattr(func, '__name__'): + # An instance of a callable class + url_name = '.'.join([func.__class__.__module__, func.__class__.__name__]) + else: + # A function + url_name = '.'.join([func.__module__, func.__name__]) + self.url_name = url_name + + def namespace(self): + return ':'.join(self.namespaces) + namespace = property(namespace) + + def view_name(self): + return ':'.join([ x for x in [ self.namespace, self.url_name ] if x ]) + view_name = property(view_name) + + def __getitem__(self, index): + return (self.func, self.args, self.kwargs)[index] + + def __repr__(self): + return "ResolverMatch(func=%s, args=%s, kwargs=%s, url_name='%s', app_name='%s', namespace='%s')" % ( + self.func, self.args, self.kwargs, self.url_name, self.app_name, self.namespace) + class Resolver404(Http404): pass @@ -120,7 +154,7 @@ class RegexURLPattern(object): # In both cases, pass any extra_kwargs as **kwargs. kwargs.update(self.default_args) - return self.callback, args, kwargs + return ResolverMatch(self.callback, args, kwargs, self.name) def _get_callback(self): if self._callback is not None: @@ -183,7 +217,8 @@ class RegexURLResolver(object): else: bits = normalize(p_pattern) lookups.appendlist(pattern.callback, (bits, p_pattern)) - lookups.appendlist(pattern.name, (bits, p_pattern)) + if pattern.name is not None: + lookups.appendlist(pattern.name, (bits, p_pattern)) self._reverse_dict = lookups self._namespace_dict = namespaces self._app_dict = apps @@ -224,9 +259,9 @@ class RegexURLResolver(object): if sub_match: sub_match_dict = dict([(smart_str(k), v) for k, v in match.groupdict().items()]) sub_match_dict.update(self.default_kwargs) - for k, v in sub_match[2].iteritems(): + for k, v in sub_match.kwargs.iteritems(): sub_match_dict[smart_str(k)] = v - return sub_match[0], sub_match[1], sub_match_dict + return ResolverMatch(sub_match.func, sub_match.args, sub_match_dict, sub_match.url_name, self.app_name or sub_match.app_name, [self.namespace] + sub_match.namespaces) tried.append(pattern.regex.pattern) raise Resolver404({'tried': tried, 'path': new_path}) raise Resolver404({'path' : path}) |