Index: django/db/models/fields/__init__.py =================================================================== --- django/db/models/fields/__init__.py (revision 7538) +++ django/db/models/fields/__init__.py (working copy) @@ -1157,6 +1157,11 @@ self.schema_path = schema_path Field.__init__(self, verbose_name, name, **kwargs) + def formfield(self, **kwargs): + defaults = {'form_class': curry(forms.XMLField, schema_path=self.schema_path)} + defaults.update(kwargs) + return super(XMLField, self).formfield(**defaults) + def get_manipulator_field_objs(self): return [curry(oldforms.XMLLargeTextField, schema_path=self.schema_path)] Index: django/newforms/fields.py =================================================================== --- django/newforms/fields.py (revision 7538) +++ django/newforms/fields.py (working copy) @@ -32,7 +32,7 @@ 'RegexField', 'EmailField', 'FileField', 'ImageField', 'URLField', 'BooleanField', 'NullBooleanField', 'ChoiceField', 'MultipleChoiceField', 'ComboField', 'MultiValueField', 'FloatField', 'DecimalField', - 'SplitDateTimeField', 'IPAddressField', 'FilePathField', + 'SplitDateTimeField', 'IPAddressField', 'FilePathField', 'XMLField' ) # These values, if given to to_python(), will trigger the self.required check. @@ -782,3 +782,19 @@ def __init__(self, *args, **kwargs): super(IPAddressField, self).__init__(ipv4_re, *args, **kwargs) + +class XMLField(CharField): + """ + A field that validates XML content against a RelaxNG schema + """ + def __init__(self, schema_path=None, *args, **kwargs): + super(XMLField, self).__init__(*args, **kwargs) + self.schema_path = schema_path + + def clean(self, value): + from django.core.validators import RelaxNGCompact, ValidationError as OldValidationError + if self.schema_path: + try: + RelaxNGCompact(self.schema_path)(value, "") + except OldValidationError, e: + raise ValidationError(e.messages)