diff --git a/django/contrib/admin/templatetags/admin_modify.py b/django/contrib/admin/templatetags/admin_modify.py
index d607594e06..9be7333f83 100644
--- a/django/contrib/admin/templatetags/admin_modify.py
+++ b/django/contrib/admin/templatetags/admin_modify.py
@@ -201,7 +201,7 @@ auto_populated_field_script = register.simple_tag(auto_populated_field_script)
#@register.simple_tag
def filter_interface_script_maybe(bound_field):
f = bound_field.field
- if f.rel and isinstance(f.rel, meta.ManyToMany) and f.rel.filter_interface:
+ if f.rel and isinstance(f.rel, models.ManyToMany) and f.rel.filter_interface:
return '\n' % (
f.name, f.verbose_name, f.rel.filter_interface-1, ADMIN_MEDIA_PREFIX)
diff --git a/django/db/models/__init__.py b/django/db/models/__init__.py
index 0f2d956804..ffb25f31bd 100644
--- a/django/db/models/__init__.py
+++ b/django/db/models/__init__.py
@@ -16,6 +16,7 @@ from django.db.models.fields.related import *
from django.core.exceptions import ObjectDoesNotExist
from django.db.models.exceptions import FieldDoesNotExist, BadKeywordArguments
+from django.db.models.signals import Signals
# Admin stages.
ADD, CHANGE, BOTH = 1, 2, 3
diff --git a/django/db/models/base.py b/django/db/models/base.py
index ae1d4f0093..dbef24ea9b 100644
--- a/django/db/models/base.py
+++ b/django/db/models/base.py
@@ -1,12 +1,14 @@
-from django.db.models.manipulators import ManipulatorDescriptor, ModelAddManipulator, ModelChangeManipulator
-from django.db.models.fields import Field, DateField, FileField, ImageField, AutoField
-from django.db.models.fields.related import RelatedField, OneToOne, ManyToOne, ManyToMany, RECURSIVE_RELATIONSHIP_CONSTANT
+from django.db.models.manipulators import ModelAddManipulator, ModelChangeManipulator
+from django.db.models.fields import AutoField
+from django.db.models.fields.related import OneToOne, ManyToOne
from django.db.models.related import RelatedObject
-from django.db.models.manager import Manager, ManagerDescriptor
+from django.db.models.manager import Manager
from django.db.models.query import orderlist2sql
from django.db.models.options import Options
from django.db import connection, backend
+from django.db.models.signals import Signals
+from django.dispatch import dispatcher
from django.core.exceptions import ObjectDoesNotExist
from django.utils.functional import curry
@@ -25,6 +27,7 @@ get_module_name = lambda class_name: class_name.lower() + 's'
get_verbose_name = lambda class_name: re.sub('([A-Z])', ' \\1', class_name).lower().strip()
+
class ModelBase(type):
"Metaclass for all models"
def __new__(cls, name, bases, attrs):
@@ -118,6 +121,9 @@ def cmp_cls(x, y):
return 1
return 0
+
+
+
class Model(object):
__metaclass__ = ModelBase
@@ -128,9 +134,7 @@ class Model(object):
setattr(cls, name, attribute)
add_to_class = classmethod(add_to_class)
- AddManipulator = ManipulatorDescriptor('AddManipulator', ModelAddManipulator)
- ChangeManipulator = ManipulatorDescriptor('ChangeManipulator', ModelChangeManipulator)
-
+
def __repr__(self):
return '<%s object>' % self.__class__.__name__
@@ -141,6 +145,7 @@ class Model(object):
return not self.__eq__(other)
def __init__(self, *args, **kwargs):
+ dispatcher.send( signal = Signals.pre_init, sender = self.__class__, args=args, kwargs=kwargs)
if kwargs:
for f in self._meta.fields:
if isinstance(f.rel, ManyToOne):
@@ -171,15 +176,21 @@ class Model(object):
raise TypeError, "'%s' is an invalid keyword argument for this function" % kwargs.keys()[0]
for i, arg in enumerate(args):
setattr(self, self._meta.fields[i].attname, arg)
+ dispatcher.send( signal = Signals.post_init, sender = self.__class__, instance=self)
def _prepare(cls):
+ cls.add_to_class( 'AddManipulator', ModelAddManipulator)
+ cls.add_to_class( 'ChangeManipulator', ModelChangeManipulator)
+
# Creates some methods once self._meta has been populated.
if cls._meta.order_with_respect_to:
cls.get_next_in_order = curry(cls._get_next_or_previous_in_order, is_next=True)
cls.get_previous_in_order = curry(cls._get_next_or_previous_in_order, is_next=False)
- RelatedField.do_pending_lookups(cls)
+ dispatcher.send( signal = Signals.class_prepared, sender = cls)
+
+ #RelatedField.do_pending_lookups(cls)
_prepare = classmethod(_prepare)
@@ -187,6 +198,7 @@ class Model(object):
# Run any pre-save hooks.
if hasattr(self, '_pre_save'):
self._pre_save()
+ dispatcher.send( signal=Signals.pre_save, sender = self.__class__, instance = self )
non_pks = [f for f in self._meta.fields if not f.primary_key]
cursor = connection.cursor()
@@ -231,6 +243,8 @@ class Model(object):
connection.commit()
# Run any post-save hooks.
+ dispatcher.send(signal=Signals.pre_save, sender = self.__class__, instance = self )
+
if hasattr(self, '_post_save'):
self._post_save()
@@ -283,6 +297,8 @@ class Model(object):
# 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" % \
@@ -311,14 +327,9 @@ class Model(object):
[pk_val])
setattr(self, cls._meta.pk.attname, None)
- for f in cls._meta.fields:
- if isinstance(f, FileField) and getattr(self, f.attname):
- file_name = getattr(instance, 'get_%s_filename' % f.name)()
- # If the file exists and no other object of this type references it,
- # delete it from the filesystem.
- if os.path.exists(file_name) and not cls._default_manager.get_list(**{'%s__exact' % f.name: getattr(self, f.name)}):
- os.remove(file_name)
- # Run any post-delete hooks.
+
+ dispatcher.send(signal=Signals.post_delete, sender = cls, instance = instance )
+
if hasattr(instance, '_post_delete'):
instance._post_delete()
diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
index fb4e38bb03..8e2514d7ab 100644
--- a/django/db/models/fields/__init__.py
+++ b/django/db/models/fields/__init__.py
@@ -1,3 +1,5 @@
+from django.db.models.signals import Signals
+from django.dispatch import dispatcher
from django.conf import settings
from django.core import formfields, validators
from django.core.exceptions import ObjectDoesNotExist
@@ -6,6 +8,7 @@ from django.utils.text import capfirst
from django.utils.translation import gettext_lazy, ngettext
import datetime, os
+
# Random entropy string used by "default" param.
NOT_PROVIDED = 'oijpwojefiojpanv'
@@ -502,6 +505,20 @@ class FileField(Field):
setattr(cls, 'get_%s_url' % self.name, curry(cls._get_FIELD_url, field=self))
setattr(cls, 'get_%s_size' % self.name, curry(cls._get_FIELD_size, field=self))
setattr(cls, 'save_%s_file' % self.name, curry(cls._save_FIELD_file, field=self))
+ dispatcher.connect(
+ self.delete_file,
+ signal = Signals.post_delete,
+ sender = cls
+ )
+
+ def delete_file(self, instance):
+ if getattr(instance, f.attname):
+ file_name = getattr(instance, 'get_%s_filename' % f.name)()
+ # If the file exists and no other object of this type references it,
+ # delete it from the filesystem.
+ if os.path.exists(file_name) and \
+ not instance.__class__._default_manager.get_list(**{'%s__exact' % self.name: getattr(instance, self.attname)}):
+ os.remove(file_name)
def get_manipulator_field_objs(self):
return [formfields.FileUploadField, formfields.HiddenField]
diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
index 8dbc315c76..a57e0041bb 100644
--- a/django/db/models/fields/related.py
+++ b/django/db/models/fields/related.py
@@ -3,7 +3,9 @@ from django.db.models.related import RelatedObject
from django.utils.translation import gettext_lazy, string_concat
from django.utils.functional import curry
from django.core import formfields
+from django.db.models.signals import Signals
+from django.dispatch import dispatcher
# Values for Relation.edit_inline.
TABULAR, STACKED = 1, 2
@@ -14,6 +16,12 @@ RECURSIVE_RELATIONSHIP_CONSTANT = 'self'
class RelatedField(object):
pending_lookups = {}
+ dispatcher.connect(
+ lambda sender: RelatedField.do_pending_lookups(sender) ,
+ signal = Signals.class_prepared,
+ weak = False)
+
+
def add_lookup(cls, rel_cls, field):
name = field.rel.to
module = rel_cls.__module__
@@ -28,6 +36,8 @@ class RelatedField(object):
field.do_related_class(other_cls, rel_cls)
do_pending_lookups = classmethod(do_pending_lookups)
+
+
def contribute_to_class(self, cls, name):
Field.contribute_to_class(self,cls,name)
other = self.rel.to
diff --git a/django/db/models/manipulators.py b/django/db/models/manipulators.py
index eb7ee13825..6ea582ba0b 100644
--- a/django/db/models/manipulators.py
+++ b/django/db/models/manipulators.py
@@ -15,11 +15,14 @@ class ManipulatorDescriptor(object):
if instance != None:
raise "Manipulator accessed via instance"
else:
- class Man(self.get_base_manipulator(type), self.base):
- pass
- Man.classinit(type)
- Man.__name__ = self.name
- return Man
+ if not self.man:
+ class Man(self.get_base_manipulator(type), self.base):
+ pass
+
+ Man._prepare(type)
+ Man.__name__ = self.name
+ self.man = Man
+ return self.man
def get_base_manipulator(self, type):
if hasattr(type, 'MANIPULATOR'):
@@ -30,7 +33,7 @@ class ManipulatorDescriptor(object):
class AutomaticManipulator(Manipulator):
- def classinit(cls, model):
+ def _prepare(cls, model):
cls.model = model
cls.manager = model._default_manager
cls.opts = model._meta
@@ -43,7 +46,11 @@ class AutomaticManipulator(Manipulator):
setattr(cls, 'isUnique%sFor%s' % (f.name, f.unique_for_month), curry(manipulator_validator_unique_for_date, f, opts.get_field(f.unique_for_month), opts, 'month'))
if f.unique_for_year:
setattr(cls, 'isUnique%sFor%s' % (f.name, f.unique_for_year), curry(manipulator_validator_unique_for_date, f, opts.get_field(f.unique_for_year), opts, 'year'))
- classinit = classmethod(classinit)
+ _prepare = classmethod(_prepare)
+
+ def contribute_to_class(cls, other_cls, name ):
+ 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)
diff --git a/django/db/models/signals.py b/django/db/models/signals.py
new file mode 100644
index 0000000000..179ab32226
--- /dev/null
+++ b/django/db/models/signals.py
@@ -0,0 +1,11 @@
+class Signals(object):
+ class_prepared = object()
+
+ pre_init= object()
+ post_init = object()
+
+ pre_save = object()
+ post_save = object()
+
+ pre_delete = object()
+ post_delete = object()
\ No newline at end of file
diff --git a/django/dispatch/dispatcher.py b/django/dispatch/dispatcher.py
index 841cadf711..2be39f2f57 100644
--- a/django/dispatch/dispatcher.py
+++ b/django/dispatch/dispatcher.py
@@ -27,7 +27,7 @@ Internal attributes:
"""
from __future__ import generators
import types, weakref
-from dispatch import saferef, robustapply, errors
+from django.dispatch import saferef, robustapply, errors
__author__ = "Patrick K. O'Brien "
__cvsid__ = "$Id: dispatcher.py,v 1.8 2004/11/26 06:37:33 mcfletch Exp $"