diff --git a/django/conf/admin_media/css/global.css b/django/conf/admin_media/css/global.css index 31156589e0..7c60bf9755 100644 --- a/django/conf/admin_media/css/global.css +++ b/django/conf/admin_media/css/global.css @@ -230,6 +230,7 @@ fieldset.collapsed h2, fieldset.collapsed { display:block !important; } fieldset.collapsed .collapse-toggle { display: inline !important; } fieldset.collapse h2 a.collapse-toggle { color:#ffc; } fieldset.collapse h2 a.collapse-toggle:hover { text-decoration:underline; } +.hidden { display:none; } /* MESSAGES & ERRORS */ @@ -348,7 +349,7 @@ p.file-upload { line-height:20px; margin:0; padding:0; color:#666; font-size:11p ul.timelist, .timelist li { list-style-type:none; margin:0; padding:0; } .timelist a { padding:2px; } -/* OLD ORDERING WIDGET */ +/* ORDERING WIDGET */ ul#orderthese { padding:0; margin:0; list-style-type:none; } ul#orderthese li { list-style-type:none; display:block; padding:0; margin:6px 0; width:214px; background:#f6f6f6; white-space:nowrap; overflow:hidden; } diff --git a/django/conf/admin_templates/admin_change_form.html b/django/conf/admin_templates/admin_change_form.html index 6efd0e82b9..120ebd29bd 100644 --- a/django/conf/admin_templates/admin_change_form.html +++ b/django/conf/admin_templates/admin_change_form.html @@ -2,29 +2,19 @@ {% load admin_modify %} {% load adminmedia %} {% block extrahead %} - {% for js in javascript_imports %} {% include_admin_script js %} {% endfor %} - {% endblock %} - {% block coltype %}{{ coltype }}{% endblock %} - {% block bodyclass %}{{app_label}}-{{object_name.lower}} change-form{% endblock %} - {% block breadcrumbs %}{% if not is_popup %} -{% endif %} -{% endblock %} +{% endif %}{% endblock %} {% block content %}
{% if change %} @@ -82,7 +72,7 @@ {% submit_row %} {% if add %} - ' + {% endif %} {% if auto_populated_fields %} diff --git a/django/conf/admin_templates/admin_field_line.html b/django/conf/admin_templates/admin_field_line.html index 1b3903b71e..cbcac1588c 100644 --- a/django/conf/admin_templates/admin_field_line.html +++ b/django/conf/admin_templates/admin_field_line.html @@ -1,36 +1,21 @@
- {% for bound_field in bound_fields %} - {{ bound_field.html_error_list }} - {% endfor %} - - {% for bound_field in bound_fields %} - {% if bound_field.has_label_first %} - {% field_label bound_field %} - {% endif %} - - {% field_widget bound_field %} - - {% if not bound_field.has_label_first %} - {% field_label bound_field %} - {% endif %} - - {% if change %} - {% if bound_field.field.primary_key %} - {{ bound_field.original_value }} - {% endif %} - - {% if bound_field.raw_id_admin %} - {% if bound_field.existing_repr %} -  {{ bound_field.existing_repr|truncatewords:"14" }} - {% endif %} - {% endif %} - {% endif %} - - {% if bound_field.field.help_text %} -

- {{bound_field.field.help_text}} -

- {% endif %} - - {% endfor %} +{% for bound_field in bound_fields %}{{ bound_field.html_error_list }}{% endfor %} +{% for bound_field in bound_fields %} + {% if bound_field.has_label_first %} + {% field_label bound_field %} + {% endif %} + {% field_widget bound_field %} + {% if not bound_field.has_label_first %} + {% field_label bound_field %} + {% endif %} + {% if change %} + {% if bound_field.field.primary_key %} + {{ bound_field.original_value }} + {% endif %} + {% if bound_field.raw_id_admin %} + {% if bound_field.existing_repr %} {{ bound_field.existing_repr|truncatewords:"14" }}{% endif %} + {% endif %} + {% endif %} + {% if bound_field.field.help_text %}

{{bound_field.field.help_text}}

{% endif %} +{% endfor %}
diff --git a/django/core/cache.py b/django/core/cache.py index cbf02f2f3b..6391304158 100644 --- a/django/core/cache.py +++ b/django/core/cache.py @@ -15,7 +15,7 @@ The CACHE_BACKEND setting is a quasi-URI; examples are: memcached://127.0.0.1:11211/ A memcached backend; the server is running on localhost port 11211. - db://tablename/ A database backend in a table named + db://tablename/ A database backend in a table named "tablename". This table should be created with "django-admin createcachetable". @@ -26,7 +26,7 @@ The CACHE_BACKEND setting is a quasi-URI; examples are: probably don't want to use this except for testing. Note that this cache backend is NOT threadsafe! - + locmem:/// A more sophisticaed local memory cache; this is multi-process- and thread-safe. @@ -72,7 +72,6 @@ class InvalidCacheBackendError(Exception): ################################ class _Cache: - def __init__(self, params): timeout = params.get('timeout', 300) try: @@ -132,8 +131,7 @@ except ImportError: _MemcachedCache = None else: class _MemcachedCache(_Cache): - """Memcached cache backend.""" - + "Memcached cache backend." def __init__(self, server, params): _Cache.__init__(self, params) self._cache = memcache.Client([server]) @@ -161,8 +159,7 @@ else: import time class _SimpleCache(_Cache): - """Simple single-process in-memory cache""" - + "Simple single-process in-memory cache." def __init__(self, host, params): _Cache.__init__(self, params) self._cache = {} @@ -230,11 +227,11 @@ try: import cPickle as pickle except ImportError: import pickle +import copy from django.utils.synch import RWLock class _LocMemCache(_SimpleCache): - """Thread-safe in-memory cache""" - + "Thread-safe in-memory cache." def __init__(self, host, params): _SimpleCache.__init__(self, host, params) self._lock = RWLock() @@ -250,7 +247,7 @@ class _LocMemCache(_SimpleCache): elif exp < now: should_delete = True else: - return self._cache[key] + return copy.deepcopy(self._cache[key]) finally: self._lock.reader_leaves() if should_delete: @@ -261,14 +258,14 @@ class _LocMemCache(_SimpleCache): return default finally: self._lock.writer_leaves() - + def set(self, key, value, timeout=None): self._lock.writer_enters() try: _SimpleCache.set(self, key, value, timeout) finally: self._lock.writer_leaves() - + def delete(self, key): self._lock.writer_enters() try: @@ -284,8 +281,7 @@ import os import urllib class _FileCache(_SimpleCache): - """File-based cache""" - + "File-based cache." def __init__(self, dir, params): self._dir = dir if not os.path.exists(self._dir): @@ -293,7 +289,7 @@ class _FileCache(_SimpleCache): _SimpleCache.__init__(self, dir, params) del self._cache del self._expire_info - + def get(self, key, default=None): fname = self._key_to_file(key) try: @@ -308,7 +304,7 @@ class _FileCache(_SimpleCache): except (IOError, pickle.PickleError): pass return default - + def set(self, key, value, timeout=None): fname = self._key_to_file(key) if timeout is None: @@ -327,16 +323,16 @@ class _FileCache(_SimpleCache): pickle.dump(value, f, 2) except (IOError, OSError): raise - + def delete(self, key): try: os.remove(self._key_to_file(key)) except (IOError, OSError): pass - + def has_key(self, key): return os.path.exists(self._key_to_file(key)) - + def _cull(self, filelist): if self.cull_frequency == 0: doomed = filelist @@ -348,7 +344,7 @@ class _FileCache(_SimpleCache): except (IOError, OSError): pass - def _createdir(self): + def _createdir(self): try: os.makedirs(self._dir) except OSError: @@ -366,22 +362,21 @@ from django.core.db import db, DatabaseError from datetime import datetime class _DBCache(_Cache): - """SQL cache backend""" - + "SQL cache backend." def __init__(self, table, params): _Cache.__init__(self, params) self._table = table - max_entries = params.get('max_entries', 300) - try: - self._max_entries = int(max_entries) - except (ValueError, TypeError): - self._max_entries = 300 - cull_frequency = params.get('cull_frequency', 3) - try: - self._cull_frequency = int(cull_frequency) - except (ValueError, TypeError): - self._cull_frequency = 3 - + max_entries = params.get('max_entries', 300) + try: + self._max_entries = int(max_entries) + except (ValueError, TypeError): + self._max_entries = 300 + cull_frequency = params.get('cull_frequency', 3) + try: + self._cull_frequency = int(cull_frequency) + except (ValueError, TypeError): + self._cull_frequency = 3 + def get(self, key, default=None): cursor = db.cursor() cursor.execute("SELECT cache_key, value, expires FROM %s WHERE cache_key = %%s" % self._table, [key]) @@ -394,7 +389,7 @@ class _DBCache(_Cache): db.commit() return default return pickle.loads(base64.decodestring(row[1])) - + def set(self, key, value, timeout=None): if timeout is None: timeout = self.default_timeout @@ -417,17 +412,17 @@ class _DBCache(_Cache): pass else: db.commit() - + def delete(self, key): cursor = db.cursor() cursor.execute("DELETE FROM %s WHERE cache_key = %%s" % self._table, [key]) db.commit() - + def has_key(self, key): cursor = db.cursor() cursor.execute("SELECT cache_key FROM %s WHERE cache_key = %%s" % self._table, [key]) return cursor.fetchone() is not None - + def _cull(self, cursor, now): if self._cull_frequency == 0: cursor.execute("DELETE FROM %s" % self._table) @@ -438,7 +433,7 @@ class _DBCache(_Cache): 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]]) - + ########################################## # Read settings and load a cache backend # ########################################## diff --git a/django/core/db/backends/mysql.py b/django/core/db/backends/mysql.py index 2e77adbc43..af0dbca6c0 100644 --- a/django/core/db/backends/mysql.py +++ b/django/core/db/backends/mysql.py @@ -143,6 +143,7 @@ DATA_TYPES = { 'DateTimeField': 'datetime', 'EmailField': 'varchar(75)', 'FileField': 'varchar(100)', + 'FilePathField': 'varchar(100)', 'FloatField': 'numeric(%(max_digits)s, %(decimal_places)s)', 'ImageField': 'varchar(100)', 'IntegerField': 'integer', diff --git a/django/core/db/backends/postgresql.py b/django/core/db/backends/postgresql.py index 683ae3c9ee..6ec7bfbfcb 100644 --- a/django/core/db/backends/postgresql.py +++ b/django/core/db/backends/postgresql.py @@ -154,6 +154,7 @@ DATA_TYPES = { 'DateTimeField': 'timestamp with time zone', 'EmailField': 'varchar(75)', 'FileField': 'varchar(100)', + 'FilePathField': 'varchar(100)', 'FloatField': 'numeric(%(max_digits)s, %(decimal_places)s)', 'ImageField': 'varchar(100)', 'IntegerField': 'integer', diff --git a/django/core/db/backends/sqlite3.py b/django/core/db/backends/sqlite3.py index d4b936f82e..ea05302a61 100644 --- a/django/core/db/backends/sqlite3.py +++ b/django/core/db/backends/sqlite3.py @@ -154,6 +154,7 @@ DATA_TYPES = { 'DateTimeField': 'datetime', 'EmailField': 'varchar(75)', 'FileField': 'varchar(100)', + 'FilePathField': 'varchar(100)', 'FloatField': 'numeric(%(max_digits)s, %(decimal_places)s)', 'ImageField': 'varchar(100)', 'IntegerField': 'integer', diff --git a/django/core/formfields.py b/django/core/formfields.py index ca35957a5e..03760e0e19 100644 --- a/django/core/formfields.py +++ b/django/core/formfields.py @@ -822,6 +822,29 @@ class IPAddressField(TextField): # MISCELLANEOUS # #################### +class FilePathField(SelectField): + "A SelectField whose choices are the files in a given directory." + def __init__(self, field_name, path, match=None, recursive=False, is_required=False, validator_list=[]): + import os + if match is not None: + import re + match_re = re.compile(match) + choices = [] + if recursive: + for root, dirs, files in os.walk(path): + for f in files: + if match is None or match_re.search(f): + choices.append((os.path.join(path, f), f)) + else: + try: + for f in os.listdir(path): + full_file = os.path.join(path, f) + if os.path.isfile(full_file) and (match is None or match_re.search(f)): + choices.append((full_file, f)) + except OSError: + pass + SelectField.__init__(self, field_name, choices, 1, is_required, validator_list) + class PhoneNumberField(TextField): "A convenience FormField for validating phone numbers (e.g. '630-555-1234')" def __init__(self, field_name, is_required=False, validator_list=[]): diff --git a/django/core/management.py b/django/core/management.py index afb498ae63..49f447cc74 100644 --- a/django/core/management.py +++ b/django/core/management.py @@ -144,6 +144,10 @@ def get_sql_delete(mod): for row in cursor.fetchall(): output.append("DELETE FROM auth_admin_log WHERE content_type_id = %s;" % row[0]) + # Close database connection explicitly, in case this output is being piped + # directly into a database client, to avoid locking issues. + db.db.close() + return output[::-1] # Reverse it, to deal with table dependencies. get_sql_delete.help_doc = "Prints the DROP TABLE SQL statements for the given model module name(s)." get_sql_delete.args = APP_ARGS @@ -636,8 +640,9 @@ def runserver(addr, port): sys.exit(1) except KeyboardInterrupt: sys.exit(0) - from django.utils import autoreload - autoreload.main(inner_run) + #from django.utils import autoreload + #autoreload.main(inner_run) + inner_run() runserver.args = '[optional port number, or ipaddr:port]' def createcachetable(tablename): diff --git a/django/core/meta/__init__.py b/django/core/meta/__init__.py index 2670a36b52..7f32913fbc 100644 --- a/django/core/meta/__init__.py +++ b/django/core/meta/__init__.py @@ -232,10 +232,8 @@ class RelatedObject(object): count = min(count, self.field.rel.max_num_in_admin) else: count = self.field.rel.num_in_admin - + fields = [] - - for i in range(count): for f in self.opts.fields + self.opts.many_to_many: if follow.get(f.name, False): @@ -424,27 +422,20 @@ class Options: return [RelatedObject(self, opts, field) for opts, field in self.get_all_related_objects()] def get_data_holders(self, follow=None): + if follow == None : + follow = self.get_follow() return [f for f in self.fields + self.many_to_many + self.get_all_related_objects_wrapped() if follow.get(f.name, None) ] def get_follow(self, override=None): follow = {} - - for f in self.fields + self.many_to_many: + for f in self.fields + self.many_to_many + self.get_all_related_objects_wrapped(): if override and override.has_key(f.name): - fol = override[f.name] + child_override = override[f.name] else: - fol = f.editable + child_override = None + fol = f.get_follow(child_override) if fol: follow[f.name] = fol - - for f in self.get_all_related_objects_wrapped(): - if override and override.has_key(f.name): - fol = f.get_follow(override[f.name]) - else: - fol = f.get_follow(None) - if fol: - follow[f.name] = fol - return follow def get_all_related_many_to_many_objects(self): @@ -478,6 +469,7 @@ class Options: Returns True if this object's admin form has at least one of the given field_type (e.g. FileField). """ + #TODO: follow if not hasattr(self, '_field_types'): self._field_types = {} if not self._field_types.has_key(field_type): diff --git a/django/core/meta/fields.py b/django/core/meta/fields.py index 556d8a0a7f..9fa250a96d 100644 --- a/django/core/meta/fields.py +++ b/django/core/meta/fields.py @@ -290,7 +290,12 @@ class Field(object): values from. """ return { self.get_db_column(): self._get_val_from_obj(obj)} - + + def get_follow(self, override=None): + if override: + return override + else: + return self.editable class AutoField(Field): empty_strings_allowed = False @@ -463,6 +468,14 @@ class FileField(Field): f = os.path.join(self.get_directory_name(), get_valid_filename(os.path.basename(filename))) return os.path.normpath(f) +class FilePathField(Field): + def __init__(self, verbose_name=None, name=None, path='', match=None, recursive=False, **kwargs): + self.path, self.match, self.recursive = path, match, recursive + Field.__init__(self, verbose_name, name, **kwargs) + + def get_manipulator_field_objs(self): + return [curry(formfields.FilePathField, path=self.path, match=self.match, recursive=self.recursive)] + class FloatField(Field): empty_strings_allowed = False def __init__(self, verbose_name=None, name=None, max_digits=None, decimal_places=None, **kwargs): diff --git a/django/middleware/cache.py b/django/middleware/cache.py index 8216c40ae1..04f98122f7 100644 --- a/django/middleware/cache.py +++ b/django/middleware/cache.py @@ -1,4 +1,3 @@ -import copy from django.conf import settings from django.core.cache import cache from django.utils.cache import get_cache_key, learn_cache_key, patch_response_headers @@ -49,7 +48,7 @@ class CacheMiddleware: return None # No cache information available, need to rebuild. request._cache_update_cache = False - return copy.copy(response) + return response def process_response(self, request, response): "Sets the cache, if needed." diff --git a/django/templatetags/admin_modify.py b/django/templatetags/admin_modify.py index 040834126a..5ec7a8381a 100644 --- a/django/templatetags/admin_modify.py +++ b/django/templatetags/admin_modify.py @@ -8,7 +8,7 @@ from django.utils.functional import curry from django.core.template_decorators import simple_tag, inclusion_tag from django.views.admin.main import AdminBoundField -from django.core.meta.fields import BoundField +from django.core.meta.fields import BoundField, Field import re word_re = re.compile('[A-Z][a-z]+') @@ -24,7 +24,7 @@ include_admin_script = simple_tag(include_admin_script) #@inclusion_tag('admin_submit_line', takes_context=True) -def submit_row(context): +def submit_row(context): change = context['change'] add = context['add'] show_delete = context['show_delete'] @@ -68,41 +68,43 @@ field_label = simple_tag(field_label) class FieldWidgetNode(template.Node): + nodelists = {} + default = None + def __init__(self, bound_field_var): self.bound_field_var = bound_field_var - self.nodelists = {} - t = template_loader.get_template("widget/default") - self.default = t.nodelist + def get_nodelist(cls, klass): + if not cls.nodelists.has_key(klass): + try: + field_class_name = klass.__name__ + template_name = "widget/%s" % \ + class_name_to_underscored(field_class_name) + nodelist = template_loader.get_template(template_name).nodelist + except template.TemplateDoesNotExist: + super_klass = bool(klass.__bases__) and klass.__bases__[0] or None + if super_klass and super_klass != Field: + nodelist = cls.get_nodelist(super_klass) + else: + if not cls.default: + cls.default = template_loader.get_template("widget/default").nodelist + nodelist = cls.default + + cls.nodelists[klass] = nodelist + return nodelist + else: + return cls.nodelists[klass] + get_nodelist = classmethod(get_nodelist) + + def render(self, context): bound_field = template.resolve_variable(self.bound_field_var, context) - add = context['add'] - change = context['change'] context.push() context['bound_field'] = bound_field - klass = bound_field.field.__class__ - if not self.nodelists.has_key(klass): - t = None - while klass: - try: - field_class_name = klass.__name__ - template_name = "widget/%s" % \ - class_name_to_underscored(field_class_name) - t = template_loader.get_template(template_name) - break - except template.TemplateDoesNotExist: - klass = bool(klass.__bases__) and klass.__bases__[0] or None - - if t == None: - nodelist = self.default - else: - nodelist = t.nodelist - - self.nodelists[klass] = nodelist - - output = self.nodelists[klass].render(context) + + output = self.get_nodelist(bound_field.field.__class__).render(context) context.pop() return output diff --git a/django/utils/decorators.py b/django/utils/decorators.py index b21a4e4248..1333f9da88 100644 --- a/django/utils/decorators.py +++ b/django/utils/decorators.py @@ -12,6 +12,10 @@ def decorator_from_middleware(middleware_class): result = middleware.process_request(request) if result is not None: return result + if hasattr(middleware, 'process_view'): + result = middleware.process_view(request, view_func, **kwargs) + if result is not None: + return result response = view_func(request, *args, **kwargs) if hasattr(middleware, 'process_response'): result = middleware.process_response(request, response) diff --git a/django/views/admin/main.py b/django/views/admin/main.py index fc24b590ef..fcce2533cc 100644 --- a/django/views/admin/main.py +++ b/django/views/admin/main.py @@ -605,6 +605,7 @@ class AdminBoundFieldSet(BoundFieldSet): def fill_extra_context(opts, app_label, context, add=False, change=False, show_delete=False, form_url=''): admin_field_objs = opts.admin.get_field_objs(opts) + ordered_objects = opts.get_ordered_objects()[:] auto_populated_fields = [f for f in opts.fields if f.prepopulate_from] @@ -621,9 +622,13 @@ def fill_extra_context(opts, app_label, context, add=False, change=False, show_d form = context['form'] original = context['original'] + + field_sets = opts.admin.get_field_sets(opts) bound_field_sets = [field_set.bind(form, original, AdminBoundFieldSet) - for field_set in opts.admin.get_field_sets(opts)] - + for field_set in field_sets] + + first_form_field = bound_field_sets[0].bound_field_lines[0].bound_fields[0].form_fields[0]; + inline_related_objects = opts.get_inline_related_objects_wrapped() ordered_object_names = ' '.join(['object.%s' % o.pk.name for o in ordered_objects]) @@ -631,6 +636,7 @@ def fill_extra_context(opts, app_label, context, add=False, change=False, show_d extra_context = { 'add': add, 'change': change, + 'first_form_field_id': first_form_field.get_id(), 'ordered_objects' : ordered_objects, 'ordered_object_names' : ordered_object_names, 'auto_populated_fields' : auto_populated_fields, @@ -643,7 +649,8 @@ def fill_extra_context(opts, app_label, context, add=False, change=False, show_d 'inline_related_objects': inline_related_objects, 'content_type_id' : opts.get_content_type_id(), 'save_on_top' : opts.admin.save_on_top, - 'verbose_name_plural': opts.verbose_name_plural, + 'verbose_name_plural': opts.verbose_name_plural, + 'verbose_name': opts.verbose_name, 'save_as': opts.admin.save_as, 'app_label': app_label, 'object_name': opts.object_name, @@ -711,7 +718,7 @@ def add_stage_new(request, app_label, module_name, show_delete=False, form_url=' c['object_id'] = object_id_override - fill_extra_context(opts, app_label, c, change=False) + fill_extra_context(opts, app_label, c, add=True) return render_to_response("admin_change_form", context_instance=c) diff --git a/django/views/generic/list_detail.py b/django/views/generic/list_detail.py index 6328b7097a..f4cdf90c56 100644 --- a/django/views/generic/list_detail.py +++ b/django/views/generic/list_detail.py @@ -32,6 +32,8 @@ def object_list(request, app_label, module_name, paginate_by=None, allow_empty=F the previous page pages number of pages, total + hits + number of objects, total """ mod = models.get_module(app_label, module_name) lookup_kwargs = extra_lookup_kwargs.copy() @@ -56,6 +58,7 @@ def object_list(request, app_label, module_name, paginate_by=None, allow_empty=F 'next': page + 1, 'previous': page - 1, 'pages': paginator.pages, + 'hits' : paginator.hits, }) else: object_list = mod.get_list(**lookup_kwargs) diff --git a/docs/db-api.txt b/docs/db-api.txt index 8a02437aaa..b80d4e8647 100644 --- a/docs/db-api.txt +++ b/docs/db-api.txt @@ -449,8 +449,7 @@ Related objects (e.g. ``Choices``) are created using convenience functions:: >>> p.get_choice_count() 4 -Each of those ``add_choice`` methods is equivalent to (except obviously much -simpler than):: +Each of those ``add_choice`` methods is equivalent to (but much simpler than):: >>> c = polls.Choice(poll_id=p.id, choice="Over easy", votes=0) >>> c.save() @@ -459,6 +458,8 @@ Note that when using the `add_foo()`` methods, you do not give any value for the ``id`` field, nor do you give a value for the field that stores the relation (``poll_id`` in this case). +The ``add_FOO()`` method always returns the newly created object. + Deleting objects ================ diff --git a/docs/generic_views.txt b/docs/generic_views.txt index 62e7c14e2b..1c0de07a7a 100644 --- a/docs/generic_views.txt +++ b/docs/generic_views.txt @@ -115,7 +115,7 @@ The date-based generic functions are: Yearly archive. Requires that the ``year`` argument be present in the URL pattern. - Uses the template ``app_label/module_name__archive_year`` by default. + Uses the template ``app_label/module_name_archive_year`` by default. Has the following template context: @@ -134,7 +134,7 @@ The date-based generic functions are: default, which is a three-letter month abbreviation. To change it to use numbers, use ``"%m"``. - Uses the template ``app_label/module_name__archive_month`` by default. + Uses the template ``app_label/module_name_archive_month`` by default. Has the following template context: @@ -151,7 +151,7 @@ The date-based generic functions are: also pass ``day_format``, which defaults to ``"%d"`` (day of the month as a decimal number, 1-31). - Uses the template ``app_label/module_name__archive_day`` by default. + Uses the template ``app_label/module_name_archive_day`` by default. Has the following template context: @@ -246,6 +246,8 @@ Individual views are: The previous page ``pages`` Number of pages total + ``hits`` + Total number of objects ``object_detail`` Object detail page. This works like and takes the same arguments as @@ -272,7 +274,7 @@ The create/update/delete views are: be interpolated against the object's field attributes. For example, you could use ``post_save_redirect="/polls/%(slug)s/"``. - Uses the template ``app_label/module_name__form`` by default. This is the + Uses the template ``app_label/module_name_form`` by default. This is the same template as the ``update_object`` view below. Your template can tell the different by the presence or absence of ``{{ object }}`` in the context. @@ -294,7 +296,7 @@ The create/update/delete views are: ``list_detail.object_detail`` does (see above), and the same ``post_save_redirect`` as ``create_object`` does. - Uses the template ``app_label/module_name__form`` by default. + Uses the template ``app_label/module_name_form`` by default. Has the following template context: diff --git a/docs/install.txt b/docs/install.txt index b347006cbb..b18d26d5c8 100644 --- a/docs/install.txt +++ b/docs/install.txt @@ -21,14 +21,15 @@ See `How to use Django with mod_python`_ for information on how to configure mod_python once you have it installed. If you can't use mod_python for some reason, fear not: Django follows the WSGI_ -spec, which allows it to run on a variety of server platforms. As people -experiment with different server platforms, we'll update this document to -give specific installation instructions for each platform. +spec, which allows it to run on a variety of server platforms. See the +`server-arrangements wiki page`_ for specific installation instructions for +each platform. .. _Apache: http://httpd.apache.org/ .. _mod_python: http://www.modpython.org/ .. _WSGI: http://www.python.org/peps/pep-0333.html .. _How to use Django with mod_python: http://www.djangoproject.com/documentation/modpython/ +.. _server-arrangements wiki page: http://code.djangoproject.com/wiki/ServerArrangements Get your database running ========================= @@ -37,11 +38,6 @@ If you plan to use Django's database API functionality, you'll need to make sure a database server is running. Django works with PostgreSQL_ (recommended), MySQL_ and SQLite_. -Note that support for MySQL and SQLite is a recent development, and Django -hasn't been comprehensively tested in those environments. If you find any bugs -in Django's MySQL or SQLite bindings, please file them in -`Django's ticket system`_ so we can fix them immediately. - Additionally, you'll need to make sure your Python database bindings are installed. diff --git a/docs/model-api.txt b/docs/model-api.txt index 4af193ca48..140518e80e 100644 --- a/docs/model-api.txt +++ b/docs/model-api.txt @@ -95,8 +95,8 @@ The following arguments are available to all field types. All are optional. ('GR', 'Graduate'), ) - The first element in each tuple is the actual value to be stored. The - second element is the human-readable name for the option. + The first element in each tuple is the actual value to be stored. The + second element is the human-readable name for the option. ``core`` For objects that are edited inline to a related object. @@ -248,18 +248,18 @@ Here are all available field types: uploaded files don't fill up the given directory). The admin represents this as an ```` (a file-upload widget). - - Using a `FieldField` or an ``ImageField`` (see below) in a model takes a few + + Using a `FieldField` or an ``ImageField`` (see below) in a model takes a few steps: - + 1. In your settings file, you'll need to define ``MEDIA_ROOT``as the full path to a directory where you'd like Django to store uploaded files. (For performance, these files are not stored in the database.) Define ``MEDIA_URL`` as the base public URL of that directory. Make sure that this directory is writable by the Web server's user account. - - 2. Add the ``FileField`` or ``ImageField`` to your model, making sure + + 2. Add the ``FileField`` or ``ImageField`` to your model, making sure to define the ``upload_to`` option to tell Django to which subdirectory of ``MEDIA_ROOT`` it should upload files. @@ -269,9 +269,44 @@ Here are all available field types: example, if your ``ImageField`` is called ``mug_shot``, you can get the absolute URL to your image in a template with ``{{ object.get_mug_shot_url }}``. - + .. _`strftime formatting`: http://docs.python.org/lib/module-time.html#l2h-1941 +``FilePathField`` + A field whose choices are limited to the filenames in a certain directory + on the filesystem. Has three special arguments, of which the first is + required: + + ====================== =================================================== + Argument Description + ====================== =================================================== + ``path`` Required. The absolute filesystem path to a + directory from which this ``FilePathField`` should + get its choices. Example: ``"/home/images"``. + + ``match`` Optional. A regular expression, as a string, that + ``FilePathField`` will use to filter filenames. + Note that the regex will be applied to the + base filename, not the full path. Example: + ``"foo.*\.txt^"``, which will match a file called + ``foo23.txt`` but not ``bar.txt`` or ``foo23.gif``. + + ``recursive`` Optional. Either ``True`` or ``False``. Default is + ``False``. Specifies whether all subdirectories of + ``path`` should be included. + ====================== =================================================== + + Of course, these arguments can be used together. + + The one potential gotcha is that ``match`` applies to the base filename, + not the full path. So, this example:: + + FilePathField(path="/home/images", match="foo.*", recursive=True) + + ...will match ``/home/images/foo.gif`` but not ``/home/images/foo/bar.gif`` + because the ``match`` applies to the base filename (``foo.gif`` and + ``bar.gif``). + ``FloatField`` A floating-point number. Has two **required** arguments: @@ -302,7 +337,7 @@ Here are all available field types: width of the image each time a model instance is saved. Requires the `Python Imaging Library`_. - + .. _Python Imaging Library: http://www.pythonware.com/products/pil/ ``IntegerField`` @@ -721,7 +756,9 @@ Here's a list of all possible ``META`` options. No options are required. Adding unique_together = (("driver", "restaurant"),) This is a list of lists of fields that must be unique when considered - together. It's used in the Django admin. + together. It's used in the Django admin and is enforced at the database + level (i.e., the appropriate ``UNIQUE`` statements are included in the + ``CREATE TABLE`` statement). ``verbose_name`` A human-readable name for the object, singular:: diff --git a/docs/modpython.txt b/docs/modpython.txt index 72c2d9a073..0f54de86ac 100644 --- a/docs/modpython.txt +++ b/docs/modpython.txt @@ -143,6 +143,9 @@ particular part of the site:: Just change ``Location`` to the root URL of your media files. +Note that the Django development server automagically serves admin media files, +but this is not the case when you use any other server arrangement. + .. _lighttpd: http://www.lighttpd.net/ .. _TUX: http://en.wikipedia.org/wiki/TUX_web_server .. _Apache: http://httpd.apache.org/