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:
parent
7b7d1e8939
commit
79ad41be03
@ -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 %}
|
||||
|
@ -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.
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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
|
||||
|
@ -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_"))
|
||||
|
@ -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
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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')]
|
||||
|
||||
|
||||
|
||||
"""
|
||||
|
Loading…
x
Reference in New Issue
Block a user