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

Added templates for 'funny' widgets here: django/conf/admin_templates/widget

Added a follow argument to manipulators. This allows you to exclude/include fields and related objects ( recursively), defaulting to current behaviour, meaning that forms can modify a subset of fields. This should fix #445. Fixed update generic view to use this (will change others soon).  Merged to trunk r772. 



git-svn-id: http://code.djangoproject.com/svn/django/branches/new-admin@773 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Robert Wittams 2005-10-04 16:35:39 +00:00
parent 1c9cf48651
commit 555502e208
10 changed files with 232 additions and 120 deletions

View File

@ -0,0 +1,4 @@
<p class="datetime">
Date: {{ bound_field.form_fields.0 }}<br />
Time: {{ bound_field.form_fields.1 }}
</p>

View File

@ -0,0 +1 @@
{% output_all bound_field.form_fields %}

View File

@ -0,0 +1,4 @@
{% if bound_field.original_value %}
Currently: <a href="{{ bound_field.original_url }}" > {{ bound_field.original_value }} </a><br />
Change: {% output_all bound_field.form_fields %}
{% else %} {% output_all bound_field.form_fields %} {% endif %}

View File

@ -0,0 +1,7 @@
{% output_all bound_field.form_fields %}
{% if bound_field.raw_id_admin %}
<a href="../../../{{ bound_field.field.rel.to.app_label }}/{{ bound_field.field.rel.to.module_name }}/" class="related-lookup" id="lookup_{{bound_field.element_id}}" onclick="return showRelatedObjectLookupPopup(this);"> <img src="{% admin_media_prefix %}img/admin/selector-search.gif" width="16" height="16" alt="Lookup"></a>
{% else %}
{% if bound_field.needs_add_label %}
<a href="../../../{{ bound_field.field.rel.to.app_label }}/{{ bound_field.field.rel.to.module_name }}/add/" class="add-another" id="add_{{ bound_field.element_id}}" onclick="return showAddAnotherPopup(this);"> <img src="{% admin_media_prefix %}img/admin/icon_addlink.gif" width="10" height="10" alt="Add Another"/></a>
{% endif %} {% endif %}

View File

@ -0,0 +1 @@
{% include "widget/foreign" %}

View File

@ -147,15 +147,16 @@ class BadKeywordArguments(Exception):
pass pass
class InlineRelatedObject(object): class RelatedObject(object):
def __init__(self,parent_opts, opts, field): def __init__(self,parent_opts, opts, field):
self.parent_opts = parent_opts self.parent_opts = parent_opts
self.opts = opts self.opts = opts
self.field = field self.field = field
self.edit_inline = field.rel.edit_inline
self.name = opts.module_name self.name = opts.module_name
self.var_name = opts.object_name.lower()
def flatten_data(self,obj = None): def flatten_data(self,obj = None):
var_name = self.opts.object_name.lower()
new_data = {} new_data = {}
rel_instances = self.get_list(obj) rel_instances = self.get_list(obj)
@ -165,7 +166,7 @@ class InlineRelatedObject(object):
field_data = f.flatten_data(rel_instance) field_data = f.flatten_data(rel_instance)
#if hasattr(f, 'editable') and f.editable and f != self.field: #if hasattr(f, 'editable') and f.editable and f != self.field:
for name, value in field_data.items(): for name, value in field_data.items():
instance_data['%s.%d.%s' % (var_name, i, name)] = value instance_data['%s.%d.%s' % (self.var_name, i, name)] = value
new_data.update(instance_data) new_data.update(instance_data)
return new_data return new_data
@ -204,12 +205,47 @@ class InlineRelatedObject(object):
This can be useful to add extra attributes for use in templates.""" This can be useful to add extra attributes for use in templates."""
return [wrapping_func(f) for f in self.opts.fields + self.opts.many_to_many if f.editable and f != self.field ] return [wrapping_func(f) for f in self.opts.fields + self.opts.many_to_many if f.editable and f != self.field ]
def get_follow(self, override=None):
if override:
over = override.copy()
elif self.edit_inline:
over = {}
else:
return None
over[self.field.name] = False
return self.opts.get_follow(over)
def __repr__(self): def __repr__(self):
return "<InlineRelatedObject: %s related to %s>" % ( self.name, self.field.name) return "<RelatedObject: %s related to %s>" % ( self.name, self.field.name)
def get_manipulator_fields(self, opts, manipulator, change, follow):
if change:
meth_name = 'get_%s_count' % self.parent_opts.get_rel_object_method_name(self.opts, self.field)
count = getattr(manipulator.original_object, meth_name)()
count += self.field.rel.num_extra_on_change
if self.field.rel.min_num_in_admin:
count = max(count, self.field.rel.min_num_in_admin)
if self.field.rel.max_num_in_admin:
count = min(count, self.field.rel.max_num_in_admin)
else:
count = self.field.rel.num_in_admin
fields = []
for i in range(count):
for f in self.opts.fields + self.opts.many_to_many:
if follow.get(f.name, False):
prefix = '%s.%d.' % (self.var_name, i)
fields.extend(
f.get_manipulator_fields(self.opts, manipulator, change, name_prefix=prefix, rel=True))
return fields
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='',
fields=None, ordering=None, unique_together=None, admin=None, has_related_links=False, fields=None, ordering=None, unique_together=None, admin=None, has_related_links=False,
@ -382,10 +418,34 @@ class Options:
return [(a, b) for a, b in self.get_all_related_objects() if b.rel.edit_inline] return [(a, b) for a, b in self.get_all_related_objects() if b.rel.edit_inline]
def get_inline_related_objects_wrapped(self): def get_inline_related_objects_wrapped(self):
return [InlineRelatedObject(self, opts, field) for opts, field in self.get_all_related_objects() if field.rel.edit_inline] return [RelatedObject(self, opts, field) for opts, field in self.get_all_related_objects() if field.rel.edit_inline]
def get_data_holders(self): def get_all_related_objects_wrapped(self):
return self.fields + self.many_to_many + self.get_inline_related_objects_wrapped() return [RelatedObject(self, opts, field) for opts, field in self.get_all_related_objects()]
def get_data_holders(self, follow=None):
return [f for f in self.fields + self.many_to_many + self.get_all_related_objects_wrapped() if follow.get(f.name, None) ]
def get_follow(self, override=None):
follow = {}
for f in self.fields + self.many_to_many:
if override and override.has_key(f.name):
fol = override[f.name]
else:
fol = f.editable
if fol:
follow[f.name] = fol
for f in self.get_all_related_objects_wrapped():
if override and override.has_key(f.name):
fol = f.get_follow(override[f.name])
else:
fol = f.get_follow(None)
if fol:
follow[f.name] = fol
return follow
def get_all_related_many_to_many_objects(self): def get_all_related_many_to_many_objects(self):
module_list = get_installed_model_modules() module_list = get_installed_model_modules()
@ -1488,7 +1548,9 @@ def get_manipulator(opts, klass, extra_methods, add=False, change=False):
setattr(man, k, v) setattr(man, k, v)
return man return man
def manipulator_init(opts, add, change, self, obj_key=None): def manipulator_init(opts, add, change, self, obj_key=None, follow=None):
self.follow = opts.get_follow(follow)
if change: if change:
assert obj_key is not None, "ChangeManipulator.__init__() must be passed obj_key parameter." assert obj_key is not None, "ChangeManipulator.__init__() must be passed obj_key parameter."
self.obj_key = obj_key self.obj_key = obj_key
@ -1512,25 +1574,32 @@ def manipulator_init(opts, add, change, self, obj_key=None):
else: else:
raise raise
self.fields = [] self.fields = []
for f in opts.fields + opts.many_to_many: for f in opts.fields + opts.many_to_many:
if f.editable and not (f.primary_key and change) and (not f.rel or not f.rel.edit_inline): if self.follow.get(f.name, False):
# if f.editable and not (f.primary_key and change) and (not f.rel or not f.rel.edit_inline):
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 obj in opts.get_inline_related_objects_wrapped(): for f in opts.get_all_related_objects_wrapped():
if change: if self.follow.get(f.name, False):
count = getattr(self.original_object, 'get_%s_count' % opts.get_rel_object_method_name(obj.opts, obj.field))() fol = self.follow[f.name]
count += obj.field.rel.num_extra_on_change self.fields.extend(f.get_manipulator_fields(opts, self, change, fol))
if obj.field.rel.min_num_in_admin:
count = max(count, obj.field.rel.min_num_in_admin) # for obj in opts.get_inline_related_objects_wrapped():
if obj.field.rel.max_num_in_admin: # if change:
count = min(count, obj.field.rel.max_num_in_admin) # count = getattr(self.original_object, 'get_%s_count' % opts.get_rel_object_method_name(obj.opts, obj.field))()
else: # count += obj.field.rel.num_extra_on_change
count = obj.field.rel.num_in_admin # if obj.field.rel.min_num_in_admin:
for f in obj.opts.fields + obj.opts.many_to_many: # count = max(count, obj.field.rel.min_num_in_admin)
if f.editable and f != obj.field : # if obj.field.rel.max_num_in_admin:
for i in range(count): # count = min(count, obj.field.rel.max_num_in_admin)
self.fields.extend(f.get_manipulator_fields(obj.opts, self, change, name_prefix='%s.%d.' % (obj.opts.object_name.lower(), i), rel=True)) # else:
# count = obj.field.rel.num_in_admin
# for f in obj.opts.fields + obj.opts.many_to_many:
# if f.editable and f != obj.field :
# for i in range(count):
# self.fields.extend(f.get_manipulator_fields(obj.opts, self, change, name_prefix='%s.%d.' % (obj.opts.object_name.lower(), i), rel=True))
# Add field for ordering. # Add field for ordering.
if change and opts.get_ordered_objects(): if change and opts.get_ordered_objects():
@ -1540,12 +1609,19 @@ def manipulator_save(opts, klass, add, change, self, new_data):
from django.utils.datastructures import DotExpandedDict from django.utils.datastructures import DotExpandedDict
params = {} params = {}
for f in opts.fields: for f in opts.fields:
auto_now_add = change and getattr(f, 'auto_now_add', False)
if self.follow.get(f.name, None) and not auto_now_add:
param = f.get_manipulator_new_data(new_data)
else:
param = getattr(self.original_object, f.column)
params[f.column] = param
# Fields with auto_now_add are another special case; they should keep # Fields with auto_now_add are another special case; they should keep
# their original value in the change stage. # their original value in the change stage.
if change and getattr(f, 'auto_now_add', False): #if change and getattr(f, 'auto_now_add', False):
params[f.column] = getattr(self.original_object, f.name) # params[f.column] = getattr(self.original_object, f.name)
else: #else:
params[f.column] = f.get_manipulator_new_data(new_data) # params[f.column] = f.get_manipulator_new_data(new_data)
if change: if change:
params[opts.pk.column] = self.obj_key params[opts.pk.column] = self.obj_key
@ -1568,101 +1644,117 @@ def manipulator_save(opts, klass, add, change, self, new_data):
# Save many-to-many objects. Example: Poll.set_sites() # Save many-to-many objects. Example: Poll.set_sites()
for f in opts.many_to_many: for f in opts.many_to_many:
if not f.rel.edit_inline: if self.follow.get(f.name, None):
was_changed = getattr(new_object, 'set_%s' % f.name)(new_data.getlist(f.name)) if not f.rel.edit_inline:
if change and was_changed: was_changed = getattr(new_object, 'set_%s' % f.name)(new_data.getlist(f.name))
self.fields_changed.append(f.verbose_name) if change and was_changed:
self.fields_changed.append(f.verbose_name)
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 rel_opts, rel_field in opts.get_inline_related_objects(): for related in opts.get_all_related_objects_wrapped():
# 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']}),
# ('2', {'id': [''], 'choice': ['']})] # ('2', {'id': [''], 'choice': ['']})]
obj_list = DotExpandedDict(new_data.data)[rel_opts.object_name.lower()].items() child_follow = self.follow.get(related.name, None)
obj_list.sort(lambda x, y: cmp(int(x[0]), int(y[0])))
params = {} if child_follow:
obj_list = expanded_data[related.var_name].items()
obj_list.sort(lambda x, y: cmp(int(x[0]), int(y[0])))
params = {}
# For each related item...
for _, rel_new_data in obj_list:
# Keep track of which core=True fields were provided. # For each related item...
# If all core fields were given, the related object will be saved. for _, rel_new_data in obj_list:
# If none of the core fields were given, the object will be deleted.
# If some, but not all, of the fields were given, the validator would
# have caught that.
all_cores_given, all_cores_blank = True, True
# Get a reference to the old object. We'll use it to compare the # Keep track of which core=True fields were provided.
# old to the new, to see which fields have changed. # If all core fields were given, the related object will be saved.
if change: # If none of the core fields were given, the object will be deleted.
# If some, but not all, of the fields were given, the validator would
# have caught that.
all_cores_given, all_cores_blank = True, True
# Get a reference to the old object. We'll use it to compare the
# old to the new, to see which fields have changed.
old_rel_obj = None old_rel_obj = None
if rel_new_data[rel_opts.pk.name][0]:
try:
old_rel_obj = getattr(self.original_object, 'get_%s' % opts.get_rel_object_method_name(rel_opts, rel_field))(**{'%s__exact' % rel_opts.pk.name: rel_new_data[rel_opts.pk.name][0]})
except ObjectDoesNotExist:
pass
for f in rel_opts.fields:
if f.core and not isinstance(f, FileField) and f.get_manipulator_new_data(rel_new_data, rel=True) in (None, ''):
all_cores_given = False
elif f.core and not isinstance(f, FileField) and f.get_manipulator_new_data(rel_new_data, rel=True) not in (None, ''):
all_cores_blank = False
# If this field isn't editable, give it the same value it had
# previously, according to the given ID. If the ID wasn't
# given, use a default value. FileFields are also a special
# case, because they'll be dealt with later.
if change and (isinstance(f, FileField) or not f.editable):
if rel_new_data.get(rel_opts.pk.name, False) and rel_new_data[rel_opts.pk.name][0]:
params[f.column] = getattr(old_rel_obj, f.column)
else:
params[f.column] = f.get_default()
elif f == rel_field:
params[f.column] = getattr(new_object, rel_field.rel.field_name)
elif add and isinstance(f, AutoField):
params[f.column] = None
else:
params[f.column] = f.get_manipulator_new_data(rel_new_data, rel=True)
# Related links are a special case, because we have to
# manually set the "content_type_id" and "object_id" fields.
if opts.has_related_links and rel_opts.module_name == 'relatedlinks':
contenttypes_mod = get_module('core', 'contenttypes')
params['content_type_id'] = contenttypes_mod.get_object(package__label__exact=opts.app_label, python_module_name__exact=opts.module_name).id
params['object_id'] = new_object.id
# Create the related item.
new_rel_obj = rel_opts.get_model_module().Klass(**params)
# If all the core fields were provided (non-empty), save the item.
if all_cores_given:
new_rel_obj.save()
# Save any uploaded files.
for f in rel_opts.fields:
if isinstance(f, FileField) and rel_new_data.get(f.name, False):
f.save_file(rel_new_data, new_rel_obj, change and old_rel_obj or None, change, rel=True)
# Calculate whether any fields have changed.
if change: if change:
if not old_rel_obj: # This object didn't exist before. if rel_new_data[related.opts.pk.name][0]:
self.fields_added.append('%s "%r"' % (rel_opts.verbose_name, new_rel_obj)) try:
old_rel_obj = getattr(self.original_object, 'get_%s' % opts.get_rel_object_method_name(related.opts, related.field))(**{'%s__exact' % related.opts.pk.name: rel_new_data[related.opts.pk.name][0]})
except ObjectDoesNotExist:
pass
for f in related.opts.fields:
if f.core and not isinstance(f, FileField) and f.get_manipulator_new_data(rel_new_data, rel=True) in (None, ''):
all_cores_given = False
elif f.core and not isinstance(f, FileField) and f.get_manipulator_new_data(rel_new_data, rel=True) not in (None, ''):
all_cores_blank = False
# If this field isn't editable, give it the same value it had
# previously, according to the given ID. If the ID wasn't
# given, use a default value. FileFields are also a special
# case, because they'll be dealt with later.
if f == related.field:
param = getattr(new_object, related.field.rel.field_name)
elif add and isinstance(f, AutoField):
param = None
elif change and (isinstance(f, FileField) or not child_follow.get(f.name, None)):
if old_rel_obj:
param = getattr(old_rel_obj, f.column)
else:
param = f.get_default()
else: else:
for f in rel_opts.fields: param = f.get_manipulator_new_data(rel_new_data, rel=True)
if not f.primary_key and f != rel_field and str(getattr(old_rel_obj, f.column)) != str(getattr(new_rel_obj, f.column)):
self.fields_changed.append('%s for %s "%r"' % (f.verbose_name, rel_opts.verbose_name, new_rel_obj)) if param:
params[f.column] = param
# Save many-to-many objects. # Related links are a special case, because we have to
for f in rel_opts.many_to_many: # manually set the "content_type_id" and "object_id" fields.
if not f.rel.edit_inline: if opts.has_related_links and related.opts.module_name == 'relatedlinks':
was_changed = getattr(new_rel_obj, 'set_%s' % f.name)(rel_new_data[f.name]) contenttypes_mod = get_module('core', 'contenttypes')
if change and was_changed: params['content_type_id'] = contenttypes_mod.get_object(package__label__exact=opts.app_label, python_module_name__exact=opts.module_name).id
self.fields_changed.append('%s for %s "%s"' % (f.verbose_name, rel_opts.verbose_name, new_rel_obj)) params['object_id'] = new_object.id
# Create the related item.
new_rel_obj = related.opts.get_model_module().Klass(**params)
# If all the core fields were provided (non-empty), save the item.
if all_cores_given:
new_rel_obj.save()
# Save any uploaded files.
for f in related.opts.fields:
if child_follow.get(f.name, None):
if isinstance(f, FileField) and rel_new_data.get(f.name, False):
f.save_file(rel_new_data, new_rel_obj, change and old_rel_obj or None, change, rel=True)
# Calculate whether any fields have changed.
if change:
if not old_rel_obj: # This object didn't exist before.
self.fields_added.append('%s "%r"' % (related.opts.verbose_name, new_rel_obj))
else:
for f in related.opts.fields:
if not f.primary_key and f != related.field and str(getattr(old_rel_obj, f.column)) != str(getattr(new_rel_obj, f.column)):
self.fields_changed.append('%s for %s "%r"' % (f.verbose_name, rel_opts.verbose_name, new_rel_obj))
# Save many-to-many objects.
for f in related.opts.many_to_many:
if child_follow.get(f.name, None) and not f.rel.edit_inline:
was_changed = getattr(new_rel_obj, 'set_%s' % f.name)(rel_new_data[f.name])
if change and was_changed:
self.fields_changed.append('%s for %s "%s"' % (f.verbose_name, rel_opts.verbose_name, new_rel_obj))
# If, in the change stage, all of the core fields were blank and
# the primary key (ID) was provided, delete the item.
if change and all_cores_blank and old_rel_obj:
new_rel_obj.delete()
self.fields_deleted.append('%s "%r"' % (related.opts.verbose_name, old_rel_obj))
# If, in the change stage, all of the core fields were blank and
# the primary key (ID) was provided, delete the item.
if change and all_cores_blank and rel_new_data.has_key(rel_opts.pk.name) and rel_new_data[rel_opts.pk.name][0]:
new_rel_obj.delete()
self.fields_deleted.append('%s "%r"' % (rel_opts.verbose_name, old_rel_obj))
# Save the order, if applicable. # Save the order, if applicable.
if change and opts.get_ordered_objects(): if change and opts.get_ordered_objects():
@ -1677,7 +1769,7 @@ def manipulator_get_inline_related_objects_wrapped(opts, klass, add, change, sel
def manipulator_flatten_data(opts, klass, add, change, self): def manipulator_flatten_data(opts, klass, add, change, self):
new_data = {} new_data = {}
obj = change and self.original_object or None obj = change and self.original_object or None
for f in opts.get_data_holders(): for f in opts.get_data_holders(self.follow):
new_data.update(f.flatten_data(obj)) new_data.update(f.flatten_data(obj))
return new_data return new_data

View File

@ -639,11 +639,14 @@ class ForeignKey(Field):
return [formfields.IntegerField] return [formfields.IntegerField]
def get_db_prep_save(self,value): def get_db_prep_save(self,value):
if value == '': try:
return None if value == '' or None:
else: return None
return int(value) else:
return int(value)
except Exception, e:
print "name: %s val: %s" % (self.name, value)
def flatten_data(self, obj = None): def flatten_data(self, obj = None):
if not obj: if not obj:
# In required many-to-one fields with only one available choice, # In required many-to-one fields with only one available choice,

View File

@ -68,7 +68,7 @@ def create_object(request, app_label, module_name, template_name=None,
def update_object(request, app_label, module_name, object_id=None, slug=None, def update_object(request, app_label, module_name, object_id=None, slug=None,
slug_field=None, template_name=None, template_loader=template_loader, slug_field=None, template_name=None, template_loader=template_loader,
extra_lookup_kwargs={}, extra_context={}, post_save_redirect=None, extra_lookup_kwargs={}, extra_context={}, post_save_redirect=None,
login_required=False): login_required=False, follow=None):
""" """
Generic object-update function. Generic object-update function.
@ -98,13 +98,13 @@ def update_object(request, app_label, module_name, object_id=None, slug=None,
except ObjectDoesNotExist: except ObjectDoesNotExist:
raise Http404("%s.%s does not exist for %s" % (app_label, module_name, lookup_kwargs)) raise Http404("%s.%s does not exist for %s" % (app_label, module_name, lookup_kwargs))
manipulator = mod.ChangeManipulator(object.id) manipulator = mod.ChangeManipulator(object.id, follow=follow)
if request.POST: if request.POST:
new_data = request.POST.copy() new_data = request.POST.copy()
errors = manipulator.get_validation_errors(new_data) errors = manipulator.get_validation_errors(new_data)
manipulator.do_html2python(new_data)
if not errors: if not errors:
manipulator.do_html2python(new_data)
manipulator.save(new_data) manipulator.save(new_data)
if not request.user.is_anonymous(): if not request.user.is_anonymous():
@ -120,7 +120,7 @@ def update_object(request, app_label, module_name, object_id=None, slug=None,
else: else:
errors = {} errors = {}
# This makes sure the form acurate represents the fields of the place. # This makes sure the form acurate represents the fields of the place.
new_data = object.__dict__ new_data = manipulator.flatten_data()
form = formfields.FormWrapper(manipulator, new_data, errors) form = formfields.FormWrapper(manipulator, new_data, errors)
if not template_name: if not template_name:

View File

@ -7,7 +7,7 @@ This is made possible by the excellent, open-source ReportLab_ Python PDF
library. library.
The advantage of generating PDF files dynamically is that you can create The advantage of generating PDF files dynamically is that you can create
customzed PDFs for different purposes -- say, for different users or different customized PDFs for different purposes -- say, for different users or different
pieces of content. pieces of content.
For example, Django was used at kusports.com to generate customized, For example, Django was used at kusports.com to generate customized,

View File

@ -267,7 +267,7 @@ every template automatic access to the current time, use something like this::
from django.core.template import Context from django.core.template import Context
import datetime import datetime
class TimeContext(template.Context): class TimeContext(Context):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
Context.__init__(self, *args, **kwargs) Context.__init__(self, *args, **kwargs)
self['current_time'] = datetime.datetime.now() self['current_time'] = datetime.datetime.now()