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:
parent
5d74e282a4
commit
bd47a97997
@ -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
|
||||||
|
@ -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 %}
|
||||||
|
@ -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():
|
||||||
|
@ -88,10 +88,6 @@ 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:
|
for field in self.fields:
|
||||||
field.convert_post_data(new_data)
|
field.convert_post_data(new_data)
|
||||||
|
|
||||||
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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']}),
|
||||||
|
@ -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
|
||||||
|
@ -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):
|
||||||
|
@ -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:
|
||||||
|
try:
|
||||||
i = app.rfind('.')
|
i = app.rfind('.')
|
||||||
m, a = app[:i], app[i+1:]
|
m, a = app[:i], app[i+1:]
|
||||||
mod = getattr(__import__(m, '', '', [a]), a)
|
mod = getattr(__import__(m, '', '', [a]), a)
|
||||||
template_dir = os.path.join(os.path.dirname(mod.__file__), 'templates')
|
template_dir = os.path.join(os.path.dirname(mod.__file__), 'templates')
|
||||||
if os.path.isdir(template_dir):
|
if os.path.isdir(template_dir):
|
||||||
app_template_dirs.append(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)
|
||||||
|
@ -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():
|
||||||
|
@ -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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user