diff --git a/django/core/validators.py b/django/core/validators.py
index 6cd290f..4515ca7 100644
--- a/django/core/validators.py
+++ b/django/core/validators.py
@@ -135,3 +135,26 @@ class MaxLengthValidator(BaseValidator):
     message = _(u'Ensure this value has at most %(limit_value)d characters (it has %(show_value)d).')
     code = 'max_length'
 
+
+class ComplexValidator(object):
+    def get_value(self, name, all_values, obj):
+        assert all_values or obj, "Either all_values or obj must be supplied"
+
+        if all_values:
+            return all_values.get(name, None)
+        if obj:
+            return getattr(obj, name, None)
+        
+
+    def __call__(self, value, all_values={}, obj=None):
+        raise NotImplementedError()
+
+class RequiredIfOtherFieldBlank(ComplexValidator):
+    def __init__(self, other_field):
+        self.other_field = other_field
+
+    def __call__(self, value, all_values={}, obj=None):
+        if self.get_value(self.other_field, all_values, obj) in EMPTY_VALUES:
+            if value in EMPTY_VALUES:
+                raise ValidationError('This field is required if %s is blank.' % self.other_field)
+
diff --git a/django/db/models/base.py b/django/db/models/base.py
index 660c570..eace314 100644
--- a/django/db/models/base.py
+++ b/django/db/models/base.py
@@ -799,6 +799,26 @@ class Model(object):
             except ValidationError, e:
                 errors[f.name] = e.messages
 
+        # run complex validators after the fields have been cleaned since they
+        # need access to model_instance.
+        for f in self._meta.fields:
+            if f.name in errors:
+                continue
+
+            value = getattr(self, f.attname)
+            for v in f.validators:
+                if isinstance(v, validators.ComplexValidator):
+                    try:
+                        v(value, obj=self)
+                    except ValidationError, e:
+                        error_list = errors.setdefault(f.name, [])
+                        if hasattr(e, 'code') and e.code in f.error_messages:
+                            message = f.error_messages[e.code]
+                            if e.params:
+                                message = message % e.params
+                            error_list.append(message)
+                        else:
+                            error_list.extend(e.messages)
         # Form.clean() is run even if other validation fails, so do the
         # same with Model.validate() for consistency.
         try:
diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
index 5c36437..96fc36f 100644
--- a/django/db/models/fields/__init__.py
+++ b/django/db/models/fields/__init__.py
@@ -176,16 +176,19 @@ class Field(object):
 
         errors = []
         for v in self.validators:
-            try:
-                v(value)
-            except exceptions.ValidationError, e:
-                if hasattr(e, 'code') and e.code in self.error_messages:
-                    message = self.error_messages[e.code]
-                    if e.params:
-                        message = message % e.params
-                    errors.append(message)
-                else:
-                    errors.extend(e.messages)
+            # Don't run complex validators since they need the model instance
+            # and must therefore be run on the model level.
+            if not isinstance(v, validators.ComplexValidator):
+                try:
+                    v(value)
+                except exceptions.ValidationError, e:
+                    if hasattr(e, 'code') and e.code in self.error_messages:
+                        message = self.error_messages[e.code]
+                        if e.params:
+                            message = message % e.params
+                        errors.append(message)
+                    else:
+                        errors.extend(e.messages)
         if errors:
             raise exceptions.ValidationError(errors)
 
diff --git a/django/forms/fields.py b/django/forms/fields.py
index 23a88d6..40e3d90 100644
--- a/django/forms/fields.py
+++ b/django/forms/fields.py
@@ -130,16 +130,19 @@ class Field(object):
             return
         errors = []
         for v in self.validators:
-            try:
-                v(value)
-            except ValidationError, e:
-                if hasattr(e, 'code') and e.code in self.error_messages:
-                    message = self.error_messages[e.code]
-                    if e.params:
-                        message = message % e.params
-                    errors.append(message)
-                else:
-                    errors.extend(e.messages)
+            # don't run complex validators since they need all_values
+            # and must therefore be run on the form level
+            if not isinstance(v, validators.ComplexValidator):
+                try:
+                    v(value)
+                except ValidationError, e:
+                    if hasattr(e, 'code') and e.code in self.error_messages:
+                        message = self.error_messages[e.code]
+                        if e.params:
+                            message = message % e.params
+                        errors.append(message)
+                    else:
+                        errors.extend(e.messages)
         if errors:
             raise ValidationError(errors)
 
diff --git a/django/forms/forms.py b/django/forms/forms.py
index d484300..5b8a4d0 100644
--- a/django/forms/forms.py
+++ b/django/forms/forms.py
@@ -3,6 +3,7 @@ Form classes
 """
 
 from django.core.exceptions import ValidationError
+from django.core.validators import ComplexValidator
 from django.utils.copycompat import deepcopy
 from django.utils.datastructures import SortedDict
 from django.utils.html import conditional_escape
@@ -282,6 +283,31 @@ class BaseForm(StrAndUnicode):
                 self._errors[name] = self.error_class(e.messages)
                 if name in self.cleaned_data:
                     del self.cleaned_data[name]
+
+        # Run complex validators after the fields have been cleaned since they
+        # need access to all_values.
+        for name, field in self.fields.items():
+            if not name in self.cleaned_data:
+                continue
+            failed = False
+            for v in field.validators:
+                # Skip noncomplex validators, they have already been run on the field.
+                if not isinstance(v, ComplexValidator):
+                    continue
+                try:
+                    v(self.cleaned_data[name], all_values=self.cleaned_data)
+                except ValidationError, e:
+                    failed = True
+                    error_list = self._errors.setdefault(name, self.error_class())
+                    if hasattr(e, 'code') and e.code in field.error_messages:
+                        message = field.error_messages[e.code]
+                        if e.params:
+                            message = message % e.params
+                        error_list.append(message)
+                    else:
+                        error_list.extend(e.messages)
+            if failed:
+                del self.cleaned_data[name]
         try:
             self.cleaned_data = self.clean()
         except ValidationError, e:
diff --git a/tests/modeltests/validation/models.py b/tests/modeltests/validation/models.py
index db5100b..1260417 100644
--- a/tests/modeltests/validation/models.py
+++ b/tests/modeltests/validation/models.py
@@ -1,6 +1,7 @@
 from datetime import datetime
 
 from django.core.exceptions import ValidationError
+from django.core.validators import ComplexValidator
 from django.db import models
 from django.test import TestCase
 
@@ -8,6 +9,14 @@ def validate_answer_to_universe(value):
     if value != 42:
         raise ValidationError('This is not the answer to life, universe and everything!', code='not42')
 
+class ValidateFieldNotEqualsOtherField(ComplexValidator):
+    def __init__(self, other_field):
+        self.other_field = other_field
+
+    def __call__(self, value, all_values={}, obj=None):
+        if value == self.get_value(self.other_field, all_values, obj):
+            raise ValidationError("Must not equal to %r's value" % self.other_field, code='not_equal', params=(self.other_field,))
+
 class ModelToValidate(models.Model):
     name = models.CharField(max_length=100)
     created = models.DateTimeField(default=datetime.now)
@@ -15,7 +24,7 @@ class ModelToValidate(models.Model):
     parent = models.ForeignKey('self', blank=True, null=True)
     email = models.EmailField(blank=True)
     url = models.URLField(blank=True)
-    f_with_custom_validator = models.IntegerField(blank=True, null=True, validators=[validate_answer_to_universe])
+    f_with_custom_validator = models.IntegerField(blank=True, null=True, validators=[validate_answer_to_universe, ValidateFieldNotEqualsOtherField('number')])
 
     def validate(self):
         super(ModelToValidate, self).validate()
@@ -49,7 +58,7 @@ class CustomMessagesModel(models.Model):
     other  = models.IntegerField(blank=True, null=True)
     number = models.IntegerField(
             error_messages={'null': 'NULL', 'not42': 'AAARGH', 'not_equal': '%s != me'},
-            validators=[validate_answer_to_universe]
+            validators=[validate_answer_to_universe, ValidateFieldNotEqualsOtherField('other')]
         )
 
 
diff --git a/tests/modeltests/validation/test_custom_messages.py b/tests/modeltests/validation/test_custom_messages.py
index 9a958a0..e543dbd 100644
--- a/tests/modeltests/validation/test_custom_messages.py
+++ b/tests/modeltests/validation/test_custom_messages.py
@@ -1,8 +1,11 @@
 from modeltests.validation import ValidationTestCase
-from models import CustomMessagesModel
-
 
+from models import CustomMessagesModel
 class CustomMessagesTest(ValidationTestCase):
+    def test_custom_complex_validator_message(self):
+        cmm = CustomMessagesModel(number=42, other=42)
+        self.assertFieldFailsValidationWithMessage(cmm.full_validate, 'number', ['other != me'])
+
     def test_custom_simple_validator_message(self):
         cmm = CustomMessagesModel(number=12)
         self.assertFieldFailsValidationWithMessage(cmm.full_validate, 'number', ['AAARGH'])
diff --git a/tests/modeltests/validation/validators.py b/tests/modeltests/validation/validators.py
index d46d0c5..d54aab6 100644
--- a/tests/modeltests/validation/validators.py
+++ b/tests/modeltests/validation/validators.py
@@ -18,3 +18,19 @@ class TestModelsWithValidators(ValidationTestCase):
                 [u'This is not the answer to life, universe and everything!']
             )
 
+    def test_custom_complex_validator_raises_error_for_incorrect_value(self):
+        mtv = ModelToValidate(number=42, name='Some Name', f_with_custom_validator=42)
+        self.assertFailsValidation(mtv.full_validate, ['f_with_custom_validator'])
+        self.assertFieldFailsValidationWithMessage(
+                mtv.full_validate,
+                'f_with_custom_validator',
+                [u"Must not equal to 'number''s value"]
+            )
+
+    def test_complex_validator_isnt_run_if_field_doesnt_validate(self):
+        mtv = ModelToValidate(number=32, name='Some Name', f_with_custom_validator=32)
+        self.assertFieldFailsValidationWithMessage(
+                mtv.full_validate,
+                'f_with_custom_validator',
+                [u'This is not the answer to life, universe and everything!']
+            )
diff --git a/tests/modeltests/validators/tests.py b/tests/modeltests/validators/tests.py
index 1108ee8..331f6f0 100644
--- a/tests/modeltests/validators/tests.py
+++ b/tests/modeltests/validators/tests.py
@@ -9,7 +9,8 @@ from django.core.validators import (
         validate_integer, validate_email, validate_slug, validate_ipv4_address,
         validate_comma_separated_integer_list, MaxValueValidator,
         MinValueValidator, MaxLengthValidator, MinLengthValidator,
-        URLValidator, BaseValidator, RegexValidator,
+        RequiredIfOtherFieldBlank, URLValidator, BaseValidator,
+        RegexValidator,
 )
 
 now = datetime.now()
@@ -152,3 +153,32 @@ for validator, value, expected in SIMPLE_VALIDATORS_VALUES:
     setattr(TestSimpleValidators, *get_simple_test_func(validator, expected, value, test_counter))
     test_counter += 1
 
+class TestComplexValidators(TestCase):
+    pass
+
+COMPLEX_VALIDATORS_VALUES = (
+    #(validator, value, all_values, obj, expected),
+    (RequiredIfOtherFieldBlank('other'), 'given', {'other': 'given'}, None, None),
+    (RequiredIfOtherFieldBlank('other'), '', {'other': 'given'}, None, None),
+    (RequiredIfOtherFieldBlank('other'), 'given', {}, None, AssertionError),
+    (RequiredIfOtherFieldBlank('other'), '', {}, None, AssertionError),
+    (RequiredIfOtherFieldBlank('other'), '', {'other': ''}, None, ValidationError),
+)
+
+def get_complex_test_func(validator, expected, value, all_values, obj, num):
+    if isinstance(expected, type) and issubclass(expected, Exception):
+        test_mask = 'test_%s_raises_error_%d'
+        def test_func(self):
+            self.assertRaises(expected, validator, value, all_values=all_values, obj=obj)
+    else:
+        test_mask = 'test_%s_%d'
+        def test_func(self):
+            self.assertEqual(expected, validator(value, all_values=all_values, obj=obj))
+    test_name = test_mask % (validator.__class__.__name__, num)
+    return test_name, test_func
+
+test_counter = {}
+for validator, value, all_values, obj, expected in COMPLEX_VALIDATORS_VALUES:
+    num = test_counter[validator.__class__.__name__] = test_counter.setdefault(validator.__class__.__name__, 0) + 1
+    setattr(TestComplexValidators, *get_complex_test_func(validator, expected, value, all_values, obj, num))
+
diff --git a/tests/regressiontests/forms/tests.py b/tests/regressiontests/forms/tests.py
index db70500..e9ae948 100644
--- a/tests/regressiontests/forms/tests.py
+++ b/tests/regressiontests/forms/tests.py
@@ -38,7 +38,7 @@ from formsets import tests as formset_tests
 from media import media_tests
 
 from fields import FieldsTests
-from validators import TestFieldWithValidators
+from validators import TestFormWithValidators, TestFieldWithValidators
 
 __test__ = {
     'extra_tests': extra_tests,
diff --git a/tests/regressiontests/forms/validators.py b/tests/regressiontests/forms/validators.py
index b75a14e..becf4e2 100644
--- a/tests/regressiontests/forms/validators.py
+++ b/tests/regressiontests/forms/validators.py
@@ -4,6 +4,9 @@ from django import forms
 from django.core import validators
 from django.core.exceptions import ValidationError
 
+class AlwaysFailingValidator(validators.ComplexValidator):
+    def __call__(self, value, all_values={}, obj=None):
+        raise ValidationError('AlwaysFailingValidator')
 
 class TestFieldWithValidators(TestCase):
     def test_all_errors_get_reported(self):
@@ -16,3 +19,16 @@ class TestFieldWithValidators(TestCase):
         except ValidationError, e:
             self.assertEqual(2, len(e.messages))
 
+class TestFormWithValidators(TestCase):
+    def test_all_complex_validators_get_run_even_if_they_fail(self):
+        class MyForm(forms.Form):
+            validator_field = forms.CharField(
+                    validators=[
+                        AlwaysFailingValidator(),
+                        AlwaysFailingValidator(),
+                    ]
+                )
+        form = MyForm({'validator_field': 'some value'})
+        self.assertFalse(form.is_valid())
+        self.assertEqual(['validator_field'], form.errors.keys())
+        self.assertEqual(['AlwaysFailingValidator', 'AlwaysFailingValidator'], form.errors['validator_field'])
