diff --git a/django/conf/admin_media/js/urlify.js b/django/conf/admin_media/js/urlify.js
index 412130ad6f..4a453ed9eb 100644
--- a/django/conf/admin_media/js/urlify.js
+++ b/django/conf/admin_media/js/urlify.js
@@ -10,7 +10,7 @@ function URLify(s, num_chars) {
s = s.replace(r, '');
s = s.replace(/[^\w\s]/g, ''); // remove unneeded chars
s = s.replace(/^\s+|\s+$/g, ''); // trim leading/trailing spaces
- s = s.replace(/\s+/g, '_'); // convert spaces to underscores
+ s = s.replace(/\s+/g, '-'); // convert spaces to dashes
s = s.toLowerCase(); // convert to lowercase
return s.substring(0, num_chars);// trim to first num_chars chars
-}
\ No newline at end of file
+}
diff --git a/django/conf/admin_templates/admin_change_form.html b/django/conf/admin_templates/admin_change_form.html
new file mode 100644
index 0000000000..2beac80272
--- /dev/null
+++ b/django/conf/admin_templates/admin_change_form.html
@@ -0,0 +1,107 @@
+{% extends "base_site" %}
+{% 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 %}
+
+{% if change %}
+ {% if not is_popup %}
+
+ {% endif %}
+{% endif %}
+
+
+
+{% endblock %}
diff --git a/django/conf/admin_templates/admin_edit_inline_stacked.html b/django/conf/admin_templates/admin_edit_inline_stacked.html
new file mode 100644
index 0000000000..66b93d4ffc
--- /dev/null
+++ b/django/conf/admin_templates/admin_edit_inline_stacked.html
@@ -0,0 +1,16 @@
+
+
diff --git a/django/conf/admin_templates/admin_edit_inline_tabular.html b/django/conf/admin_templates/admin_edit_inline_tabular.html
new file mode 100644
index 0000000000..e8882fa627
--- /dev/null
+++ b/django/conf/admin_templates/admin_edit_inline_tabular.html
@@ -0,0 +1,44 @@
+
+
diff --git a/django/conf/admin_templates/admin_field.html b/django/conf/admin_templates/admin_field.html
new file mode 100644
index 0000000000..9a2228ac41
--- /dev/null
+++ b/django/conf/admin_templates/admin_field.html
@@ -0,0 +1,36 @@
+
+ {% for bound_field in bound_fields %}
+ {{ bound_field.html_error_list }}
+ {% endfor %}
+
+ {% for bound_field in bound_fields %}
+ {% if bound_field.has_label_first %}
+ {% field_label bound_field %}
+ {% endif %}
+
+ {% field_widget bound_field %}
+
+ {% if not bound_field.has_label_first %}
+ {% field_label bound_field %}
+ {% endif %}
+
+ {% if change %}
+ {% if bound_field.field.primary_key %}
+ {{ bound_field.original_value }}
+ {% endif %}
+
+ {% if bound_field.raw_id_admin %}
+ {% if bound_field.existing_repr %}
+
{{ bound_field.existing_repr|truncatewords:"14" }}
+ {% endif %}
+ {% endif %}
+ {% endif %}
+
+ {% if bound_field.field.help_text %}
+
+ {{bound_field.field.help_text}}
+
+ {% endif %}
+ {% endfor %}
+
+
diff --git a/django/conf/admin_templates/admin_field_widget.html b/django/conf/admin_templates/admin_field_widget.html
new file mode 100644
index 0000000000..6a882cd2b6
--- /dev/null
+++ b/django/conf/admin_templates/admin_field_widget.html
@@ -0,0 +1,27 @@
+{% if bound_field.is_date_time %}
+
+ Date: {{ bound_field.form_fields.0 }}
+ Time: {{ bound_field.form_fields.1 }}
+
+{% else %}
+ {% if bound_field.is_file_field %}
+ {% if bound_field.original_value %}
+ Currently:
{{ bound_field.original_value }}
+ Change: {% output_all bound_field.form_fields %}
+ {% else %}
+ {% output_all bound_field.form_fields %}
+ {% endif %}
+ {% else %}
+ {% output_all bound_field.form_fields %}
+ {% if bound_field.raw_id_admin %}
+

+ {% else %}
+ {% if bound_field.needs_add_label %}
+

+ {% endif %}
+ {% endif %}
+ {% endif %}
+{% endif %}
+
+
+
diff --git a/django/conf/urls/admin.py b/django/conf/urls/admin.py
index 841d50523e..0cd784aa32 100644
--- a/django/conf/urls/admin.py
+++ b/django/conf/urls/admin.py
@@ -48,11 +48,13 @@ if 'ellington.media' in INSTALLED_APPS:
urlpatterns += (
# Metasystem admin pages
+ ('^(?P
[^/]+)/(?P[^/]+)/add_old/$', 'django.views.admin.main.add_stage'),
+ ('^(?P[^/]+)/(?P[^/]+)/(?P.+)_old/$', 'django.views.admin.main.change_stage'),
('^(?P[^/]+)/(?P[^/]+)/$', 'django.views.admin.main.change_list'),
- ('^(?P[^/]+)/(?P[^/]+)/add/$', 'django.views.admin.main.add_stage'),
+ ('^(?P[^/]+)/(?P[^/]+)/add/$', 'django.views.admin.main.add_stage_new'),
('^(?P[^/]+)/(?P[^/]+)/jsvalidation/$', 'django.views.admin.jsvalidation.jsvalidation'),
('^(?P[^/]+)/(?P[^/]+)/(?P.+)/history/$', 'django.views.admin.main.history'),
('^(?P[^/]+)/(?P[^/]+)/(?P.+)/delete/$', 'django.views.admin.main.delete_stage'),
- ('^(?P[^/]+)/(?P[^/]+)/(?P.+)/$', 'django.views.admin.main.change_stage'),
+ ('^(?P[^/]+)/(?P[^/]+)/(?P.+)/$', 'django.views.admin.main.change_stage_new'),
)
urlpatterns = patterns('', *urlpatterns)
diff --git a/django/core/defaulttags.py b/django/core/defaulttags.py
index 103eb4c9f4..81bce67bd4 100644
--- a/django/core/defaulttags.py
+++ b/django/core/defaulttags.py
@@ -2,6 +2,7 @@
import sys
import template
+import template_loader
class CommentNode(template.Node):
def render(self, context):
@@ -223,6 +224,19 @@ class SsiNode(template.Node):
return '' # Fail silently for invalid included templates.
return output
+class IncludeNode(template.Node):
+ def __init__(self, template_path):
+ self.template_path_var = template_path_var
+
+ def render(self, context):
+ try:
+ template_path = template.resolve(self.template_path_var, context)
+ t = template_loader.get_template(template_path)
+ return t.render(context)
+ except:
+ return '' # Fail silently for invalid included templates.
+
+
class LoadNode(template.Node):
def __init__(self, taglib):
self.taglib = taglib
@@ -600,6 +614,16 @@ def do_ssi(parser, token):
raise template.TemplateSyntaxError, "Second (optional) argument to %s tag must be 'parsed'" % bits[0]
return SsiNode(bits[1], parsed)
+def do_include(parser, token):
+ """
+ Loads a template using standard resolution mechanisms, and renders it in the current context.
+ """
+ bits = token.contents.split()
+ parsed = False
+ if len(bits) != 2:
+ raise template.TemplateSyntaxError, "'include' tag takes one argument: the path to the template to be included"
+ return IncludeNode(bits[1])
+
def do_load(parser, token):
"""
Load a custom template tag set.
@@ -755,6 +779,7 @@ template.register_tag('ifequal', lambda parser, token: do_ifequal(parser, token,
template.register_tag('ifnotequal', lambda parser, token: do_ifequal(parser, token, True))
template.register_tag('if', do_if)
template.register_tag('ifchanged', do_ifchanged)
+template.register_tag('include', do_include)
template.register_tag('regroup', do_regroup)
template.register_tag('ssi', do_ssi)
template.register_tag('load', do_load)
diff --git a/django/core/formfields.py b/django/core/formfields.py
index 7587b67170..cae6aadb9a 100644
--- a/django/core/formfields.py
+++ b/django/core/formfields.py
@@ -22,7 +22,7 @@ class Manipulator:
for field in self.fields:
if field.field_name == field_name:
return field
- raise KeyError, "Field %s not found" % field_name
+ raise KeyError, "Field %s not found\n%s" % (field_name, repr(self.fields))
def __delitem__(self, field_name):
"Deletes the field with the given field name; raises KeyError on failure"
@@ -87,16 +87,12 @@ class Manipulator:
must happen after validation because html2python functions aren't
expected to deal with invalid input.
"""
- for field in self.fields:
- if new_data.has_key(field.field_name):
- new_data.setlist(field.field_name,
- [field.__class__.html2python(data) for data in new_data.getlist(field.field_name)])
- else:
- try:
- # individual fields deal with None values themselves
- new_data.setlist(field.field_name, [field.__class__.html2python(None)])
- except EmptyValue:
- new_data.setlist(field.field_name, [])
+ """
+ for field in self.fields:
+ """
+
+ for field in self.fields:
+ field.convert_post_data(new_data)
class FormWrapper:
"""
@@ -104,25 +100,40 @@ class FormWrapper:
This allows dictionary-style lookups of formfields. It also handles feeding
prepopulated data and validation error messages to the formfield objects.
"""
- def __init__(self, manipulator, data, error_dict):
+ def __init__(self, manipulator, data, error_dict, edit_inline = False):
self.manipulator, self.data = manipulator, data
self.error_dict = error_dict
+ self._inline_collections = None
+ self.edit_inline = edit_inline
def __repr__(self):
- return repr(self.data)
+ return repr(self.__dict__)
def __getitem__(self, key):
for field in self.manipulator.fields:
if field.field_name == key:
- if hasattr(field, 'requires_data_list') and hasattr(self.data, 'getlist'):
- data = self.data.getlist(field.field_name)
- else:
- data = self.data.get(field.field_name, None)
- if data is None:
- data = ''
- return FormFieldWrapper(field, data, self.error_dict.get(field.field_name, []))
+ data = field.extract_data(self.data)
+ return FormFieldWrapper(field, data, self.error_dict.get(field.field_name, []))
+ if self.edit_inline:
+ self.fill_inline_collections()
+ for inline_collection in self._inline_collections:
+ if inline_collection.name == key:
+ return inline_collection
+
raise KeyError
+ def fill_inline_collections(self):
+ if not self._inline_collections:
+ ic = []
+ related_objects = self.manipulator.get_inline_related_objects_wrapped()
+ for rel_obj in related_objects:
+ data = rel_obj.extract_data(self.data)
+ inline_collection = InlineObjectCollection(self.manipulator, rel_obj, data, self.error_dict)
+ ic.append(inline_collection)
+ self._inline_collections = ic
+
+
+
def has_errors(self):
return self.error_dict != {}
@@ -135,6 +146,7 @@ class FormFieldWrapper:
def __str__(self):
"Renders the field"
return str(self.formfield.render(self.data))
+
def __repr__(self):
return '' % self.formfield.field_name
@@ -155,6 +167,9 @@ class FormFieldWrapper:
else:
return ''
+ def get_id(self):
+ return self.formfield.get_id()
+
class FormFieldCollection(FormFieldWrapper):
"A utility class that gives the template access to a dict of FormFieldWrappers"
def __init__(self, formfield_dict):
@@ -174,9 +189,67 @@ class FormFieldCollection(FormFieldWrapper):
"Returns list of all errors in this collection's formfields"
errors = []
for field in self.formfield_dict.values():
- errors.extend(field.errors())
+ if(hasattr(field, 'errors') ):
+ errors.extend(field.errors())
return errors
+ def has_errors(self):
+ return bool(len(self.errors()))
+
+ def html_combined_error_list(self):
+ return ''.join( [ field.html_error_list() for field in self.formfield_dict.values() if hasattr(field, 'errors')])
+
+class InlineObjectCollection:
+ "An object that acts like a list of form field collections."
+ def __init__(self, parent_manipulator, rel_obj, data, errors):
+ self.parent_manipulator = parent_manipulator
+ self.rel_obj = rel_obj
+ self.data = data
+ self.errors = errors
+ self._collections = None
+ self.name = rel_obj.name
+
+ def __len__(self):
+ self.fill()
+ return self._collections.__len__()
+
+ def __getitem__(self, k):
+ self.fill()
+ return self._collections.__getitem__(k)
+
+ def __setitem__(self, k, v):
+ self.fill()
+ return self._collections.__setitem__(k,v)
+
+ def __delitem__(self, k):
+ self.fill()
+ return self._collections.__delitem__(k)
+
+ def __iter__(self):
+ self.fill()
+ return self._collections.__iter__()
+
+ def fill(self):
+ if self._collections:
+ return
+ else:
+ var_name = self.rel_obj.opts.object_name.lower()
+ wrapper = []
+ orig = hasattr(self.parent_manipulator, 'original_object') and self.parent_manipulator.original_object or None
+ orig_list = self.rel_obj.get_list(orig)
+ for i, instance in enumerate(orig_list):
+ collection = {'original': instance }
+ for f in self.rel_obj.editable_fields():
+ for field_name in f.get_manipulator_field_names(''):
+ full_field_name = '%s.%d.%s' % (var_name, i, field_name)
+ field = self.parent_manipulator[full_field_name]
+ data = field.extract_data(self.data)
+ errors = self.errors.get(full_field_name, [])
+# if(errors):raise full_field_name + " " + repr(errors)
+ collection[field_name] = FormFieldWrapper(field, data, errors)
+ wrapper.append(FormFieldCollection(collection))
+ self._collections = wrapper
+
class FormField:
"""Abstract class representing a form field.
@@ -209,6 +282,39 @@ class FormField:
def render(self, data):
raise NotImplementedError
+ def get_member_name(self):
+ if hasattr(self, 'member_name'):
+ return self.member_name
+ else:
+ return self.field_name
+
+ def extract_data(self, data_dict):
+ if hasattr(self, 'requires_data_list') and hasattr(data_dict, 'getlist'):
+ data = data_dict.getlist(self.get_member_name())
+ else:
+ data = data_dict.get(self.get_member_name(), None)
+ if data is None:
+ data = ''
+ self.data_dict = data_dict
+ return data
+
+ def convert_post_data(self, new_data):
+ name = self.get_member_name()
+ if new_data.has_key(name):
+ d = new_data.getlist(name)
+ #del new_data[self.field_name]
+ new_data.setlist(name,
+ [self.__class__.html2python(data)
+ for data in d])
+ else:
+ try:
+ # individual fields deal with None values themselves
+ new_data.setlist(name, [self.__class__.html2python(None)])
+ except EmptyValue:
+ new_data.setlist(name, [])
+
+ def get_id(self):
+ return FORM_FIELD_ID_PREFIX + self.field_name
####################
# GENERIC WIDGETS #
####################
@@ -237,7 +343,7 @@ class TextField(FormField):
if isinstance(data, unicode):
data = data.encode('utf-8')
return '' % \
- (FORM_FIELD_ID_PREFIX + self.field_name, self.__class__.__name__, self.is_required and ' required' or '',
+ (self.get_id(), self.__class__.__name__, self.is_required and ' required' or '',
self.field_name, self.length, escape(data), maxlength)
def html2python(data):
@@ -248,7 +354,7 @@ class PasswordField(TextField):
def render(self, data):
# value is always blank because we never want to redisplay it
return '' % \
- (FORM_FIELD_ID_PREFIX + self.field_name, self.__class__.__name__, self.is_required and ' required' or '',
+ (self.get_id(), self.__class__.__name__, self.is_required and ' required' or '',
self.field_name)
class LargeTextField(TextField):
@@ -266,7 +372,7 @@ class LargeTextField(TextField):
if isinstance(data, unicode):
data = data.encode('utf-8')
return '' % \
- (FORM_FIELD_ID_PREFIX + self.field_name, self.__class__.__name__, self.is_required and ' required' or '',
+ (self.get_id(), self.__class__.__name__, self.is_required and ' required' or '',
self.field_name, self.rows, self.cols, escape(data))
class HiddenField(FormField):
@@ -276,7 +382,7 @@ class HiddenField(FormField):
def render(self, data):
return '' % \
- (FORM_FIELD_ID_PREFIX + self.field_name, self.field_name, escape(data))
+ (self.get_id(), self.field_name, escape(data))
class CheckboxField(FormField):
def __init__(self, field_name, checked_by_default=False):
@@ -289,7 +395,7 @@ class CheckboxField(FormField):
if data or (data is '' and self.checked_by_default):
checked_html = ' checked="checked"'
return '' % \
- (FORM_FIELD_ID_PREFIX + self.field_name, self.__class__.__name__,
+ (self.get_id(), self.__class__.__name__,
self.field_name, checked_html)
def html2python(data):
@@ -299,18 +405,21 @@ class CheckboxField(FormField):
return False
html2python = staticmethod(html2python)
+
class SelectField(FormField):
- def __init__(self, field_name, choices=[], size=1, is_required=False, validator_list=[]):
+ def __init__(self, field_name, choices=[], size=1, is_required=False, validator_list=[], member_name=None):
self.field_name = field_name
# choices is a list of (value, human-readable key) tuples because order matters
self.choices, self.size, self.is_required = choices, size, is_required
self.validator_list = [self.isValidChoice] + validator_list
+ if member_name != None:
+ self.member_name = member_name
def render(self, data):
- output = [' \n')
return t
+def get_javascript_imports(opts,auto_populated_fields, ordered_objects, admin_field_objs):
+# Put in any necessary JavaScript imports.
+ js = ['js/core.js', 'js/admin/RelatedObjectLookups.js']
+ if auto_populated_fields:
+ js.append('js/urlify.js')
+ if opts.has_field_type(meta.DateTimeField) or opts.has_field_type(meta.TimeField) or opts.has_field_type(meta.DateField):
+ js.extend(['js/calendar.js', 'js/admin/DateTimeShortcuts.js'])
+ if ordered_objects:
+ js.extend(['js/getElementsBySelector.js', 'js/dom-drag.js' , 'js/admin/ordering.js'])
+ if opts.admin.js:
+ js.extend(opts.admin.js)
+ seen_collapse = False
+ for _, options in admin_field_objs:
+ if not seen_collapse and 'collapse' in options.get('classes', ''):
+ seen_collapse = True
+ js.append('js/admin/CollapsedFieldsets.js' )
+ try:
+ for field_list in options['fields']:
+ for f in field_list:
+ if f.rel and isinstance(f, meta.ManyToManyField) and f.rel.filter_interface:
+ js.extend(['js/SelectBox.js' , 'js/SelectFilter2.js'])
+ raise StopIteration
+ except StopIteration:
+ break
+ return js
+
+class BoundField(object):
+ def __init__(self, field, original, rel, field_mapping):
+ self.field = field
+ self.form_fields = self.resolve_form_fields(field_mapping)
+ self.original = original
+ self.rel = rel
+
+ 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]
+
+class AdminBoundField(BoundField):
+ def __init__(self, field, original, rel, field_mapping):
+ super(AdminBoundField, self).__init__(field,original, rel, field_mapping)
+
+ self.element_id = self.form_fields[0].get_id()
+ self.has_label_first = not isinstance(self.field, meta.BooleanField)
+ self.raw_id_admin = use_raw_id_admin(field)
+ self.is_date_time = isinstance(field, meta.DateTimeField)
+ self.is_file_field = isinstance(field, meta.FileField)
+ self.needs_add_label = field.rel and isinstance(field.rel, meta.ManyToOne) or isinstance(field.rel, meta.ManyToMany) and field.rel.to.admin
+ self.not_in_table = isinstance(self.field, meta.AutoField)
+ self.first = True
+
+ classes = []
+ if(self.raw_id_admin):
+ classes.append('nowrap')
+ if max([bool(f.errors()) for f in self.form_fields]):
+ classes.append('error')
+ self.cell_class_attribute = ' '.join(classes)
+ self._repr_filled = False
+
+
+
+ def _fetch_existing_repr(self, func_name):
+ class_dict = self.original.__class__.__dict__
+ func = class_dict.get(func_name)
+ return func(self.original)
+
+ def _fill_existing_repr(self):
+ if self._repr_filled:
+ return
+ #HACK
+ if isinstance(self.field.rel, meta.ManyToOne):
+ func_name = 'get_%s' % self.field.name
+ self._repr = self._fetch_existing_repr(func_name)
+ elif isinstance(self.field.rel, meta.ManyToMany):
+ func_name = 'get_%s_list' % self.field.name
+ self._repr = ",".join(self._fetch_existing_repr(func_name))
+ self._repr_filled = True
+
+ def existing_repr(self):
+ self._fill_existing_repr()
+ return self._repr
+
+ def __repr__(self):
+ return repr(self.__dict__)
+
+ def html_error_list(self):
+ return " ".join([form_field.html_error_list() for form_field in self.form_fields if form_field.errors])
+
+
+class AdminFieldSet(object):
+ def __init__(self, fieldset_name, options, form, original):
+ self.name = fieldset_name
+ self.options = options
+ self.bound_field_sets = self.get_bound_field_sets(form, original)
+ self.classes = options.get('classes', '')
+
+ def __repr__(self):
+ return "Fieldset:(%s,%s)" % (self.name, self.bound_field_sets)
+
+ def get_bound_field_sets(self, form, original):
+ fields = self.options['fields']
+ bound_field_sets = [ [AdminBoundField(f, original, False, form) for f in field ] for field in fields]
+ for set in bound_field_sets:
+ first = True
+ for bound_field in set:
+ bound_field.first = first
+ first = False
+
+ return bound_field_sets
+
+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]
+
+ 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')
+
+ form_enc_attrib = opts.has_field_type(meta.FileField) and 'enctype="multipart/form-data" ' or ''
+
+ form = context['form']
+ original = context['original']
+ admin_fieldsets = [AdminFieldSet(name, options, form, original) for name, options in admin_field_objs]
+ inline_related_objects = opts.get_inline_related_objects_wrapped()
+
+ ordered_object_names = ' '.join(['object.%s' % o.pk.name for o in ordered_objects])
+
+ extra_context = {
+ 'add': add,
+ 'change': change,
+ 'admin_field_objs' : admin_field_objs,
+ 'ordered_objects' : ordered_objects,
+ 'auto_populated_fields' : auto_populated_fields,
+ 'javascript_imports' : javascript_imports,
+ 'coltype' : coltype,
+ 'has_absolute_url': has_absolute_url,
+ 'form_enc_attrib': form_enc_attrib,
+ 'form_url' : form_url,
+ 'admin_fieldsets' : admin_fieldsets,
+ 'inline_related_objects': inline_related_objects,
+ 'ordered_object_names' : ordered_object_names,
+ 'content_type_id' : opts.get_content_type_id(),
+ 'save_on_top' : opts.admin.save_on_top,
+ 'verbose_name_plural': opts.verbose_name_plural,
+ 'save_as': opts.admin.save_as,
+ 'app_label': app_label,
+ 'object_name': opts.object_name,
+ 'has_delete_permission' : context['perms'][app_label][opts.get_delete_permission()]
+ }
+
+ context.update(extra_context)
+
+
+def add_stage_new(request, app_label, module_name, show_delete=False, form_url='', post_url='../', post_url_continue='../%s/', object_id_override=None):
+ mod, opts = _get_mod_opts(app_label, module_name)
+ if not request.user.has_perm(app_label + '.' + opts.get_add_permission()):
+ raise PermissionDenied
+ manipulator = mod.AddManipulator()
+ if request.POST:
+ new_data = request.POST.copy()
+ if opts.has_field_type(meta.FileField):
+ new_data.update(request.FILES)
+ errors = manipulator.get_validation_errors(new_data)
+ 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)
+ new_object = manipulator.save(new_data)
+ pk_value = getattr(new_object, opts.pk.column)
+ log.log_action(request.user.id, opts.get_content_type_id(), pk_value, repr(new_object), log.ADDITION)
+ msg = 'The %s "%s" was added successfully.' % (opts.verbose_name, new_object)
+ # Here, we distinguish between different save types by checking for
+ # the presence of keys in request.POST.
+ if request.POST.has_key("_continue"):
+ request.user.add_message("%s You may edit it again below." % msg)
+ if request.POST.has_key("_popup"):
+ post_url_continue += "?_popup=1"
+ return HttpResponseRedirect(post_url_continue % pk_value)
+ if request.POST.has_key("_popup"):
+ return HttpResponse('' % \
+ (pk_value, repr(new_object).replace('"', '\\"')))
+ elif request.POST.has_key("_addanother"):
+ request.user.add_message("%s You may add another %s below." % (msg, opts.verbose_name))
+ return HttpResponseRedirect(request.path)
+ else:
+ request.user.add_message(msg)
+ return HttpResponseRedirect(post_url)
+ if request.POST.has_key("_preview"):
+ manipulator.do_html2python(new_data)
+ else:
+ # Add default data.
+ new_data = manipulator.flatten_data()
+
+ # Override the defaults with request.GET, if it exists.
+ new_data.update(request.GET)
+ errors = {}
+
+ # Populate the FormWrapper.
+ form = formfields.FormWrapper(manipulator, new_data, errors, edit_inline=True)
+
+ c = Context(request, {
+ 'title': 'Add %s' % opts.verbose_name,
+ 'form': form,
+ 'is_popup': request.REQUEST.has_key('_popup'),
+ })
+ if object_id_override is not None:
+ c['object_id'] = object_id_override
+
+
+ fill_extra_context(opts, app_label, c, change=True)
+
+ return render_to_response("admin_change_form", context_instance=c)
+
+
+
+def change_stage_new(request, app_label, module_name, object_id):
+ mod, opts = _get_mod_opts(app_label, module_name)
+ if not request.user.has_perm(app_label + '.' + opts.get_change_permission()):
+ raise PermissionDenied
+ if request.POST and request.POST.has_key("_saveasnew"):
+ return add_stage_new(request, app_label, module_name, form_url='../add/')
+ try:
+ manipulator = mod.ChangeManipulator(object_id)
+ except ObjectDoesNotExist:
+ raise Http404
+
+ inline_related_objects = opts.get_inline_related_objects()
+ if request.POST:
+ new_data = request.POST.copy()
+ if opts.has_field_type(meta.FileField):
+ new_data.update(request.FILES)
+
+ errors = manipulator.get_validation_errors(new_data)
+ 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)
+ new_object = manipulator.save(new_data)
+ pk_value = getattr(new_object, opts.pk.column)
+
+ # Construct the change message.
+ change_message = []
+ if manipulator.fields_added:
+ change_message.append('Added %s.' % get_text_list(manipulator.fields_added, 'and'))
+ if manipulator.fields_changed:
+ change_message.append('Changed %s.' % get_text_list(manipulator.fields_changed, 'and'))
+ if manipulator.fields_deleted:
+ change_message.append('Deleted %s.' % get_text_list(manipulator.fields_deleted, 'and'))
+ change_message = ' '.join(change_message)
+ if not change_message:
+ change_message = 'No fields changed.'
+
+ log.log_action(request.user.id, opts.get_content_type_id(), pk_value, repr(new_object), log.CHANGE, change_message)
+ msg = 'The %s "%s" was changed successfully.' % (opts.verbose_name, new_object)
+ if request.POST.has_key("_continue"):
+ request.user.add_message("%s You may edit it again below." % msg)
+ if request.REQUEST.has_key('_popup'):
+ return HttpResponseRedirect(request.path + "?_popup=1")
+ else:
+ return HttpResponseRedirect(request.path)
+ elif request.POST.has_key("_saveasnew"):
+ request.user.add_message('The %s "%s" was added successfully. You may edit it again below.' % (opts.verbose_name, new_object))
+ return HttpResponseRedirect("../%s/" % pk_value)
+ elif request.POST.has_key("_addanother"):
+ request.user.add_message("%s You may add another %s below." % (msg, opts.verbose_name))
+ return HttpResponseRedirect("../add/")
+ else:
+ request.user.add_message(msg)
+ return HttpResponseRedirect("../")
+ if request.POST.has_key("_preview"):
+ manipulator.do_html2python(new_data)
+ else:
+ # Populate new_data with a "flattened" version of the current data.
+ new_data = manipulator.flatten_data()
+
+
+ # If the object has ordered objects on its admin page, get the existing
+ # order and flatten it into a comma-separated list of IDs.
+ id_order_list = []
+ for rel_obj in opts.get_ordered_objects():
+ id_order_list.extend(getattr(obj, 'get_%s_order' % rel_obj.object_name.lower())())
+ if id_order_list:
+ new_data['order_'] = ','.join(map(str, id_order_list))
+ errors = {}
+
+ # Populate the FormWrapper.
+ form = formfields.FormWrapper(manipulator, new_data, errors, edit_inline = True)
+ form.original = manipulator.original_object
+ form.order_objects = []
+
+ for rel_opts, rel_field in inline_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:
+ form.order_objects.extend(orig_list)
+
+ c = Context(request, {
+ 'title': 'Change %s' % opts.verbose_name,
+ 'form': form,
+ 'object_id': object_id,
+ 'original': manipulator.original_object,
+ 'is_popup' : request.REQUEST.has_key('_popup')
+ })
+
+ fill_extra_context(opts, app_label, c, change=True)
+
+ #t = template_loader.get_template_from_string(raw_template)
+
+ return render_to_response('admin_change_form', context_instance=c);
+
+
def _get_template(opts, app_label, add=False, change=False, show_delete=False, form_url=''):
admin_field_objs = opts.admin.get_field_objs(opts)
ordered_objects = opts.get_ordered_objects()[:]
@@ -608,6 +912,7 @@ def _get_template(opts, app_label, add=False, change=False, show_delete=False, f
for rel_obj, rel_field in opts.get_inline_related_objects():
var_name = rel_obj.object_name.lower()
field_list = [f for f in rel_obj.fields + rel_obj.many_to_many if f.editable and f != rel_field]
+
t.append('