1
0
mirror of https://github.com/django/django.git synced 2025-04-15 21:04:36 +00:00

magic-removal: Manipulator and deletion fixes.

git-svn-id: http://code.djangoproject.com/svn/django/branches/magic-removal@1765 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Robert Wittams 2005-12-22 15:07:36 +00:00
parent 7b7d1e8939
commit 79ad41be03
8 changed files with 173 additions and 113 deletions

View File

@ -1,6 +1,6 @@
{% load i18n %}
<div class="submit-row">
{% if show_delete_link %}<p class="float-left"><a href="delete/" class="deletelink">{% trans "Delete" %}</a></p>{% endif %}
{% if show_delete_link %}<p class="float-left"><a href="../delete/" class="deletelink">{% trans "Delete" %}</a></p>{% endif %}
{% if show_save_as_new %}<input type="submit" value="{% trans 'Save as new' %}" name="_saveasnew" {{ onclick_attrib }}/>{%endif%}
{% if show_save_and_add_another %}<input type="submit" value="{% trans 'Save and add another' %}" name="_addanother" {{ onclick_attrib }} />{% endif %}
{% if show_save_and_continue %}<input type="submit" value="{% trans 'Save and continue editing' %}" name="_continue" {{ onclick_attrib }}/>{% endif %}

View File

@ -1,3 +1,15 @@
from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.admin.views.main import get_model_and_app
from django.core.extensions import get_object_or_404,render_to_response
from django.core.extensions import DjangoContext as Context
from django.utils.text import capfirst
from django.utils.html import escape, strip_tags
from django.db import models
try:
from django.contrib.admin.models import LogEntry, ADDITION, CHANGE, DELETION
except ImportError:
raise ImproperlyConfigured, "You don't have 'django.contrib.admin' in INSTALLED_APPS."
from django.utils.httpwrappers import HttpResponse, HttpResponseRedirect
def _nest_help(obj, depth, val):
current = obj
@ -83,12 +95,14 @@ def _get_deleted_objects(deleted_objects, perms_needed, user, obj, opts, current
if not user.has_perm(p):
perms_needed.add(related.opts.verbose_name)
def delete_stage(request, app_label, module_name, object_id):
def delete_stage(request, path, object_id):
import sets
mod, opts = _get_mod_opts(app_label, module_name)
#mod, opts = _get_mod_opts(app_label, module_name)
model, app_label = get_model_and_app(path)
opts = model._meta
if not request.user.has_perm(app_label + '.' + opts.get_delete_permission()):
raise PermissionDenied
obj = get_object_or_404(mod, pk=object_id)
obj = get_object_or_404(model, pk=object_id)
# Populate deleted_objects, a data structure of all related objects that
# will also be deleted.

View File

@ -54,10 +54,10 @@ class ModelBase(type):
def cmp_cls(x, y):
for field in x._meta.fields:
if field.rel and field.null and field.rel.to == y:
if field.rel and not field.null and field.rel.to == y:
return -1
for field in y._meta.fields:
if field.rel and field.null and field.rel.to == x:
if field.rel and not field.null and field.rel.to == x:
return 1
return 0
@ -196,14 +196,12 @@ class Model(object):
def __get_pk_val(self):
return str(getattr(self, self._meta.pk.attname))
def __collect_sub_objects(self, seen_objs, ignore_objs):
def __collect_sub_objects(self, seen_objs):
pk_val = self.__get_pk_val()
key = (self.__class__, pk_val)
if key in seen_objs or key in ignore_objs:
if pk_val in seen_objs.setdefault(self.__class__, {}):
return
seen_objs[key] = self
seen_objs.setdefault(self.__class__, {})[pk_val] = (self, True)
for related in self._meta.get_all_related_objects():
rel_opts_name = related.get_method_name_part()
@ -213,64 +211,74 @@ class Model(object):
except ObjectDoesNotExist:
pass
else:
sub_obj.__collect_sub_objects(seen_objs, ignore_objs)
sub_obj.__collect_sub_objects(seen_objs)
else:
for sub_obj in getattr(self, 'get_%s_list' % rel_opts_name)():
sub_obj.__collect_sub_objects(seen_objs, ignore_objs)
sub_obj.__collect_sub_objects(seen_objs)
def delete(self, ignore_objects=None):
assert getattr(self, self._meta.pk.attname) is not None, "%r can't be deleted because it doesn't have an ID."
ignore_objects = ignore_objects and dict([(o.__class__,o.__get_pk_val()) for o in ignore_objects]) or {}
seen_objs = {}
if ignore_objects:
for obj in ignore_objects:
ignore_objs.setdefault(self.__class__,{})[obj.__get_pk_val()] = (obj, False)
seen_objs = {}
self.__collect_sub_objects(seen_objs, ignore_objects)
self.__collect_sub_objects(seen_objs)
seen_cls = set([cls for cls,pk in seen_objs.keys()])
#TODO: create a total class ordering rather than this sorting, which is
# only a partial ordering, and also is done each delete..
seen_cls = set(seen_objs.keys())
cls_order = list(seen_cls)
cls_order.sort(cmp_cls)
seen_tups = [ (cls, pk_val, instance) for (cls, pk_val),instance in seen_objs.items() ]
seen_tups.sort(lambda x,y: cmp(cls_order.index(x[0]), cls_order.index(y[0])))
cursor = connection.cursor()
for cls, pk_val, instance in seen_tups:
# Run any pre-delete hooks.
if hasattr(instance, '_pre_delete'):
instance._pre_delete()
dispatcher.send(signal=signals.pre_delete, sender=cls, instance=instance)
for related in cls._meta.get_all_related_many_to_many_objects():
cursor.execute("DELETE FROM %s WHERE %s=%%s" % \
(backend.quote_name(related.field.get_m2m_db_table(related.opts)),
backend.quote_name(cls._meta.object_name.lower() + '_id')),
[pk_val])
for f in cls._meta.many_to_many:
cursor.execute("DELETE FROM %s WHERE %s=%%s" % \
(backend.quote_name(f.get_m2m_db_table(cls._meta)),
backend.quote_name(cls._meta.object_name.lower() + '_id')),
[pk_val])
for field in cls._meta.fields:
if field.rel and field.null and field.rel.to in seen_cls:
cursor.execute("UPDATE %s SET %s = NULL WHERE %s=%%s" % \
(backend.quote_name(cls._meta.db_table), backend.quote_name(field.column),
backend.quote_name(cls._meta.pk.column)), [pk_val])
seen_tups.reverse()
for cls, pk_val, instance in seen_tups:
cursor.execute("DELETE FROM %s WHERE %s=%%s" % \
(backend.quote_name(cls._meta.db_table), backend.quote_name(cls._meta.pk.column)),
[pk_val])
setattr(self, cls._meta.pk.attname, None)
dispatcher.send(signal=signals.post_delete, sender=cls, instance=instance)
if hasattr(instance, '_post_delete'):
instance._post_delete()
for cls in cls_order:
seen_objs[cls] = seen_objs[cls].items()
seen_objs[cls].sort()
for pk_val,(instance, do_delete) in seen_objs[cls]:
# Run any pre-delete hooks.
if do_delete:
if hasattr(instance, '_pre_delete'):
instance._pre_delete()
dispatcher.send(signal=signals.pre_delete, sender=cls, instance=instance)
for related in cls._meta.get_all_related_many_to_many_objects():
cursor.execute("DELETE FROM %s WHERE %s=%%s" % \
(backend.quote_name(related.field.get_m2m_db_table(related.opts)),
backend.quote_name(cls._meta.object_name.lower() + '_id')),
[pk_val])
for f in cls._meta.many_to_many:
cursor.execute("DELETE FROM %s WHERE %s=%%s" % \
(backend.quote_name(f.get_m2m_db_table(cls._meta)),
backend.quote_name(cls._meta.object_name.lower() + '_id')),
[pk_val])
for field in cls._meta.fields:
if field.rel and field.null and field.rel.to in seen_cls:
cursor.execute("UPDATE %s SET %s = NULL WHERE %s=%%s" % \
(backend.quote_name(cls._meta.db_table), backend.quote_name(field.column),
backend.quote_name(cls._meta.pk.column)), [pk_val])
setattr(instance, field.attname, None)
for cls in cls_order:
seen_objs[cls].reverse()
for pk_val, (instance, do_delete) in seen_objs[cls]:
if do_delete:
cursor.execute("DELETE FROM %s WHERE %s=%%s" % \
(backend.quote_name(cls._meta.db_table), backend.quote_name(cls._meta.pk.column)),
[pk_val])
setattr(instance, cls._meta.pk.attname, None)
dispatcher.send(signal=signals.post_delete, sender=cls, instance=instance)
if hasattr(instance, '_post_delete'):
instance._post_delete()
connection.commit()

View File

@ -213,6 +213,12 @@ class Field(object):
field_objs = self.get_manipulator_field_objs()
return (field_objs,params)
def get_fields_and_manipulators(self, opts, manipulator, follow ):
change = manipulator.change
rel = manipulator.name_prefix != ''
name_prefix = manipulator.name_prefix
return (self.get_manipulator_fields(opts, manipulator, change,name_prefix, rel, follow), [] )
def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True):
"""
Returns a list of formfields.FormField instances for this field. It

View File

@ -20,9 +20,6 @@ dispatcher.connect(
)
class ManipulatorDescriptor(object):
class empty:
pass
def __init__(self, name, base):
self.man = None
self.name = name
@ -62,15 +59,32 @@ class AutomaticManipulator(Manipulator):
setattr(other_cls, name, ManipulatorDescriptor(name, cls))
contribute_to_class = classmethod(contribute_to_class)
def __init__(self, original_object=None, follow=None):
self.follow = self.model._meta.get_follow(follow)
self.fields = []
def __init__(self, original_object=None, follow=None, name_prefix=''):
if name_prefix == '':
self.follow = self.model._meta.get_follow(follow)
else:
self.follow = follow
self.fields_, self.children = [], []
self.original_object = original_object
self.name_prefix = name_prefix
for f in self.opts.get_data_holders(self.follow):
fol = self.follow[f.name]
fields = f.get_manipulator_fields(self.opts, self, self.change, follow=fol)
self.fields.extend(fields)
fields,manipulators = f.get_fields_and_manipulators(self.opts, self, follow=fol)
#fields = f.get_manipulator_fields(self.opts, self, self.change, follow=fol)
self.fields_.extend(fields)
self.children.append(manipulators)
#print self.fields_
def get_fields(self):
l = list(self.fields_)
for child in self.children:
for manip in child:
l.extend(manip.fields)
#print l
return l
fields = property(get_fields)
def save(self, new_data):
add, change, opts, klass = self.add, self.change, self.opts, self.model
# TODO: big cleanup when core fields go -> use recursive manipulators.
@ -233,37 +247,42 @@ class AutomaticManipulator(Manipulator):
class ModelAddManipulator(AutomaticManipulator):
change = False
add = True
def __init__(self, follow=None):
super(ModelAddManipulator, self).__init__(follow=follow)
def __init__(self, follow=None, name_prefix=''):
super(ModelAddManipulator, self).__init__(follow=follow, name_prefix=name_prefix)
class ModelChangeManipulator(AutomaticManipulator):
change = True
add = False
def __init__(self, obj_key=None, follow=None):
def __init__(self, obj_key=None, follow=None, name_prefix=''):
assert obj_key is not None, "ChangeManipulator.__init__() must be passed obj_key parameter."
self.obj_key = obj_key
try:
original_object = self.__class__.manager.get_object(pk=obj_key)
except ObjectDoesNotExist:
# If the object doesn't exist, this might be a manipulator for a
# one-to-one related object that hasn't created its subobject yet.
# For example, this might be a Restaurant for a Place that doesn't
# yet have restaurant information.
if opts.one_to_one_field:
# Sanity check -- Make sure the "parent" object exists.
# For example, make sure the Place exists for the Restaurant.
# Let the ObjectDoesNotExist exception propogate up.
lookup_kwargs = opts.one_to_one_field.rel.limit_choices_to
lookup_kwargs['%s__exact' % opts.one_to_one_field.rel.field_name] = obj_key
null = opts.one_to_one_field.rel.to._meta.get_model_module().get_object(**lookup_kwargs)
params = dict([(f.attname, f.get_default()) for f in opts.fields])
params[opts.pk.attname] = obj_key
original_object = opts.get_model_module().Klass(**params)
else:
raise
super(ModelChangeManipulator, self).__init__(original_object=original_object, follow=follow)
self.original_object = original_object
if isinstance(obj_key, self.model):
original_object = obj_key
self.obj_key = getattr(original_object, self.model._meta.pk.attname)
else:
self.obj_key = obj_key
try:
original_object = self.manager.get_object(pk=obj_key)
except ObjectDoesNotExist:
# If the object doesn't exist, this might be a manipulator for a
# one-to-one related object that hasn't created its subobject yet.
# For example, this might be a Restaurant for a Place that doesn't
# yet have restaurant information.
if opts.one_to_one_field:
# Sanity check -- Make sure the "parent" object exists.
# For example, make sure the Place exists for the Restaurant.
# Let the ObjectDoesNotExist exception propogate up.
lookup_kwargs = opts.one_to_one_field.rel.limit_choices_to
lookup_kwargs['%s__exact' % opts.one_to_one_field.rel.field_name] = obj_key
null = opts.one_to_one_field.rel.to._meta.get_model_module().get_object(**lookup_kwargs)
params = dict([(f.attname, f.get_default()) for f in opts.fields])
params[opts.pk.attname] = obj_key
original_object = opts.get_model_module().Klass(**params)
else:
raise
super(ModelChangeManipulator, self).__init__(original_object=original_object, follow=follow, name_prefix=name_prefix)
#self.original_object = original_object
if self.opts.get_ordered_objects():
self.fields.append(formfields.CommaSeparatedIntegerField(field_name="order_"))

View File

@ -161,7 +161,7 @@ class Options:
else:
child_override = None
fol = f.get_follow(child_override)
if fol:
if fol != None:
follow[f.name] = fol
return follow

View File

@ -46,24 +46,9 @@ class RelatedObject(object):
if parent_instance != None:
func_name = 'get_%s_list' % self.get_method_name_part()
func = getattr(parent_instance, func_name)
list = func()
count = len(list) + 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)
change = count - len(list)
if change > 0:
return list + [None for i in range(change)]
if change < 0:
return list[:change]
else: # Just right
return list
return func()
else:
return [None for i in range(self.field.rel.num_in_admin)]
return []
def editable_fields(self):
"Get the fields in this class that should be edited inline."
@ -85,13 +70,30 @@ class RelatedObject(object):
over[self.field.name] = False
return self.opts.get_follow(over)
def __repr__(self):
return "<RelatedObject: %s related to %s>" % (self.name, self.field.name)
def get_manipulator_fields(self, opts, manipulator, change, follow):
# TODO: Remove core fields stuff.
def get_fields_and_manipulators(self, opts, manipulator, follow ):
return ([], self.get_manipulators(manipulator, follow) )
def get_manipulators(self,parent_manipulator, follow):
man_class = self.model.ChangeManipulator
if parent_manipulator.original_object:
meth_name = 'get_%s_list' % self.get_method_name_part()
list = getattr(parent_manipulator.original_object, meth_name)()
manipulators = []
for i,obj in enumerate(list):
prefix = '%s.%d.' % (self.var_name, i)
manipulators.append(man_class(obj,follow, prefix) )
return manipulators
def get_manipulator_fields(self, opts, manipulator, follow):
# TODO: Remove core fields stuff.
change = manipulator.change
if manipulator.original_object:
meth_name = 'get_%s_count' % self.get_method_name_part()
count = getattr(manipulator.original_object, meth_name)()
@ -104,6 +106,7 @@ class RelatedObject(object):
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):

View File

@ -86,4 +86,14 @@ Ella Fitzgerald
Ultimate Ella
>>> a2.release_date
datetime.date(2005, 2, 13)
# Test follow (inline editing) functionality.
>>> follow = {"albums":True}
>>> man = Musician.ChangeManipulator(m1 ,follow=follow)
>>> data = man.flatten_data()
>>> sorted(data.items(), cmp=lambda x,y: cmp(x[0],y[0]))
[('album.0.id', 1), ('album.0.name', 'Ella and Basie'), ('album.0.release_date', ''), ('album.1.id', 2), ('album.1.name', 'Ultimate Ella'), ('album.1.release_date', '2005-02-13'), ('first_name', 'Ella'), ('id', 1L), ('last_name', 'Fitzgerald')]
"""