1
0
mirror of https://github.com/django/django.git synced 2025-07-05 18:29:11 +00:00

Merged to r852.

Cleaned up some templates. 
Efficiency fix in field_widget tag.




git-svn-id: http://code.djangoproject.com/svn/django/branches/new-admin@854 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Robert Wittams 2005-10-13 18:28:44 +00:00
parent 0f338b8c2e
commit d41ffe4ee4
21 changed files with 225 additions and 164 deletions

View File

@ -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; }

View File

@ -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 %}
<div class="breadcrumbs">
<a href="../../../">Home</a> &rsaquo;
<a href="../">{{verbose_name_plural|capfirst}}</a> &rsaquo;
{% if add %}
Add {{verbose_name}}
{% else %}
{{original|striptags|truncatewords:"18"}}
{% endif %}
{% if add %}Add {{verbose_name}}{% else %}{{original|striptags|truncatewords:"18"}}{% endif %}
</div>
{% endif %}
{% endblock %}
{% endif %}{% endblock %}
{% block content %}<div id="content-main">
{% if change %}
@ -82,7 +72,7 @@
{% submit_row %}
{% if add %}
<script type="text/javascript">document.getElementById("id_{{first_field}}").focus();</script>'
<script type="text/javascript">document.getElementById("{{first_form_field_id}}").focus();</script>
{% endif %}
{% if auto_populated_fields %}

View File

@ -1,36 +1,21 @@
<div class="{{ class_names }}" >
{% for bound_field in bound_fields %}
{{ bound_field.html_error_list }}
{% 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 %}
&nbsp;<strong>{{ bound_field.existing_repr|truncatewords:"14" }}</strong>
{% if bound_field.existing_repr %}&nbsp;<strong>{{ bound_field.existing_repr|truncatewords:"14" }}</strong>{% endif %}
{% endif %}
{% endif %}
{% endif %}
{% if bound_field.field.help_text %}
<p class="help">
{{bound_field.field.help_text}}
</p>
{% endif %}
{% if bound_field.field.help_text %}<p class="help">{{bound_field.field.help_text}}</p>{% endif %}
{% endfor %}
</div>

View File

@ -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:
@ -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):
@ -366,8 +362,7 @@ 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

View File

@ -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',

View File

@ -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',

View File

@ -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',

View File

@ -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=[]):

View File

@ -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):

View File

@ -234,8 +234,6 @@ class RelatedObject(object):
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):

View File

@ -291,6 +291,11 @@ class Field(object):
"""
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):

View File

@ -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."

View File

@ -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]+')
@ -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 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:
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)
t = template_loader.get_template(template_name)
break
nodelist = template_loader.get_template(template_name).nodelist
except template.TemplateDoesNotExist:
klass = bool(klass.__bases__) and klass.__bases__[0] or None
if t == None:
nodelist = self.default
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:
nodelist = t.nodelist
if not cls.default:
cls.default = template_loader.get_template("widget/default").nodelist
nodelist = cls.default
self.nodelists[klass] = nodelist
cls.nodelists[klass] = nodelist
return nodelist
else:
return cls.nodelists[klass]
get_nodelist = classmethod(get_nodelist)
output = self.nodelists[klass].render(context)
def render(self, context):
bound_field = template.resolve_variable(self.bound_field_var, context)
context.push()
context['bound_field'] = bound_field
output = self.get_nodelist(bound_field.field.__class__).render(context)
context.pop()
return output

View File

@ -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)

View File

@ -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,8 +622,12 @@ 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()
@ -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,
@ -644,6 +650,7 @@ def fill_extra_context(opts, app_label, context, add=False, change=False, show_d
'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': 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)

View File

@ -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)

View File

@ -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
================

View File

@ -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:

View File

@ -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.

View File

@ -272,6 +272,41 @@ Here are all available field types:
.. _`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:
@ -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::

View File

@ -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/