1
0
mirror of https://github.com/django/django.git synced 2025-07-05 10:19:20 +00:00

Rationalised related object methods

git-svn-id: http://code.djangoproject.com/svn/django/branches/new-admin@982 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Robert Wittams 2005-10-20 19:07:15 +00:00
parent 5d74e282a4
commit bd47a97997
11 changed files with 144 additions and 174 deletions

View File

@ -13,19 +13,28 @@ def validate_class(klass):
# Fields. # Fields.
for f in opts.fields: for f in opts.fields:
if isinstance(f, meta.ManyToManyField): if isinstance(f, meta.ManyToManyField):
assert isinstance(f.rel, meta.ManyToMany), "ManyToManyField %s should have 'rel' set to a ManyToMany instance." % f.name assert isinstance(f.rel, meta.ManyToMany), \
"ManyToManyField %s should have 'rel' set to a ManyToMany instance." % f.name
# Inline related objects. # Inline related objects.
for rel_opts, rel_field in opts.get_inline_related_objects(): for related in opts.get_followed_related_objects():
assert len([f for f in rel_opts.fields if f.core]) > 0, "At least one field in %s should have core=True, because it's being edited inline by %s." % (rel_opts.object_name, opts.object_name) assert len([f for f in related.opts.fields if f.core]) > 0, \
"At least one field in %s should have core=True, because it's being edited inline by %s." % \
(related.opts.object_name, opts.object_name)
# All related objects. # All related objects.
related_apps_seen = [] related_apps_seen = []
for rel_opts, rel_field in opts.get_all_related_objects(): for related in opts.get_all_related_objects():
if rel_opts in related_apps_seen: if related.opts in related_apps_seen:
assert rel_field.rel.related_name is not None, "Relationship in field %s.%s needs to set 'related_name' because more than one %s object is referenced in %s." % (rel_opts.object_name, rel_field.name, opts.object_name, rel_opts.object_name) assert related.field.rel.related_name is not None, \
related_apps_seen.append(rel_opts) "Relationship in field %s.%s needs to set 'related_name' because more than one" \
" %s object is referenced in %s." % \
(related.opts.object_name, related.field.name, opts.object_name, rel_opts.object_name)
related_apps_seen.append(related.opts)
# Etc. # Etc.
if opts.admin is not None: if opts.admin is not None:
assert opts.admin.ordering or opts.ordering, "%s needs to set 'ordering' on either its 'admin' or its model, because it has 'admin' set." % opts.object_name assert opts.admin.ordering or opts.ordering, \
"%s needs to set 'ordering' on either its 'admin' or its model," \
"because it has 'admin' set." % \
opts.object_name
if __name__ == "__main__": if __name__ == "__main__":
import sys import sys

View File

@ -21,13 +21,11 @@
{% endif %}{% endif %} {% endif %}{% endif %}
<form {{ form_enc_attrib }} action='{{ form_url }}' method="post"> <form {{ form_enc_attrib }} action='{{ form_url }}' method="post">
{% if is_popup %}<input type="hidden" name="_popup" value="1">{% endif %} {% if is_popup %}<input type="hidden" name="_popup" value="1">{% endif %}
{% if save_on_top %}{% submit_row %}{% endif %} {% if save_on_top %}{% submit_row %}{% endif %}
{% if form.error_dict %}<p class="errornote">Please correct the error{{ form.error_dict.items|pluralize }} below.</p>{% endif %} {% if form.error_dict %}<p class="errornote">Please correct the error{{ form.error_dict.items|pluralize }} below.</p>{% endif %}
{% for bound_field_set in bound_field_sets %} {% for bound_field_set in bound_field_sets %}
<fieldset class="module aligned {{ bound_field_set.classes }}"> <fieldset class="module aligned {{ bound_field_set.classes }}">
{% if bound_field_set.name %}<h2>{{bound_field_set.name }}</h2>{% endif %} {% if bound_field_set.name %}<h2>{{bound_field_set.name }}</h2>{% endif %}
{% for bound_field_line in bound_field_set %} {% for bound_field_line in bound_field_set %}
{% admin_field_line bound_field_line %} {% admin_field_line bound_field_line %}

View File

@ -518,7 +518,7 @@ def _get_submit_row_template(opts, app_label, add, change, show_delete, ordered_
t.append('</div>\n') t.append('</div>\n')
return t return t
def get_javascript_imports(opts,auto_populated_fields, ordered_objects, admin_field_objs): def get_javascript_imports(opts,auto_populated_fields, ordered_objects, field_sets):
# Put in any necessary JavaScript imports. # Put in any necessary JavaScript imports.
js = ['js/core.js', 'js/admin/RelatedObjectLookups.js'] js = ['js/core.js', 'js/admin/RelatedObjectLookups.js']
if auto_populated_fields: if auto_populated_fields:
@ -530,13 +530,13 @@ def get_javascript_imports(opts,auto_populated_fields, ordered_objects, admin_fi
if opts.admin.js: if opts.admin.js:
js.extend(opts.admin.js) js.extend(opts.admin.js)
seen_collapse = False seen_collapse = False
for _, options in admin_field_objs: for field_set in field_sets:
if not seen_collapse and 'collapse' in options.get('classes', ''): if not seen_collapse and 'collapse' in field_set.classes:
seen_collapse = True seen_collapse = True
js.append('js/admin/CollapsedFieldsets.js' ) js.append('js/admin/CollapsedFieldsets.js' )
try: try:
for field_list in options['fields']: for field_line in field_set:
for f in field_list: for f in field_line:
if f.rel and isinstance(f, meta.ManyToManyField) and f.rel.filter_interface: if f.rel and isinstance(f, meta.ManyToManyField) and f.rel.filter_interface:
js.extend(['js/SelectBox.js' , 'js/SelectFilter2.js']) js.extend(['js/SelectBox.js' , 'js/SelectFilter2.js'])
raise StopIteration raise StopIteration
@ -608,22 +608,12 @@ class AdminBoundFieldSet(BoundFieldSet):
def fill_extra_context(opts, app_label, context, add=False, change=False, show_delete=False, form_url=''): 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()[:] ordered_objects = opts.get_ordered_objects()[:]
auto_populated_fields = [f for f in opts.fields if f.prepopulate_from] auto_populated_fields = [f for f in opts.fields if f.prepopulate_from]
coltype = ordered_objects and 'colMS' or 'colM'
javascript_imports = get_javascript_imports(opts, auto_populated_fields, ordered_objects, admin_field_objs);
if ordered_objects:
coltype = 'colMS'
else:
coltype = 'colM'
has_absolute_url = hasattr(opts.get_model_module().Klass, 'get_absolute_url') has_absolute_url = hasattr(opts.get_model_module().Klass, 'get_absolute_url')
form_enc_attrib = opts.has_field_type(meta.FileField) and 'enctype="multipart/form-data" ' or '' form_enc_attrib = opts.has_field_type(meta.FileField) and 'enctype="multipart/form-data" ' or ''
form = context['form'] form = context['form']
original = context['original'] original = context['original']
@ -631,10 +621,9 @@ def fill_extra_context(opts, app_label, context, add=False, change=False, show_d
bound_field_sets = [field_set.bind(form, original, AdminBoundFieldSet) bound_field_sets = [field_set.bind(form, original, AdminBoundFieldSet)
for field_set in field_sets] for field_set in field_sets]
javascript_imports = get_javascript_imports(opts, auto_populated_fields, ordered_objects, field_sets);
first_form_field = bound_field_sets[0].bound_field_lines[0].bound_fields[0].form_fields[0]; first_form_field = bound_field_sets[0].bound_field_lines[0].bound_fields[0].form_fields[0];
inline_related_objects = opts.get_followed_related_objects()
inline_related_objects = opts.get_inline_related_objects_wrapped()
ordered_object_names = ' '.join(['object.%s' % o.pk.name for o in ordered_objects]) ordered_object_names = ' '.join(['object.%s' % o.pk.name for o in ordered_objects])
extra_context = { extra_context = {
@ -739,7 +728,6 @@ def change_stage(request, app_label, module_name, object_id):
except ObjectDoesNotExist: except ObjectDoesNotExist:
raise Http404 raise Http404
inline_related_objects = opts.get_inline_related_objects()
if request.POST: if request.POST:
new_data = request.POST.copy() new_data = request.POST.copy()
if opts.has_field_type(meta.FileField): if opts.has_field_type(meta.FileField):
@ -806,9 +794,12 @@ def change_stage(request, app_label, module_name, object_id):
form.original = manipulator.original_object form.original = manipulator.original_object
form.order_objects = [] form.order_objects = []
for rel_opts, rel_field in inline_related_objects: for related in opts.get_followed_related_objects():
if rel_opts.order_with_respect_to and rel_opts.order_with_respect_to.rel and rel_opts.order_with_respect_to.rel.to == opts: wrt = related.opts.order_with_respect_to
orig_list = getattr(manipulator.original_object, 'get_%s_list' % opts.get_rel_object_method_name(rel_opts, rel_field))() if wrt and wrt.rel and wrt.rel.to == opts:
func = getattr(manipulator.original_object, 'get_%s_list' %
opts.get_rel_object_method_name(rel_opts, rel_field))
orig_list = func()
form.order_objects.extend(orig_list) form.order_objects.extend(orig_list)
c = Context(request, { c = Context(request, {
@ -836,50 +827,50 @@ def _get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current
if current_depth > 16: if current_depth > 16:
return # Avoid recursing too deep. return # Avoid recursing too deep.
objects_seen = [] objects_seen = []
for rel_opts, rel_field in opts.get_all_related_objects(): for related in opts.get_all_related_objects():
if rel_opts in objects_seen: if related.opts in objects_seen:
continue continue
objects_seen.append(rel_opts) objects_seen.append(rel_opts)
rel_opts_name = opts.get_rel_object_method_name(rel_opts, rel_field) rel_opts_name = opts.get_rel_object_method_name(related.opts, related.field)
if isinstance(rel_field.rel, meta.OneToOne): if isinstance(related.field.rel, meta.OneToOne):
try: try:
sub_obj = getattr(obj, 'get_%s' % rel_opts_name)() sub_obj = getattr(obj, 'get_%s' % rel_opts_name)()
except ObjectDoesNotExist: except ObjectDoesNotExist:
pass pass
else: else:
if rel_opts.admin: if rel_opts.admin:
p = '%s.%s' % (rel_opts.app_label, rel_opts.get_delete_permission()) p = '%s.%s' % (related.opts.app_label, related.opts.get_delete_permission())
if not user.has_perm(p): if not user.has_perm(p):
perms_needed.add(rel_opts.verbose_name) perms_needed.add(related.opts.verbose_name)
# We don't care about populating deleted_objects now. # We don't care about populating deleted_objects now.
continue continue
if rel_field.rel.edit_inline or not rel_opts.admin: if related.field.rel.edit_inline or not related.opts.admin:
# Don't display link to edit, because it either has no # Don't display link to edit, because it either has no
# admin or is edited inline. # admin or is edited inline.
nh(deleted_objects, current_depth, ['%s: %r' % (capfirst(rel_opts.verbose_name), sub_obj), []]) nh(deleted_objects, current_depth, ['%s: %r' % (capfirst(related.opts.verbose_name), sub_obj), []])
else: else:
# Display a link to the admin page. # Display a link to the admin page.
nh(deleted_objects, current_depth, ['%s: <a href="../../../../%s/%s/%s/">%r</a>' % \ nh(deleted_objects, current_depth, ['%s: <a href="../../../../%s/%s/%s/">%r</a>' % \
(capfirst(rel_opts.verbose_name), rel_opts.app_label, rel_opts.module_name, (capfirst(related.opts.verbose_name), related.opts.app_label, related.opts.module_name,
getattr(sub_obj, rel_opts.pk.column), sub_obj), []]) getattr(sub_obj, related.opts.pk.column), sub_obj), []])
_get_deleted_objects(deleted_objects, perms_needed, user, sub_obj, rel_opts, current_depth+2) _get_deleted_objects(deleted_objects, perms_needed, user, sub_obj, related.opts, current_depth+2)
else: else:
has_related_objs = False has_related_objs = False
for sub_obj in getattr(obj, 'get_%s_list' % rel_opts_name)(): for sub_obj in getattr(obj, 'get_%s_list' % rel_opts_name)():
has_related_objs = True has_related_objs = True
if rel_field.rel.edit_inline or not rel_opts.admin: if related.field.rel.edit_inline or not related.opts.admin:
# Don't display link to edit, because it either has no # Don't display link to edit, because it either has no
# admin or is edited inline. # admin or is edited inline.
nh(deleted_objects, current_depth, ['%s: %s' % (capfirst(rel_opts.verbose_name), strip_tags(repr(sub_obj))), []]) nh(deleted_objects, current_depth, ['%s: %s' % (capfirst(related.opts.verbose_name), strip_tags(repr(sub_obj))), []])
else: else:
# Display a link to the admin page. # Display a link to the admin page.
nh(deleted_objects, current_depth, ['%s: <a href="../../../../%s/%s/%s/">%s</a>' % \ nh(deleted_objects, current_depth, ['%s: <a href="../../../../%s/%s/%s/">%s</a>' % \
(capfirst(rel_opts.verbose_name), rel_opts.app_label, rel_opts.module_name, sub_obj.id, strip_tags(repr(sub_obj))), []]) (capfirst(related.opts.verbose_name), related.opts.app_label, related.opts.module_name, sub_obj.id, strip_tags(repr(sub_obj))), []])
_get_deleted_objects(deleted_objects, perms_needed, user, sub_obj, rel_opts, current_depth+2) _get_deleted_objects(deleted_objects, perms_needed, user, sub_obj, related.opts, current_depth+2)
# If there were related objects, and the user doesn't have # If there were related objects, and the user doesn't have
# permission to delete them, add the missing perm to perms_needed. # permission to delete them, add the missing perm to perms_needed.
if rel_opts.admin and has_related_objs: if related.opts.admin and has_related_objs:
p = '%s.%s' % (rel_opts.app_label, rel_opts.get_delete_permission()) p = '%s.%s' % (related.opts.app_label, related.opts.get_delete_permission())
if not user.has_perm(p): if not user.has_perm(p):
perms_needed.add(rel_opts.verbose_name) perms_needed.add(rel_opts.verbose_name)
for rel_opts, rel_field in opts.get_all_related_many_to_many_objects(): for rel_opts, rel_field in opts.get_all_related_many_to_many_objects():

View File

@ -88,12 +88,8 @@ class Manipulator:
must happen after validation because html2python functions aren't must happen after validation because html2python functions aren't
expected to deal with invalid input. expected to deal with invalid input.
""" """
""" for field in self.fields:
for field in self.fields: field.convert_post_data(new_data)
"""
for field in self.fields:
field.convert_post_data(new_data)
class FormWrapper: class FormWrapper:
""" """
@ -101,7 +97,7 @@ class FormWrapper:
This allows dictionary-style lookups of formfields. It also handles feeding This allows dictionary-style lookups of formfields. It also handles feeding
prepopulated data and validation error messages to the formfield objects. prepopulated data and validation error messages to the formfield objects.
""" """
def __init__(self, manipulator, data, error_dict, edit_inline = False): def __init__(self, manipulator, data, error_dict, edit_inline = True):
self.manipulator, self.data = manipulator, data self.manipulator, self.data = manipulator, data
self.error_dict = error_dict self.error_dict = error_dict
self._inline_collections = None self._inline_collections = None
@ -293,7 +289,7 @@ class FormField:
data = data_dict.get(self.get_member_name(), None) data = data_dict.get(self.get_member_name(), None)
if data is None: if data is None:
data = '' data = ''
return data return data
def convert_post_data(self, new_data): def convert_post_data(self, new_data):
name = self.get_member_name() name = self.get_member_name()

View File

@ -306,7 +306,7 @@ def init():
db.db.rollback() db.db.rollback()
except UnboundLocalError: except UnboundLocalError:
pass pass
sys.exit(1) raise
else: else:
db.db.commit() db.db.commit()
init.args = '' init.args = ''
@ -582,12 +582,12 @@ def get_validation_errors(outfile):
e.add(opts, '"ordering" refers to "%s", a field that doesn\'t exist.' % field_name) e.add(opts, '"ordering" refers to "%s", a field that doesn\'t exist.' % field_name)
# Check core=True, if needed. # Check core=True, if needed.
for rel_opts, rel_field in opts.get_inline_related_objects(): for related in opts.get_followed_related_objects():
try: try:
for f in rel_opts.fields: for f in related.opts.fields:
if f.core: if f.core:
raise StopIteration raise StopIteration
e.add(rel_opts, "At least one field in %s should have core=True, because it's being edited inline by %s.%s." % (rel_opts.object_name, opts.module_name, opts.object_name)) e.add(related.opts, "At least one field in %s should have core=True, because it's being edited inline by %s.%s." % (related.opts.object_name, opts.module_name, opts.object_name))
except StopIteration: except StopIteration:
pass pass
return len(e.errors) return len(e.errors)

View File

@ -250,6 +250,8 @@ class RelatedObject(object):
return fields return fields
class BoundRelatedObject(object):
pass
class Options: class Options:
def __init__(self, module_name='', verbose_name='', verbose_name_plural='', db_table='', def __init__(self, module_name='', verbose_name='', verbose_name_plural='', db_table='',
@ -401,7 +403,7 @@ class Options:
for klass in mod._MODELS: for klass in mod._MODELS:
for f in klass._meta.fields: for f in klass._meta.fields:
if f.rel and self == f.rel.to: if f.rel and self == f.rel.to:
rel_objs.append((klass._meta, f)) rel_objs.append(RelatedObject(self, klass._meta, f))
if self.has_related_links: if self.has_related_links:
# Manually add RelatedLink objects, which are a special case. # Manually add RelatedLink objects, which are a special case.
relatedlinks = get_module('relatedlinks', 'relatedlinks') relatedlinks = get_module('relatedlinks', 'relatedlinks')
@ -415,32 +417,23 @@ class Options:
'content_type__package__label__exact': self.app_label, 'content_type__package__label__exact': self.app_label,
'content_type__python_module_name__exact': self.module_name, 'content_type__python_module_name__exact': self.module_name,
}) })
rel_objs.append((relatedlinks.RelatedLink._meta, link_field)) rel_objs.append(RelatedObject(self, relatedlinks.RelatedLink._meta, link_field))
self._all_related_objects = rel_objs self._all_related_objects = rel_objs
return rel_objs return rel_objs
def get_inline_related_objects(self):
return [(a, b) for a, b in self.get_all_related_objects() if b.rel.edit_inline]
def get_inline_related_objects_wrapped(self):
return [RelatedObject(self, opts, field) for opts, field in self.get_all_related_objects() if field.rel.edit_inline]
def get_all_related_objects_wrapped(self):
return [RelatedObject(self, opts, field) for opts, field in self.get_all_related_objects()]
def get_followed_related_objects(self, follow=None): def get_followed_related_objects(self, follow=None):
if follow == None: if follow == None:
follow = self.get_follow() follow = self.get_follow()
return [f for f in self.get_all_related_objects_wrapped() if follow.get(f.name, None) ] return [f for f in self.get_all_related_objects() if follow.get(f.name, None) ]
def get_data_holders(self, follow=None): def get_data_holders(self, follow=None):
if follow == None : if follow == None :
follow = self.get_follow() 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) ] return [f for f in self.fields + self.many_to_many + self.get_all_related_objects() if follow.get(f.name, None) ]
def get_follow(self, override=None): def get_follow(self, override=None):
follow = {} follow = {}
for f in self.fields + self.many_to_many + self.get_all_related_objects_wrapped(): for f in self.fields + self.many_to_many + self.get_all_related_objects():
if override and override.has_key(f.name): if override and override.has_key(f.name):
child_override = override[f.name] child_override = override[f.name]
else: else:
@ -476,7 +469,7 @@ class Options:
self._ordered_objects = objects self._ordered_objects = objects
return self._ordered_objects return self._ordered_objects
def has_field_type(self, field_type): def has_field_type(self, field_type, follow):
""" """
Returns True if this object's admin form has at least one of the given Returns True if this object's admin form has at least one of the given
field_type (e.g. FileField). field_type (e.g. FileField).
@ -491,8 +484,8 @@ class Options:
if isinstance(f, field_type): if isinstance(f, field_type):
raise StopIteration raise StopIteration
# Failing that, check related fields. # Failing that, check related fields.
for rel_obj, rel_field in self.get_inline_related_objects(): for related in self.get_followed_related_objects(follow):
for f in rel_obj.fields: for f in related.opts.fields:
if isinstance(f, field_type): if isinstance(f, field_type):
raise StopIteration raise StopIteration
except StopIteration: except StopIteration:
@ -853,8 +846,8 @@ class ModelBase(type):
old_app._MODELS[i] = new_class old_app._MODELS[i] = new_class
# Replace all relationships to the old class with # Replace all relationships to the old class with
# relationships to the new one. # relationships to the new one.
for rel_opts, rel_field in model._meta.get_all_related_objects(): for related in model._meta.get_all_related_objects():
rel_field.rel.to = opts related.field.rel.to = opts
for rel_opts, rel_field in model._meta.get_all_related_many_to_many_objects(): for rel_opts, rel_field in model._meta.get_all_related_many_to_many_objects():
rel_field.rel.to = opts rel_field.rel.to = opts
break break
@ -954,9 +947,9 @@ def method_delete(opts, self):
if hasattr(self, '_pre_delete'): if hasattr(self, '_pre_delete'):
self._pre_delete() self._pre_delete()
cursor = db.db.cursor() cursor = db.db.cursor()
for rel_opts, rel_field in opts.get_all_related_objects(): for related in opts.get_all_related_objects():
rel_opts_name = opts.get_rel_object_method_name(rel_opts, rel_field) rel_opts_name = opts.get_rel_object_method_name(related.opts, related.field)
if isinstance(rel_field.rel, OneToOne): if isinstance(related.field.rel, OneToOne):
try: try:
sub_obj = getattr(self, 'get_%s' % rel_opts_name)() sub_obj = getattr(self, 'get_%s' % rel_opts_name)()
except ObjectDoesNotExist: except ObjectDoesNotExist:
@ -1583,7 +1576,7 @@ def manipulator_init(opts, add, change, self, obj_key=None, follow=None):
self.fields.extend(f.get_manipulator_fields(opts, self, change)) self.fields.extend(f.get_manipulator_fields(opts, self, change))
# Add fields for related objects. # Add fields for related objects.
for f in opts.get_all_related_objects_wrapped(): for f in opts.get_all_related_objects():
if self.follow.get(f.name, False): if self.follow.get(f.name, False):
fol = self.follow[f.name] fol = self.follow[f.name]
self.fields.extend(f.get_manipulator_fields(opts, self, change, fol)) self.fields.extend(f.get_manipulator_fields(opts, self, change, fol))
@ -1638,7 +1631,7 @@ def manipulator_save(opts, klass, add, change, self, new_data):
expanded_data = DotExpandedDict(new_data.data) expanded_data = DotExpandedDict(new_data.data)
# Save many-to-one objects. Example: Add the Choice objects for a Poll. # Save many-to-one objects. Example: Add the Choice objects for a Poll.
for related in opts.get_all_related_objects_wrapped(): for related in opts.get_all_related_objects():
# Create obj_list, which is a DotExpandedDict such as this: # Create obj_list, which is a DotExpandedDict such as this:
# [('0', {'id': ['940'], 'choice': ['This is the first choice']}), # [('0', {'id': ['940'], 'choice': ['This is the first choice']}),
# ('1', {'id': ['941'], 'choice': ['This is the second choice']}), # ('1', {'id': ['941'], 'choice': ['This is the second choice']}),

View File

@ -48,6 +48,25 @@ def manipulator_validator_unique(f, opts, self, field_data, all_data):
return return
raise validators.ValidationError, "%s with this %s already exists." % (capfirst(opts.verbose_name), f.verbose_name) raise validators.ValidationError, "%s with this %s already exists." % (capfirst(opts.verbose_name), f.verbose_name)
class BoundField(object):
def __init__(self, field, field_mapping, original):
self.field = field
self.original = original
self.form_fields = self.resolve_form_fields(field_mapping)
def resolve_form_fields(self, field_mapping):
return [field_mapping[name] for name in self.field.get_manipulator_field_names('')]
def as_field_list(self):
return [self.field]
def original_value(self):
if self.original:
return self.original.__dict__[self.field.column]
def __repr__(self):
return "BoundField:(%s, %s)" %( self.field.name, self.form_fields)
class Field(object): class Field(object):
# Designates whether empty strings fundamentally are allowed at the # Designates whether empty strings fundamentally are allowed at the
@ -293,6 +312,9 @@ class Field(object):
else: else:
return self.editable return self.editable
def bind(self, fieldmapping, original, bound_field_class=BoundField):
return bound_field_class(self, fieldmapping, original)
class AutoField(Field): class AutoField(Field):
empty_strings_allowed = False empty_strings_allowed = False
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -730,7 +752,6 @@ class ManyToManyField(Field):
new_data[self.name] = [choices_list[0][0]] new_data[self.name] = [choices_list[0][0]]
return new_data return new_data
class OneToOneField(IntegerField): class OneToOneField(IntegerField):
def __init__(self, to, to_field=None, **kwargs): def __init__(self, to, to_field=None, **kwargs):
kwargs['verbose_name'] = kwargs.get('verbose_name', 'ID') kwargs['verbose_name'] = kwargs.get('verbose_name', 'ID')
@ -796,28 +817,10 @@ class OneToOne(ManyToOne):
self.lookup_overrides = lookup_overrides or {} self.lookup_overrides = lookup_overrides or {}
self.raw_id_admin = raw_id_admin self.raw_id_admin = raw_id_admin
class BoundField(object):
def __init__(self, field, field_mapping, original):
self.field = field
self.form_fields = self.resolve_form_fields(field_mapping)
self.original = original
def resolve_form_fields(self, field_mapping):
return [field_mapping[name] for name in self.field.get_manipulator_field_names('')]
def as_field_list(self):
return [self.field]
def original_value(self):
return self.original.__dict__[self.field.name]
def __repr__(self):
return "BoundField:(%s, %s)" %( self.field.name, self.form_fields)
class BoundFieldLine(object): class BoundFieldLine(object):
def __init__(self, field_line, field_mapping, original, bound_field_class=BoundField): def __init__(self, field_line, field_mapping, original, bound_field_class=BoundField):
self.bound_fields = [bound_field_class(field, field_mapping, original) for field in field_line] self.bound_fields = [field.bind(field_mapping, original, bound_field_class)
for field in field_line]
def __iter__(self): def __iter__(self):
for bound_field in self.bound_fields: for bound_field in self.bound_fields:
@ -881,7 +884,6 @@ class FieldSet(object):
def __len__(self): def __len__(self):
return len(self.field_lines) return len(self.field_lines)
class Admin: class Admin:
def __init__(self, fields=None, js=None, list_display=None, list_filter=None, date_hierarchy=None, def __init__(self, fields=None, js=None, list_display=None, list_filter=None, date_hierarchy=None,
save_as=False, ordering=None, search_fields=None, save_on_top=False): save_as=False, ordering=None, search_fields=None, save_on_top=False):
@ -894,34 +896,6 @@ class Admin:
self.search_fields = search_fields or [] self.search_fields = search_fields or []
self.save_on_top = save_on_top self.save_on_top = save_on_top
def get_field_objs(self, opts):
"""
Returns self.fields, except with fields as Field objects instead of
field names. If self.fields is None, defaults to putting every
non-AutoField field with editable=True in a single fieldset.
returns a list of lists of name, dict
the dict has attribs 'fields' and maybe 'classes'.
fields is a list of subclasses of Field.
"""
if self.fields is None:
field_struct = ((None, {'fields': [f.name for f in opts.fields + opts.many_to_many if f.editable and not isinstance(f, AutoField)]}),)
else:
field_struct = self.fields
new_fieldset_list = []
for fieldset in field_struct:
new_fieldset = [fieldset[0], {}]
new_fieldset[1].update(fieldset[1])
admin_fields = []
for field_name_or_list in fieldset[1]['fields']:
if isinstance(field_name_or_list, basestring):
admin_fields.append([opts.get_field(field_name_or_list)])
else:
admin_fields.append([opts.get_field(field_name) for field_name in field_name_or_list])
new_fieldset[1]['fields'] = admin_fields
new_fieldset_list.append(new_fieldset)
return new_fieldset_list
def get_field_sets(self, opts): def get_field_sets(self, opts):
if self.fields is None: if self.fields is None:
field_struct = ((None, { field_struct = ((None, {
@ -930,12 +904,11 @@ class Admin:
else: else:
field_struct = self.fields field_struct = self.fields
new_fieldset_list = [] new_fieldset_list = []
for fieldset in field_struct: for fieldset in field_struct:
name = fieldset[0] name = fieldset[0]
fs_options = fieldset[1] fs_options = fieldset[1]
classes = fs_options.get('classes', None) classes = fs_options.get('classes', () )
line_specs = fs_options['fields'] line_specs = fs_options['fields']
new_fieldset_list.append(FieldSet(name, classes, opts.get_field, line_specs) ) new_fieldset_list.append(FieldSet(name, classes, opts.get_field, line_specs) )
return new_fieldset_list return new_fieldset_list

View File

@ -30,6 +30,7 @@ for path in TEMPLATE_LOADERS:
try: try:
mod = __import__(module, globals(), locals(), [attr]) mod = __import__(module, globals(), locals(), [attr])
except ImportError, e: except ImportError, e:
raise e
raise ImproperlyConfigured, 'Error importing template source loader %s: "%s"' % (module, e) raise ImproperlyConfigured, 'Error importing template source loader %s: "%s"' % (module, e)
try: try:
func = getattr(mod, attr) func = getattr(mod, attr)
@ -42,10 +43,10 @@ for path in TEMPLATE_LOADERS:
template_source_loaders.append(func) template_source_loaders.append(func)
class LoaderOrigin(Origin): class LoaderOrigin(Origin):
def __init__(self, name, loader, name, dirs): def __init__(self, display_name, loader, name, dirs):
def reload(): def reload():
return loader(name, dirs)[0] return loader(name, dirs)[0]
super(LoaderOrigin, self).__init__(name) super(LoaderOrigin, self).__init__(display_name)
self._reload = reload self._reload = reload
def reload(self): def reload(self):

View File

@ -7,12 +7,17 @@ import os
# At compile time, cache the directories to search. # At compile time, cache the directories to search.
app_template_dirs = [] app_template_dirs = []
for app in INSTALLED_APPS: for app in INSTALLED_APPS:
i = app.rfind('.') try:
m, a = app[:i], app[i+1:] i = app.rfind('.')
mod = getattr(__import__(m, '', '', [a]), a) m, a = app[:i], app[i+1:]
template_dir = os.path.join(os.path.dirname(mod.__file__), 'templates') mod = getattr(__import__(m, '', '', [a]), a)
if os.path.isdir(template_dir): template_dir = os.path.join(os.path.dirname(mod.__file__), 'templates')
app_template_dirs.append(template_dir) if os.path.isdir(template_dir):
app_template_dirs.append(template_dir)
except Exception, e:
print "exception loading"
print e
raise e
# It won't change, so convert it to a tuple to save memory. # It won't change, so convert it to a tuple to save memory.
app_template_dirs = tuple(app_template_dirs) app_template_dirs = tuple(app_template_dirs)

View File

@ -19,44 +19,47 @@ for mod in modules:
# Add "get_thingie", "get_thingie_count" and "get_thingie_list" methods # Add "get_thingie", "get_thingie_count" and "get_thingie_list" methods
# for all related objects. # for all related objects.
for rel_obj, rel_field in klass._meta.get_all_related_objects(): for related in klass._meta.get_all_related_objects():
# Determine whether this related object is in another app. # Determine whether this related object is in another app.
# If it's in another app, the method names will have the app # If it's in another app, the method names will have the app
# label prepended, and the add_BLAH() method will not be # label prepended, and the add_BLAH() method will not be
# generated. # generated.
rel_mod = rel_obj.get_model_module() rel_mod = related.opts.get_model_module()
rel_obj_name = klass._meta.get_rel_object_method_name(rel_obj, rel_field) rel_obj_name = klass._meta.get_rel_object_method_name(related.opts, related.field)
if isinstance(rel_field.rel, meta.OneToOne): if isinstance(related.field.rel, meta.OneToOne):
# Add "get_thingie" methods for one-to-one related objects. # Add "get_thingie" methods for one-to-one related objects.
# EXAMPLE: Place.get_restaurants_restaurant() # EXAMPLE: Place.get_restaurants_restaurant()
func = curry(meta.method_get_related, 'get_object', rel_mod, rel_field) func = curry(meta.method_get_related, 'get_object', rel_mod, related.field)
func.__doc__ = "Returns the associated `%s.%s` object." % (rel_obj.app_label, rel_obj.module_name) func.__doc__ = "Returns the associated `%s.%s` object." % (related.opts.app_label, related.opts.module_name)
setattr(klass, 'get_%s' % rel_obj_name, func) setattr(klass, 'get_%s' % rel_obj_name, func)
elif isinstance(rel_field.rel, meta.ManyToOne): elif isinstance(related.field.rel, meta.ManyToOne):
# Add "get_thingie" methods for many-to-one related objects. # Add "get_thingie" methods for many-to-one related objects.
# EXAMPLE: Poll.get_choice() # EXAMPLE: Poll.get_choice()
func = curry(meta.method_get_related, 'get_object', rel_mod, rel_field) func = curry(meta.method_get_related, 'get_object', rel_mod, related.field)
func.__doc__ = "Returns the associated `%s.%s` object matching the given criteria." % (rel_obj.app_label, rel_obj.module_name) func.__doc__ = "Returns the associated `%s.%s` object matching the given criteria." % \
(related.opts.app_label, related.opts.module_name)
setattr(klass, 'get_%s' % rel_obj_name, func) setattr(klass, 'get_%s' % rel_obj_name, func)
# Add "get_thingie_count" methods for many-to-one related objects. # Add "get_thingie_count" methods for many-to-one related objects.
# EXAMPLE: Poll.get_choice_count() # EXAMPLE: Poll.get_choice_count()
func = curry(meta.method_get_related, 'get_count', rel_mod, rel_field) func = curry(meta.method_get_related, 'get_count', rel_mod, related.field)
func.__doc__ = "Returns the number of associated `%s.%s` objects." % (rel_obj.app_label, rel_obj.module_name) func.__doc__ = "Returns the number of associated `%s.%s` objects." % \
(related.opts.app_label, related.opts.module_name)
setattr(klass, 'get_%s_count' % rel_obj_name, func) setattr(klass, 'get_%s_count' % rel_obj_name, func)
# Add "get_thingie_list" methods for many-to-one related objects. # Add "get_thingie_list" methods for many-to-one related objects.
# EXAMPLE: Poll.get_choice_list() # EXAMPLE: Poll.get_choice_list()
func = curry(meta.method_get_related, 'get_list', rel_mod, rel_field) func = curry(meta.method_get_related, 'get_list', rel_mod, related.field)
func.__doc__ = "Returns a list of associated `%s.%s` objects." % (rel_obj.app_label, rel_obj.module_name) func.__doc__ = "Returns a list of associated `%s.%s` objects." % \
(related.opts.app_label, related.opts.module_name)
setattr(klass, 'get_%s_list' % rel_obj_name, func) setattr(klass, 'get_%s_list' % rel_obj_name, func)
# Add "add_thingie" methods for many-to-one related objects, # Add "add_thingie" methods for many-to-one related objects,
# but only for related objects that are in the same app. # but only for related objects that are in the same app.
# EXAMPLE: Poll.add_choice() # EXAMPLE: Poll.add_choice()
if rel_obj.app_label == klass._meta.app_label: if related.opts.app_label == klass._meta.app_label:
func = curry(meta.method_add_related, rel_obj, rel_mod, rel_field) func = curry(meta.method_add_related, related.opts, rel_mod, related.field)
func.alters_data = True func.alters_data = True
setattr(klass, 'add_%s' % rel_obj_name, func) setattr(klass, 'add_%s' % rel_obj_name, func)
del func del func
del rel_obj_name, rel_mod, rel_obj, rel_field # clean up del rel_obj_name, rel_mod, related # clean up
# Do the same for all related many-to-many objects. # Do the same for all related many-to-many objects.
for rel_opts, rel_field in klass._meta.get_all_related_many_to_many_objects(): for rel_opts, rel_field in klass._meta.get_all_related_many_to_many_objects():

View File

@ -100,6 +100,7 @@ class TestRunner:
self.output(1, "Creating test database") self.output(1, "Creating test database")
try: try:
cursor.execute("CREATE DATABASE %s" % TEST_DATABASE_NAME) cursor.execute("CREATE DATABASE %s" % TEST_DATABASE_NAME)
print "created"
except Exception, e: except Exception, e:
self.output(0, "There was an error creating the test database:%s " % str(e)) self.output(0, "There was an error creating the test database:%s " % str(e))
confirm = raw_input("The test database, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % TEST_DATABASE_NAME) confirm = raw_input("The test database, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % TEST_DATABASE_NAME)