mirror of https://github.com/django/django.git
Added raw_id_admin support to ManyToManyField objects; fixes #260
git-svn-id: http://code.djangoproject.com/svn/django/trunk@516 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
935daf0626
commit
7d374ad597
|
@ -9,7 +9,12 @@ function showRelatedObjectLookupPopup(triggeringLink) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function dismissRelatedLookupPopup(win, chosenId) {
|
function dismissRelatedLookupPopup(win, chosenId) {
|
||||||
document.getElementById(win.name).value = chosenId;
|
var elem = document.getElementById(win.name);
|
||||||
|
if (elem.className.indexOf('vCommaSeparatedIntegerField') != -1 && elem.value) {
|
||||||
|
elem.value += ',' + chosenId;
|
||||||
|
} else {
|
||||||
|
document.getElementById(win.name).value = chosenId;
|
||||||
|
}
|
||||||
win.close();
|
win.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,10 @@ class Field(object):
|
||||||
self.radio_admin = radio_admin
|
self.radio_admin = radio_admin
|
||||||
self.help_text = help_text
|
self.help_text = help_text
|
||||||
if rel and isinstance(rel, ManyToMany):
|
if rel and isinstance(rel, ManyToMany):
|
||||||
self.help_text += ' Hold down "Control", or "Command" on a Mac, to select more than one.'
|
if rel.raw_id_admin:
|
||||||
|
self.help_text += ' Separate multiple IDs with commas.'
|
||||||
|
else:
|
||||||
|
self.help_text += ' Hold down "Control", or "Command" on a Mac, to select more than one.'
|
||||||
|
|
||||||
# Set db_index to True if the field has a relationship and doesn't explicitly set db_index.
|
# Set db_index to True if the field has a relationship and doesn't explicitly set db_index.
|
||||||
if db_index is None:
|
if db_index is None:
|
||||||
|
@ -572,17 +575,37 @@ class ManyToManyField(Field):
|
||||||
num_in_admin=kwargs.pop('num_in_admin', 0),
|
num_in_admin=kwargs.pop('num_in_admin', 0),
|
||||||
related_name=kwargs.pop('related_name', None),
|
related_name=kwargs.pop('related_name', None),
|
||||||
filter_interface=kwargs.pop('filter_interface', None),
|
filter_interface=kwargs.pop('filter_interface', None),
|
||||||
limit_choices_to=kwargs.pop('limit_choices_to', None))
|
limit_choices_to=kwargs.pop('limit_choices_to', None),
|
||||||
|
raw_id_admin=kwargs.pop('raw_id_admin', False))
|
||||||
|
if kwargs["rel"].raw_id_admin:
|
||||||
|
kwargs.setdefault("validator_list", []).append(self.isValidIDList)
|
||||||
Field.__init__(self, **kwargs)
|
Field.__init__(self, **kwargs)
|
||||||
|
|
||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
choices = self.get_choices(include_blank=False)
|
if self.rel.raw_id_admin:
|
||||||
return [curry(formfields.SelectMultipleField, size=min(max(len(choices), 5), 15), choices=choices)]
|
return [formfields.CommaSeparatedIntegerField]
|
||||||
|
else:
|
||||||
|
choices = self.get_choices(include_blank=False)
|
||||||
|
return [curry(formfields.SelectMultipleField, size=min(max(len(choices), 5), 15), choices=choices)]
|
||||||
|
|
||||||
def get_m2m_db_table(self, original_opts):
|
def get_m2m_db_table(self, original_opts):
|
||||||
"Returns the name of the many-to-many 'join' table."
|
"Returns the name of the many-to-many 'join' table."
|
||||||
return '%s_%s' % (original_opts.db_table, self.name)
|
return '%s_%s' % (original_opts.db_table, self.name)
|
||||||
|
|
||||||
|
def isValidIDList(self, field_data, all_data):
|
||||||
|
"Validates that the value is a valid list of foreign keys"
|
||||||
|
mod = self.rel.to.get_model_module()
|
||||||
|
try:
|
||||||
|
pks = map(int, field_data.split(','))
|
||||||
|
except ValueError:
|
||||||
|
# the CommaSeparatedIntegerField validator will catch this error
|
||||||
|
return
|
||||||
|
objects = mod.get_in_bulk(pks)
|
||||||
|
if len(objects) != len(pks):
|
||||||
|
badkeys = [k for k in pks if k not in objects]
|
||||||
|
raise validators.ValidationError, "Please enter valid %s IDs (the value%s %r %s invalid)" % \
|
||||||
|
(self.verbose_name, len(badkeys) > 1 and 's' or '', len(badkeys) == 1 and badkeys[0] or tuple(badkeys), len(badkeys) == 1 and "is" or "are")
|
||||||
|
|
||||||
class OneToOneField(IntegerField):
|
class OneToOneField(IntegerField):
|
||||||
def __init__(self, to, to_field=None, rel_name=None, **kwargs):
|
def __init__(self, to, to_field=None, rel_name=None, **kwargs):
|
||||||
kwargs['name'] = kwargs.get('name', 'id')
|
kwargs['name'] = kwargs.get('name', 'id')
|
||||||
|
@ -631,13 +654,15 @@ class ManyToOne:
|
||||||
|
|
||||||
class ManyToMany:
|
class ManyToMany:
|
||||||
def __init__(self, to, name, num_in_admin=0, related_name=None,
|
def __init__(self, to, name, num_in_admin=0, related_name=None,
|
||||||
filter_interface=None, limit_choices_to=None):
|
filter_interface=None, limit_choices_to=None, raw_id_admin=False):
|
||||||
self.to, self.name = to._meta, name
|
self.to, self.name = to._meta, name
|
||||||
self.num_in_admin = num_in_admin
|
self.num_in_admin = num_in_admin
|
||||||
self.related_name = related_name
|
self.related_name = related_name
|
||||||
self.filter_interface = filter_interface
|
self.filter_interface = filter_interface
|
||||||
self.limit_choices_to = limit_choices_to or {}
|
self.limit_choices_to = limit_choices_to or {}
|
||||||
self.edit_inline = False
|
self.edit_inline = False
|
||||||
|
self.raw_id_admin = raw_id_admin
|
||||||
|
assert not (self.raw_id_admin and self.filter_interface), "ManyToMany relationships may not use both raw_id_admin and filter_interface"
|
||||||
|
|
||||||
class OneToOne(ManyToOne):
|
class OneToOne(ManyToOne):
|
||||||
def __init__(self, to, name, field_name, num_in_admin=0, edit_inline=False,
|
def __init__(self, to, name, field_name, num_in_admin=0, edit_inline=False,
|
||||||
|
|
|
@ -510,7 +510,7 @@ def _get_flattened_data(field, val):
|
||||||
else:
|
else:
|
||||||
return {field.name: val}
|
return {field.name: val}
|
||||||
|
|
||||||
use_raw_id_admin = lambda field: isinstance(field.rel, meta.ManyToOne) and field.rel.raw_id_admin
|
use_raw_id_admin = lambda field: isinstance(field.rel, (meta.ManyToOne, meta.ManyToMany)) and field.rel.raw_id_admin
|
||||||
|
|
||||||
def _get_submit_row_template(opts, app_label, add, change, show_delete, ordered_objects):
|
def _get_submit_row_template(opts, app_label, add, change, show_delete, ordered_objects):
|
||||||
t = ['<div class="submit-row">']
|
t = ['<div class="submit-row">']
|
||||||
|
@ -722,8 +722,13 @@ def _get_admin_field(field_list, name_prefix, rel, add, change):
|
||||||
if change and field.primary_key:
|
if change and field.primary_key:
|
||||||
t.append('{{ %soriginal.%s }}' % ((rel and name_prefix or ''), field.name))
|
t.append('{{ %soriginal.%s }}' % ((rel and name_prefix or ''), field.name))
|
||||||
if change and use_raw_id_admin(field):
|
if change and use_raw_id_admin(field):
|
||||||
obj_repr = '%soriginal.get_%s|truncatewords:"14"' % (rel and name_prefix or '', field.rel.name)
|
if isinstance(field.rel, meta.ManyToOne):
|
||||||
t.append('{%% if %s %%} <strong>{{ %s }}</strong>{%% endif %%}' % (obj_repr, obj_repr))
|
if_bit = '%soriginal.get_%s' % (rel and name_prefix or '', field.rel.name)
|
||||||
|
obj_repr = if_bit + '|truncatewords:"14"'
|
||||||
|
elif isinstance(field.rel, meta.ManyToMany):
|
||||||
|
if_bit = '%soriginal.get_%s_list' % (rel and name_prefix or '', field.rel.name)
|
||||||
|
obj_repr = if_bit + '|join:", "|truncatewords:"14"'
|
||||||
|
t.append('{%% if %s %%} <strong>{{ %s }}</strong>{%% endif %%}' % (if_bit, obj_repr))
|
||||||
if field.help_text:
|
if field.help_text:
|
||||||
t.append('<p class="help">%s</p>\n' % field.help_text)
|
t.append('<p class="help">%s</p>\n' % field.help_text)
|
||||||
t.append('</div>\n\n')
|
t.append('</div>\n\n')
|
||||||
|
@ -766,6 +771,9 @@ def add_stage(request, app_label, module_name, show_delete=False, form_url='', p
|
||||||
new_data.update(request.FILES)
|
new_data.update(request.FILES)
|
||||||
errors = manipulator.get_validation_errors(new_data)
|
errors = manipulator.get_validation_errors(new_data)
|
||||||
if not errors and not request.POST.has_key("_preview"):
|
if not errors and not request.POST.has_key("_preview"):
|
||||||
|
for f in opts.many_to_many:
|
||||||
|
if f.rel.raw_id_admin:
|
||||||
|
new_data.setlist(f.name, new_data[f.name].split(","))
|
||||||
manipulator.do_html2python(new_data)
|
manipulator.do_html2python(new_data)
|
||||||
new_object = manipulator.save(new_data)
|
new_object = manipulator.save(new_data)
|
||||||
pk_value = getattr(new_object, opts.pk.name)
|
pk_value = getattr(new_object, opts.pk.name)
|
||||||
|
@ -804,7 +812,7 @@ def add_stage(request, app_label, module_name, show_delete=False, form_url='', p
|
||||||
# In required many-to-many fields with only one available choice,
|
# In required many-to-many fields with only one available choice,
|
||||||
# select that one available choice.
|
# select that one available choice.
|
||||||
for f in opts.many_to_many:
|
for f in opts.many_to_many:
|
||||||
if not f.blank and not f.rel.edit_inline and len(manipulator[f.name].choices) == 1:
|
if not f.blank and not f.rel.edit_inline and not f.rel.raw_id_admin and len(manipulator[f.name].choices) == 1:
|
||||||
new_data[f.name] = [manipulator[f.name].choices[0][0]]
|
new_data[f.name] = [manipulator[f.name].choices[0][0]]
|
||||||
# Add default data for related objects.
|
# Add default data for related objects.
|
||||||
for rel_opts, rel_field in opts.get_inline_related_objects():
|
for rel_opts, rel_field in opts.get_inline_related_objects():
|
||||||
|
@ -855,13 +863,18 @@ def change_stage(request, app_label, module_name, object_id):
|
||||||
manipulator = mod.ChangeManipulator(object_id)
|
manipulator = mod.ChangeManipulator(object_id)
|
||||||
except ObjectDoesNotExist:
|
except ObjectDoesNotExist:
|
||||||
raise Http404
|
raise Http404
|
||||||
|
|
||||||
inline_related_objects = opts.get_inline_related_objects()
|
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):
|
||||||
new_data.update(request.FILES)
|
new_data.update(request.FILES)
|
||||||
|
|
||||||
errors = manipulator.get_validation_errors(new_data)
|
errors = manipulator.get_validation_errors(new_data)
|
||||||
if not errors and not request.POST.has_key("_preview"):
|
if not errors and not request.POST.has_key("_preview"):
|
||||||
|
for f in opts.many_to_many:
|
||||||
|
if f.rel.raw_id_admin:
|
||||||
|
new_data.setlist(f.name, new_data[f.name].split(","))
|
||||||
manipulator.do_html2python(new_data)
|
manipulator.do_html2python(new_data)
|
||||||
new_object = manipulator.save(new_data)
|
new_object = manipulator.save(new_data)
|
||||||
pk_value = getattr(new_object, opts.pk.name)
|
pk_value = getattr(new_object, opts.pk.name)
|
||||||
|
@ -904,7 +917,9 @@ def change_stage(request, app_label, module_name, object_id):
|
||||||
for f in opts.fields:
|
for f in opts.fields:
|
||||||
new_data.update(_get_flattened_data(f, getattr(obj, f.name)))
|
new_data.update(_get_flattened_data(f, getattr(obj, f.name)))
|
||||||
for f in opts.many_to_many:
|
for f in opts.many_to_many:
|
||||||
if not f.rel.edit_inline:
|
if f.rel.raw_id_admin:
|
||||||
|
new_data[f.name] = ",".join([str(i.id) for i in getattr(obj, 'get_%s_list' % f.rel.name)()])
|
||||||
|
elif not f.rel.edit_inline:
|
||||||
new_data[f.name] = [i.id for i in getattr(obj, 'get_%s_list' % f.rel.name)()]
|
new_data[f.name] = [i.id for i in getattr(obj, 'get_%s_list' % f.rel.name)()]
|
||||||
for rel_obj, rel_field in inline_related_objects:
|
for rel_obj, rel_field in inline_related_objects:
|
||||||
var_name = rel_obj.object_name.lower()
|
var_name = rel_obj.object_name.lower()
|
||||||
|
|
Loading…
Reference in New Issue