<commit_before>from forms import Form
from django.utils.encoding import StrAndUnicode
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext as _
from fields import IntegerField, BooleanField
from widgets import Media, HiddenInput
from util import ErrorList, ValidationError

__all__ = ('BaseFormSet', 'all_valid')

# special field names
TOTAL_FORM_COUNT = 'TOTAL_FORMS'
INITIAL_FORM_COUNT = 'INITIAL_FORMS'
ORDERING_FIELD_NAME = 'ORDER'
DELETION_FIELD_NAME = 'DELETE'

class ManagementForm(Form):
    """
    ``ManagementForm`` is used to keep track of how many form instances
    are displayed on the page. If adding new forms via javascript, you should
    increment the count field of this form as well.
    """
    def __init__(self, *args, **kwargs):
        self.base_fields[TOTAL_FORM_COUNT] = IntegerField(widget=HiddenInput)
        self.base_fields[INITIAL_FORM_COUNT] = IntegerField(widget=HiddenInput)
        super(ManagementForm, self).__init__(*args, **kwargs)

class BaseFormSet(StrAndUnicode):
    """
    A collection of instances of the same Form class.
    """
    def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
                 initial=None, error_class=ErrorList):
        self.is_bound = data is not None or files is not None
        self.prefix = prefix or 'form'
        self.auto_id = auto_id
        self.data = data
        self.files = files
        self.initial = initial
        self.error_class = error_class
        self._errors = None
        self._non_form_errors = None
        # initialization is different depending on whether we recieved data, initial, or nothing
        if data or files:
            self.management_form = ManagementForm(data, auto_id=self.auto_id, prefix=self.prefix)
            if self.management_form.is_valid():
                self._total_form_count = self.management_form.cleaned_data[TOTAL_FORM_COUNT]
                self._initial_form_count = self.management_form.cleaned_data[INITIAL_FORM_COUNT]
            else:
                raise ValidationError('ManagementForm data is missing or has been tampered with')
        else:
            if initial:
                self._initial_form_count = len(initial)
                if self._initial_form_count > self.max_num and self.max_num > 0:
                    self._initial_form_count = self.max_num
                self._total_form_count = self._initial_form_count + self.extra
            else:
                self._initial_form_count = 0
                self._total_form_count = self.extra
            if self._total_form_count > self.max_num and self.max_num > 0:
                self._total_form_count = self.max_num
            initial = {TOTAL_FORM_COUNT: self._total_form_count,
                       INITIAL_FORM_COUNT: self._initial_form_count}
            self.management_form = ManagementForm(initial=initial, auto_id=self.auto_id, prefix=self.prefix)

        # construct the forms in the formset
        self._construct_forms()

    def __unicode__(self):
        return self.as_table()

    def _construct_forms(self):
        # instantiate all the forms and put them in self.forms
        self.forms = []
        for i in xrange(self._total_form_count):
            self.forms.append(self._construct_form(i))

    def _construct_form(self, i, **kwargs):
        """
        Instantiates and returns the i-th form instance in a formset.
        """
        defaults = {'auto_id': self.auto_id, 'prefix': self.add_prefix(i)}
        if self.data or self.files:
            defaults['data'] = self.data
            defaults['files'] = self.files
        if self.initial:
            try:
                defaults['initial'] = self.initial[i]
            except IndexError:
                pass
        # Allow extra forms to be empty.
        if i >= self._initial_form_count:
            defaults['empty_permitted'] = True
        defaults.update(kwargs)
        form = self.form(**defaults)
        self.add_fields(form, i)
        return form

    def _get_initial_forms(self):
        """Return a list of all the intial forms in this formset."""
        return self.forms[:self._initial_form_count]
    initial_forms = property(_get_initial_forms)

    def _get_extra_forms(self):
        """Return a list of all the extra forms in this formset."""
        return self.forms[self._initial_form_count:]
    extra_forms = property(_get_extra_forms)

    # Maybe this should just go away?
    def _get_cleaned_data(self):
        """
        Returns a list of form.cleaned_data dicts for every form in self.forms.
        """
        if not self.is_valid():
            raise AttributeError("'%s' object has no attribute 'cleaned_data'" % self.__class__.__name__)
        return [form.cleaned_data for form in self.forms]
    cleaned_data = property(_get_cleaned_data)

    def _get_deleted_forms(self):
        """
        Returns a list of forms that have been marked for deletion. Raises an
        AttributeError is deletion is not allowed.
        """
        if not self.is_valid() or not self.can_delete:
            raise AttributeError("'%s' object has no attribute 'deleted_forms'" % self.__class__.__name__)
        # construct _deleted_form_indexes which is just a list of form indexes
        # that have had their deletion widget set to True
        if not hasattr(self, '_deleted_form_indexes'):
            self._deleted_form_indexes = []
            for i in range(0, self._total_form_count):
                form = self.forms[i]
                # if this is an extra form and hasn't changed, don't consider it
                if i >= self._initial_form_count and not form.has_changed():
                    continue
                if form.cleaned_data[DELETION_FIELD_NAME]:
                    self._deleted_form_indexes.append(i)
        return [self.forms[i] for i in self._deleted_form_indexes]
    deleted_forms = property(_get_deleted_forms)

    def _get_ordered_forms(self):
        """
        Returns a list of form in the order specified by the incoming data.
        Raises an AttributeError is deletion is not allowed.
        """
        if not self.is_valid() or not self.can_order:
            raise AttributeError("'%s' object has no attribute 'ordered_forms'" % self.__class__.__name__)
        # Construct _ordering, which is a list of (form_index, order_field_value)
        # tuples. After constructing this list, we'll sort it by order_field_value
        # so we have a way to get to the form indexes in the order specified
        # by the form data.
        if not hasattr(self, '_ordering'):
            self._ordering = []
            for i in range(0, self._total_form_count):
                form = self.forms[i]
                # if this is an extra form and hasn't changed, don't consider it
                if i >= self._initial_form_count and not form.has_changed():
                    continue
                # don't add data marked for deletion to self.ordered_data
                if self.can_delete and form.cleaned_data[DELETION_FIELD_NAME]:
                    continue
                # A sort function to order things numerically ascending, but
                # None should be sorted below anything else. Allowing None as
                # a comparison value makes it so we can leave ordering fields
                # blamk.
                def compare_ordering_values(x, y):
                    if x[1] is None:
                        return 1
                    if y[1] is None:
                        return -1
                    return x[1] - y[1]
                self._ordering.append((i, form.cleaned_data[ORDERING_FIELD_NAME]))
            # After we're done populating self._ordering, sort it.
            self._ordering.sort(compare_ordering_values)
        # Return a list of form.cleaned_data dicts in the order spcified by
        # the form data.
        return [self.forms[i[0]] for i in self._ordering]
    ordered_forms = property(_get_ordered_forms)

    def non_form_errors(self):
        """
        Returns an ErrorList of errors that aren't associated with a particular
        form -- i.e., from formset.clean(). Returns an empty ErrorList if there
        are none.
        """
        if self._non_form_errors is not None:
            return self._non_form_errors
        return self.error_class()

    def _get_errors(self):
        """
        Returns a list of form.errors for every form in self.forms.
        """
        if self._errors is None:
            self.full_clean()
        return self._errors
    errors = property(_get_errors)

    def is_valid(self):
        """
        Returns True if form.errors is empty for every form in self.forms.
        """
        if not self.is_bound:
            return False
        # We loop over every form.errors here rather than short circuiting on the
        # first failure to make sure validation gets triggered for every form.
        forms_valid = True
        for errors in self.errors:
            if bool(errors):
                forms_valid = False
        return forms_valid and not bool(self.non_form_errors())

    def full_clean(self):
        """
        Cleans all of self.data and populates self._errors.
        """
        self._errors = []
        if not self.is_bound: # Stop further processing.
            return
        for i in range(0, self._total_form_count):
            form = self.forms[i]
            self._errors.append(form.errors)
        # Give self.clean() a chance to do cross-form validation.
        try:
            self.clean()
        except ValidationError, e:
            self._non_form_errors = e.messages

    def clean(self):
        """
        Hook for doing any extra formset-wide cleaning after Form.clean() has
        been called on every form. Any ValidationError raised by this method
        will not be associated with a particular form; it will be accesible
        via formset.non_form_errors()
        """
        pass

    def add_fields(self, form, index):
        """A hook for adding extra fields on to each form instance."""
        if self.can_order:
            # Only pre-fill the ordering field for initial forms.
            if index < self._initial_form_count:
                form.fields[ORDERING_FIELD_NAME] = IntegerField(label=_(u'Order'), initial=index+1, required=False)
            else:
                form.fields[ORDERING_FIELD_NAME] = IntegerField(label=_(u'Order'), required=False)
        if self.can_delete:
            form.fields[DELETION_FIELD_NAME] = BooleanField(label=_(u'Delete'), required=False)

    def add_prefix(self, index):
        return '%s-%s' % (self.prefix, index)

    def is_multipart(self):
        """
        Returns True if the formset needs to be multipart-encrypted, i.e. it
        has FileInput. Otherwise, False.
        """
        return self.forms[0].is_multipart()

    def _get_media(self):
        # All the forms on a FormSet are the same, so you only need to
        # interrogate the first form for media.
        if self.forms:
            return self.forms[0].media
        else:
            return Media()
    media = property(_get_media)

    def as_table(self):
        "Returns this formset rendered as HTML
s -- excluding the

."
        # XXX: there is no semantic division between forms here, there
        # probably should be. It might make sense to render each form as a
        # table row with each field as a td.
        forms = u' '.join([form.as_table() for form in self.forms])
        return mark_safe(u'\n'.join([unicode(self.management_form), forms]))

def formset_factory(form, formset=BaseFormSet, extra=1, can_order=False,
                    can_delete=False, max_num=0):
    """Return a FormSet for the given form class."""
    attrs = {'form': form, 'extra': extra,
             'can_order': can_order, 'can_delete': can_delete,
             'max_num': max_num}
    return type(form.__name__ + 'FormSet', (formset,), attrs)

def all_valid(formsets):
    """Returns true if every formset in formsets is valid."""
    valid = True
    for formset in formsets:
        if not formset.is_valid():
            valid = False
    return valid
<commit_msg>Corrected a couple of typos in docstrings of methods in BaseFormSet.<commit_after>from forms import Form
from django.utils.encoding import StrAndUnicode
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext as _
from fields import IntegerField, BooleanField
from widgets import Media, HiddenInput
from util import ErrorList, ValidationError

__all__ = ('BaseFormSet', 'all_valid')

# special field names
TOTAL_FORM_COUNT = 'TOTAL_FORMS'
INITIAL_FORM_COUNT = 'INITIAL_FORMS'
ORDERING_FIELD_NAME = 'ORDER'
DELETION_FIELD_NAME = 'DELETE'

class ManagementForm(Form):
    """
    ``ManagementForm`` is used to keep track of how many form instances
    are displayed on the page. If adding new forms via javascript, you should
    increment the count field of this form as well.
    """
    def __init__(self, *args, **kwargs):
        self.base_fields[TOTAL_FORM_COUNT] = IntegerField(widget=HiddenInput)
        self.base_fields[INITIAL_FORM_COUNT] = IntegerField(widget=HiddenInput)
        super(ManagementForm, self).__init__(*args, **kwargs)

class BaseFormSet(StrAndUnicode):
    """
    A collection of instances of the same Form class.
    """
    def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
                 initial=None, error_class=ErrorList):
        self.is_bound = data is not None or files is not None
        self.prefix = prefix or 'form'
        self.auto_id = auto_id
        self.data = data
        self.files = files
        self.initial = initial
        self.error_class = error_class
        self._errors = None
        self._non_form_errors = None
        # initialization is different depending on whether we recieved data, initial, or nothing
        if data or files:
            self.management_form = ManagementForm(data, auto_id=self.auto_id, prefix=self.prefix)
            if self.management_form.is_valid():
                self._total_form_count = self.management_form.cleaned_data[TOTAL_FORM_COUNT]
                self._initial_form_count = self.management_form.cleaned_data[INITIAL_FORM_COUNT]
            else:
                raise ValidationError('ManagementForm data is missing or has been tampered with')
        else:
            if initial:
                self._initial_form_count = len(initial)
                if self._initial_form_count > self.max_num and self.max_num > 0:
                    self._initial_form_count = self.max_num
                self._total_form_count = self._initial_form_count + self.extra
            else:
                self._initial_form_count = 0
                self._total_form_count = self.extra
            if self._total_form_count > self.max_num and self.max_num > 0:
                self._total_form_count = self.max_num
            initial = {TOTAL_FORM_COUNT: self._total_form_count,
                       INITIAL_FORM_COUNT: self._initial_form_count}
            self.management_form = ManagementForm(initial=initial, auto_id=self.auto_id, prefix=self.prefix)

        # construct the forms in the formset
        self._construct_forms()

    def __unicode__(self):
        return self.as_table()

    def _construct_forms(self):
        # instantiate all the forms and put them in self.forms
        self.forms = []
        for i in xrange(self._total_form_count):
            self.forms.append(self._construct_form(i))

    def _construct_form(self, i, **kwargs):
        """
        Instantiates and returns the i-th form instance in a formset.
        """
        defaults = {'auto_id': self.auto_id, 'prefix': self.add_prefix(i)}
        if self.data or self.files:
            defaults['data'] = self.data
            defaults['files'] = self.files
        if self.initial:
            try:
                defaults['initial'] = self.initial[i]
            except IndexError:
                pass
        # Allow extra forms to be empty.
        if i >= self._initial_form_count:
            defaults['empty_permitted'] = True
        defaults.update(kwargs)
        form = self.form(**defaults)
        self.add_fields(form, i)
        return form

    def _get_initial_forms(self):
        """Return a list of all the intial forms in this formset."""
        return self.forms[:self._initial_form_count]
    initial_forms = property(_get_initial_forms)

    def _get_extra_forms(self):
        """Return a list of all the extra forms in this formset."""
        return self.forms[self._initial_form_count:]
    extra_forms = property(_get_extra_forms)

    # Maybe this should just go away?
    def _get_cleaned_data(self):
        """
        Returns a list of form.cleaned_data dicts for every form in self.forms.
        """
        if not self.is_valid():
            raise AttributeError("'%s' object has no attribute 'cleaned_data'" % self.__class__.__name__)
        return [form.cleaned_data for form in self.forms]
    cleaned_data = property(_get_cleaned_data)

    def _get_deleted_forms(self):
        """
        Returns a list of forms that have been marked for deletion. Raises an
        AttributeError if deletion is not allowed.
        """
        if not self.is_valid() or not self.can_delete:
            raise AttributeError("'%s' object has no attribute 'deleted_forms'" % self.__class__.__name__)
        # construct _deleted_form_indexes which is just a list of form indexes
        # that have had their deletion widget set to True
        if not hasattr(self, '_deleted_form_indexes'):
            self._deleted_form_indexes = []
            for i in range(0, self._total_form_count):
                form = self.forms[i]
                # if this is an extra form and hasn't changed, don't consider it
                if i >= self._initial_form_count and not form.has_changed():
                    continue
                if form.cleaned_data[DELETION_FIELD_NAME]:
                    self._deleted_form_indexes.append(i)
        return [self.forms[i] for i in self._deleted_form_indexes]
    deleted_forms = property(_get_deleted_forms)

    def _get_ordered_forms(self):
        """
        Returns a list of form in the order specified by the incoming data.
        Raises an AttributeError if deletion is not allowed.
        """
        if not self.is_valid() or not self.can_order:
            raise AttributeError("'%s' object has no attribute 'ordered_forms'" % self.__class__.__name__)
        # Construct _ordering, which is a list of (form_index, order_field_value)
        # tuples. After constructing this list, we'll sort it by order_field_value
        # so we have a way to get to the form indexes in the order specified
        # by the form data.
        if not hasattr(self, '_ordering'):
            self._ordering = []
            for i in range(0, self._total_form_count):
                form = self.forms[i]
                # if this is an extra form and hasn't changed, don't consider it
                if i >= self._initial_form_count and not form.has_changed():
                    continue
                # don't add data marked for deletion to self.ordered_data
                if self.can_delete and form.cleaned_data[DELETION_FIELD_NAME]:
                    continue
                # A sort function to order things numerically ascending, but
                # None should be sorted below anything else. Allowing None as
                # a comparison value makes it so we can leave ordering fields
                # blamk.
                def compare_ordering_values(x, y):
                    if x[1] is None:
                        return 1
                    if y[1] is None:
                        return -1
                    return x[1] - y[1]
                self._ordering.append((i, form.cleaned_data[ORDERING_FIELD_NAME]))
            # After we're done populating self._ordering, sort it.
            self._ordering.sort(compare_ordering_values)
        # Return a list of form.cleaned_data dicts in the order spcified by
        # the form data.
        return [self.forms[i[0]] for i in self._ordering]
    ordered_forms = property(_get_ordered_forms)

    def non_form_errors(self):
        """
        Returns an ErrorList of errors that aren't associated with a particular
        form -- i.e., from formset.clean(). Returns an empty ErrorList if there
        are none.
        """
        if self._non_form_errors is not None:
            return self._non_form_errors
        return self.error_class()

    def _get_errors(self):
        """
        Returns a list of form.errors for every form in self.forms.
        """
        if self._errors is None:
            self.full_clean()
        return self._errors
    errors = property(_get_errors)

    def is_valid(self):
        """
        Returns True if form.errors is empty for every form in self.forms.
        """
        if not self.is_bound:
            return False
        # We loop over every form.errors here rather than short circuiting on the
        # first failure to make sure validation gets triggered for every form.
        forms_valid = True
        for errors in self.errors:
            if bool(errors):
                forms_valid = False
        return forms_valid and not bool(self.non_form_errors())

    def full_clean(self):
        """
        Cleans all of self.data and populates self._errors.
        """
        self._errors = []
        if not self.is_bound: # Stop further processing.
            return
        for i in range(0, self._total_form_count):
            form = self.forms[i]
            self._errors.append(form.errors)
        # Give self.clean() a chance to do cross-form validation.
        try:
            self.clean()
        except ValidationError, e:
            self._non_form_errors = e.messages

    def clean(self):
        """
        Hook for doing any extra formset-wide cleaning after Form.clean() has
        been called on every form. Any ValidationError raised by this method
        will not be associated with a particular form; it will be accesible
        via formset.non_form_errors()
        """
        pass

    def add_fields(self, form, index):
        """A hook for adding extra fields on to each form instance."""
        if self.can_order:
            # Only pre-fill the ordering field for initial forms.
            if index < self._initial_form_count:
                form.fields[ORDERING_FIELD_NAME] = IntegerField(label=_(u'Order'), initial=index+1, required=False)
            else:
                form.fields[ORDERING_FIELD_NAME] = IntegerField(label=_(u'Order'), required=False)
        if self.can_delete:
            form.fields[DELETION_FIELD_NAME] = BooleanField(label=_(u'Delete'), required=False)

    def add_prefix(self, index):
        return '%s-%s' % (self.prefix, index)

    def is_multipart(self):
        """
        Returns True if the formset needs to be multipart-encrypted, i.e. it
        has FileInput. Otherwise, False.
        """
        return self.forms[0].is_multipart()

    def _get_media(self):
        # All the forms on a FormSet are the same, so you only need to
        # interrogate the first form for media.
        if self.forms:
            return self.forms[0].media
        else:
            return Media()
    media = property(_get_media)

    def as_table(self):
        "Returns this formset rendered as HTML
s -- excluding the

."
        # XXX: there is no semantic division between forms here, there
        # probably should be. It might make sense to render each form as a
        # table row with each field as a td.
        forms = u' '.join([form.as_table() for form in self.forms])
        return mark_safe(u'\n'.join([unicode(self.management_form), forms]))

def formset_factory(form, formset=BaseFormSet, extra=1, can_order=False,
                    can_delete=False, max_num=0):
    """Return a FormSet for the given form class."""
    attrs = {'form': form, 'extra': extra,
             'can_order': can_order, 'can_delete': can_delete,
             'max_num': max_num}
    return type(form.__name__ + 'FormSet', (formset,), attrs)

def all_valid(formsets):
    """Returns true if every formset in formsets is valid."""
    valid = True
    for formset in formsets:
        if not formset.is_valid():
            valid = False
    return valid
<|endoftext|>


<commit_before>"""
Form classes
"""

from copy import deepcopy

from django.utils.datastructures import SortedDict
from django.utils.html import escape
from django.utils.encoding import StrAndUnicode, smart_unicode, force_unicode
from django.utils.safestring import mark_safe

from fields import Field, FileField
from widgets import TextInput, Textarea
from util import flatatt, ErrorDict, ErrorList, ValidationError

__all__ = ('BaseForm', 'Form')

NON_FIELD_ERRORS = '__all__'

def pretty_name(name):
    "Converts 'first_name' to 'First name'"
    name = name[0].upper() + name[1:]
    return name.replace('_', ' ')

def get_declared_fields(bases, attrs, with_base_fields=True):
    """
    Create a list of form field instances from the passed in 'attrs', plus any
    similar fields on the base classes (in 'bases'). This is used by both the
    Form and ModelForm metclasses.

    If 'with_base_fields' is True, all fields from the bases are used.
    Otherwise, only fields in the 'declared_fields' attribute on the bases are
    used. The distinction is useful in ModelForm subclassing.
    """
    fields = [(field_name, attrs.pop(field_name)) for field_name, obj in attrs.items() if isinstance(obj, Field)]
    fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter))

    # If this class is subclassing another Form, add that Form's fields.
    # Note that we loop over the bases in *reverse*. This is necessary in
    # order to preserve the correct order of fields.
    if with_base_fields:
        for base in bases[::-1]:
            if hasattr(base, 'base_fields'):
                fields = base.base_fields.items() + fields
    else:
        for base in bases[::-1]:
            if hasattr(base, 'declared_fields'):
                fields = base.declared_fields.items() + fields

    return SortedDict(fields)

class DeclarativeFieldsMetaclass(type):
    """
    Metaclass that converts Field attributes to a dictionary called
    'base_fields', taking into account parent class 'base_fields' as well.
    """
    def __new__(cls, name, bases, attrs):
        attrs['base_fields'] = get_declared_fields(bases, attrs)
        return type.__new__(cls, name, bases, attrs)

class BaseForm(StrAndUnicode):
    # This is the main implementation of all the Form logic. Note that this
    # class is different than Form. See the comments by the Form class for more
    # information. Any improvements to the form API should be made to *this*
    # class, not to the Form class.
    def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
                 initial=None, error_class=ErrorList, label_suffix=':'):
        self.is_bound = data is not None or files is not None
        self.data = data or {}
        self.files = files or {}
        self.auto_id = auto_id
        self.prefix = prefix
        self.initial = initial or {}
        self.error_class = error_class
        self.label_suffix = label_suffix
        self._errors = None # Stores the errors after clean() has been called.

        # The base_fields class attribute is the *class-wide* definition of
        # fields. Because a particular *instance* of the class might want to
        # alter self.fields, we create self.fields here by copying base_fields.
        # Instances should always modify self.fields; they should not modify
        # self.base_fields.
        self.fields = deepcopy(self.base_fields)

    def __unicode__(self):
        return self.as_table()

    def __iter__(self):
        for name, field in self.fields.items():
            yield BoundField(self, field, name)

    def __getitem__(self, name):
        "Returns a BoundField with the given name."
        try:
            field = self.fields[name]
        except KeyError:
            raise KeyError('Key %r not found in Form' % name)
        return BoundField(self, field, name)

    def _get_errors(self):
        "Returns an ErrorDict for the data provided for the form"
        if self._errors is None:
            self.full_clean()
        return self._errors
    errors = property(_get_errors)

    def is_valid(self):
        """
        Returns True if the form has no errors. Otherwise, False. If errors are
        being ignored, returns False.
        """
        return self.is_bound and not bool(self.errors)

    def add_prefix(self, field_name):
        """
        Returns the field name with a prefix appended, if this Form has a
        prefix set.

        Subclasses may wish to override.
        """
        return self.prefix and ('%s-%s' % (self.prefix, field_name)) or field_name

    def _html_output(self, normal_row, error_row, row_ender, help_text_html, errors_on_separate_row):
        "Helper function for outputting HTML. Used by as_table(), as_ul(), as_p()."
        top_errors = self.non_field_errors() # Errors that should be displayed above all fields.
        output, hidden_fields = [], []
        for name, field in self.fields.items():
            bf = BoundField(self, field, name)
            bf_errors = self.error_class([escape(error) for error in bf.errors]) # Escape and cache in local variable.
            if bf.is_hidden:
                if bf_errors:
                    top_errors.extend(['(Hidden field %s) %s' % (name, force_unicode(e)) for e in bf_errors])
                hidden_fields.append(unicode(bf))
            else:
                if errors_on_separate_row and bf_errors:
                    output.append(error_row % force_unicode(bf_errors))
                if bf.label:
                    label = escape(force_unicode(bf.label))
                    # Only add the suffix if the label does not end in
                    # punctuation.
                    if self.label_suffix:
                        if label[-1] not in ':?.!':
                            label += self.label_suffix
                    label = bf.label_tag(label) or ''
                else:
                    label = ''
                if field.help_text:
                    help_text = help_text_html % force_unicode(field.help_text)
                else:
                    help_text = u''
                output.append(normal_row % {'errors': force_unicode(bf_errors), 'label': force_unicode(label), 'field': unicode(bf), 'help_text': help_text})
        if top_errors:
            output.insert(0, error_row % top_errors)
        if hidden_fields: # Insert any hidden fields in the last row.
            str_hidden = u''.join(hidden_fields)
            if output:
                last_row = output[-1]
                # Chop off the trailing row_ender (e.g. '
') and
                # insert the hidden fields.
                output[-1] = last_row[:-len(row_ender)] + str_hidden + row_ender
            else:
                # If there aren't any rows in the output, just append the
                # hidden fields.
                output.append(str_hidden)
        return mark_safe(u'\n'.join(output))

    def as_table(self):
        "Returns this form rendered as HTML
s -- excluding the

."
        return self._html_output(u'
%(label)s	%(errors)s%(field)s%(help_text)s
', u'
%s
', '
', u'
%s', False)

    def as_ul(self):
        "Returns this form rendered as HTML
s -- excluding the

."
        return self._html_output(u'
%(errors)s%(label)s %(field)s%(help_text)s
', u'
%s
', '
', u' %s', False)

    def as_p(self):
        "Returns this form rendered as HTML
s."
        return self._html_output(u'
%(label)s %(field)s%(help_text)s
', u'%s', '
', u' %s', True)

    def non_field_errors(self):
        """
        Returns an ErrorList of errors that aren't associated with a particular
        field -- i.e., from Form.clean(). Returns an empty ErrorList if there
        are none.
        """
        return self.errors.get(NON_FIELD_ERRORS, self.error_class())

    def full_clean(self):
        """
        Cleans all of self.data and populates self._errors and
        self.cleaned_data.
        """
        self._errors = ErrorDict()
        if not self.is_bound: # Stop further processing.
            return
        self.cleaned_data = {}
        for name, field in self.fields.items():
            # value_from_datadict() gets the data from the data dictionaries.
            # Each widget type knows how to retrieve its own data, because some
            # widgets split data over several HTML fields.
            value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
            try:
                if isinstance(field, FileField):
                    initial = self.initial.get(name, field.initial)
                    value = field.clean(value, initial)
                else:
                    value = field.clean(value)
                self.cleaned_data[name] = value
                if hasattr(self, 'clean_%s' % name):
                    value = getattr(self, 'clean_%s' % name)()
                    self.cleaned_data[name] = value
            except ValidationError, e:
                self._errors[name] = e.messages
                if name in self.cleaned_data:
                    del self.cleaned_data[name]
        try:
            self.cleaned_data = self.clean()
        except ValidationError, e:
            self._errors[NON_FIELD_ERRORS] = e.messages
        if self._errors:
            delattr(self, 'cleaned_data')

    def clean(self):
        """
        Hook for doing any extra form-wide cleaning after Field.clean() been
        called on every field. Any ValidationError raised by this method will
        not be associated with a particular field; it will have a special-case
        association with the field named '__all__'.
        """
        return self.cleaned_data

    def is_multipart(self):
        """
        Returns True if the form needs to be multipart-encrypted, i.e. it has
        FileInput. Otherwise, False.
        """
        for field in self.fields.values():
            if field.widget.needs_multipart_form:
                return True
        return False

class Form(BaseForm):
    "A collection of Fields, plus their associated data."
    # This is a separate class from BaseForm in order to abstract the way
    # self.fields is specified. This class (Form) is the one that does the
    # fancy metaclass stuff purely for the semantic sugar -- it allows one
    # to define a form using declarative syntax.
    # BaseForm itself has no way of designating self.fields.
    __metaclass__ = DeclarativeFieldsMetaclass

class BoundField(StrAndUnicode):
    "A Field plus data"
    def __init__(self, form, field, name):
        self.form = form
        self.field = field
        self.name = name
        self.html_name = form.add_prefix(name)
        if self.field.label is None:
            self.label = pretty_name(name)
        else:
            self.label = self.field.label
        self.help_text = field.help_text or ''

    def __unicode__(self):
        """Renders this field as an HTML widget."""
        return self.as_widget()

    def _errors(self):
        """
        Returns an ErrorList for this field. Returns an empty ErrorList
        if there are none.
        """
        return self.form.errors.get(self.name, self.form.error_class())
    errors = property(_errors)

    def as_widget(self, widget=None, attrs=None):
        """
        Renders the field by rendering the passed widget, adding any HTML
        attributes passed as attrs.  If no widget is specified, then the
        field's default widget will be used.
        """
        if not widget:
            widget = self.field.widget
        attrs = attrs or {}
        auto_id = self.auto_id
        if auto_id and 'id' not in attrs and 'id' not in widget.attrs:
            attrs['id'] = auto_id
        if not self.form.is_bound:
            data = self.form.initial.get(self.name, self.field.initial)
            if callable(data):
                data = data()
        else:
            data = self.data
        return widget.render(self.html_name, data, attrs=attrs)

    def as_text(self, attrs=None):
        """
        Returns a string of HTML for representing this as an <input type="text">.
        """
        return self.as_widget(TextInput(), attrs)

    def as_textarea(self, attrs=None):
        "Returns a string of HTML for representing this as a <textarea>."
        return self.as_widget(Textarea(), attrs)

    def as_hidden(self, attrs=None):
        """
        Returns a string of HTML for representing this as an <input type="hidden">.
        """
        return self.as_widget(self.field.hidden_widget(), attrs)

    def _data(self):
        """
        Returns the data for this BoundField, or None if it wasn't given.
        """
        return self.field.widget.value_from_datadict(self.form.data, self.form.files, self.html_name)
    data = property(_data)

    def label_tag(self, contents=None, attrs=None):
        """
        Wraps the given contents in a , if the field has an ID attribute.
        Does not HTML-escape the contents. If contents aren't given, uses the
        field's HTML-escaped label.

        If attrs are given, they're used as HTML attributes on the  tag.
        """
        contents = contents or escape(self.label)
        widget = self.field.widget
        id_ = widget.attrs.get('id') or self.auto_id
        if id_:
            attrs = attrs and flatatt(attrs) or ''
            contents = '%s' % (widget.id_for_label(id_), attrs, contents)
        return mark_safe(contents)

    def _is_hidden(self):
        "Returns True if this BoundField's widget is hidden."
        return self.field.widget.is_hidden
    is_hidden = property(_is_hidden)

    def _auto_id(self):
        """
        Calculates and returns the ID attribute for this BoundField, if the
        associated Form has specified auto_id. Returns an empty string otherwise.
        """
        auto_id = self.form.auto_id
        if auto_id and '%s' in smart_unicode(auto_id):
            return smart_unicode(auto_id) % self.html_name
        elif auto_id:
            return self.html_name
        return ''
    auto_id = property(_auto_id)
<commit_msg>Fixed #5811 -- Added an explicit conversion to unicode that was otherwise causing problems in some cases. Thanks, Mike Maravillo.<commit_after>"""
Form classes
"""

from copy import deepcopy

from django.utils.datastructures import SortedDict
from django.utils.html import escape
from django.utils.encoding import StrAndUnicode, smart_unicode, force_unicode
from django.utils.safestring import mark_safe

from fields import Field, FileField
from widgets import TextInput, Textarea
from util import flatatt, ErrorDict, ErrorList, ValidationError

__all__ = ('BaseForm', 'Form')

NON_FIELD_ERRORS = '__all__'

def pretty_name(name):
    "Converts 'first_name' to 'First name'"
    name = name[0].upper() + name[1:]
    return name.replace('_', ' ')

def get_declared_fields(bases, attrs, with_base_fields=True):
    """
    Create a list of form field instances from the passed in 'attrs', plus any
    similar fields on the base classes (in 'bases'). This is used by both the
    Form and ModelForm metclasses.

    If 'with_base_fields' is True, all fields from the bases are used.
    Otherwise, only fields in the 'declared_fields' attribute on the bases are
    used. The distinction is useful in ModelForm subclassing.
    """
    fields = [(field_name, attrs.pop(field_name)) for field_name, obj in attrs.items() if isinstance(obj, Field)]
    fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter))

    # If this class is subclassing another Form, add that Form's fields.
    # Note that we loop over the bases in *reverse*. This is necessary in
    # order to preserve the correct order of fields.
    if with_base_fields:
        for base in bases[::-1]:
            if hasattr(base, 'base_fields'):
                fields = base.base_fields.items() + fields
    else:
        for base in bases[::-1]:
            if hasattr(base, 'declared_fields'):
                fields = base.declared_fields.items() + fields

    return SortedDict(fields)

class DeclarativeFieldsMetaclass(type):
    """
    Metaclass that converts Field attributes to a dictionary called
    'base_fields', taking into account parent class 'base_fields' as well.
    """
    def __new__(cls, name, bases, attrs):
        attrs['base_fields'] = get_declared_fields(bases, attrs)
        return type.__new__(cls, name, bases, attrs)

class BaseForm(StrAndUnicode):
    # This is the main implementation of all the Form logic. Note that this
    # class is different than Form. See the comments by the Form class for more
    # information. Any improvements to the form API should be made to *this*
    # class, not to the Form class.
    def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
                 initial=None, error_class=ErrorList, label_suffix=':'):
        self.is_bound = data is not None or files is not None
        self.data = data or {}
        self.files = files or {}
        self.auto_id = auto_id
        self.prefix = prefix
        self.initial = initial or {}
        self.error_class = error_class
        self.label_suffix = label_suffix
        self._errors = None # Stores the errors after clean() has been called.

        # The base_fields class attribute is the *class-wide* definition of
        # fields. Because a particular *instance* of the class might want to
        # alter self.fields, we create self.fields here by copying base_fields.
        # Instances should always modify self.fields; they should not modify
        # self.base_fields.
        self.fields = deepcopy(self.base_fields)

    def __unicode__(self):
        return self.as_table()

    def __iter__(self):
        for name, field in self.fields.items():
            yield BoundField(self, field, name)

    def __getitem__(self, name):
        "Returns a BoundField with the given name."
        try:
            field = self.fields[name]
        except KeyError:
            raise KeyError('Key %r not found in Form' % name)
        return BoundField(self, field, name)

    def _get_errors(self):
        "Returns an ErrorDict for the data provided for the form"
        if self._errors is None:
            self.full_clean()
        return self._errors
    errors = property(_get_errors)

    def is_valid(self):
        """
        Returns True if the form has no errors. Otherwise, False. If errors are
        being ignored, returns False.
        """
        return self.is_bound and not bool(self.errors)

    def add_prefix(self, field_name):
        """
        Returns the field name with a prefix appended, if this Form has a
        prefix set.

        Subclasses may wish to override.
        """
        return self.prefix and ('%s-%s' % (self.prefix, field_name)) or field_name

    def _html_output(self, normal_row, error_row, row_ender, help_text_html, errors_on_separate_row):
        "Helper function for outputting HTML. Used by as_table(), as_ul(), as_p()."
        top_errors = self.non_field_errors() # Errors that should be displayed above all fields.
        output, hidden_fields = [], []
        for name, field in self.fields.items():
            bf = BoundField(self, field, name)
            bf_errors = self.error_class([escape(error) for error in bf.errors]) # Escape and cache in local variable.
            if bf.is_hidden:
                if bf_errors:
                    top_errors.extend([u'(Hidden field %s) %s' % (name, force_unicode(e)) for e in bf_errors])
                hidden_fields.append(unicode(bf))
            else:
                if errors_on_separate_row and bf_errors:
                    output.append(error_row % force_unicode(bf_errors))
                if bf.label:
                    label = escape(force_unicode(bf.label))
                    # Only add the suffix if the label does not end in
                    # punctuation.
                    if self.label_suffix:
                        if label[-1] not in ':?.!':
                            label += self.label_suffix
                    label = bf.label_tag(label) or ''
                else:
                    label = ''
                if field.help_text:
                    help_text = help_text_html % force_unicode(field.help_text)
                else:
                    help_text = u''
                output.append(normal_row % {'errors': force_unicode(bf_errors), 'label': force_unicode(label), 'field': unicode(bf), 'help_text': help_text})
        if top_errors:
            output.insert(0, error_row % force_unicode(top_errors))
        if hidden_fields: # Insert any hidden fields in the last row.
            str_hidden = u''.join(hidden_fields)
            if output:
                last_row = output[-1]
                # Chop off the trailing row_ender (e.g. '
') and
                # insert the hidden fields.
                output[-1] = last_row[:-len(row_ender)] + str_hidden + row_ender
            else:
                # If there aren't any rows in the output, just append the
                # hidden fields.
                output.append(str_hidden)
        return mark_safe(u'\n'.join(output))

    def as_table(self):
        "Returns this form rendered as HTML
s -- excluding the

."
        return self._html_output(u'
%(label)s	%(errors)s%(field)s%(help_text)s
', u'
%s
', '
', u'
%s', False)

    def as_ul(self):
        "Returns this form rendered as HTML
s -- excluding the

."
        return self._html_output(u'
%(errors)s%(label)s %(field)s%(help_text)s
', u'
%s
', '
', u' %s', False)

    def as_p(self):
        "Returns this form rendered as HTML
s."
        return self._html_output(u'
%(label)s %(field)s%(help_text)s
', u'%s', '
', u' %s', True)

    def non_field_errors(self):
        """
        Returns an ErrorList of errors that aren't associated with a particular
        field -- i.e., from Form.clean(). Returns an empty ErrorList if there
        are none.
        """
        return self.errors.get(NON_FIELD_ERRORS, self.error_class())

    def full_clean(self):
        """
        Cleans all of self.data and populates self._errors and
        self.cleaned_data.
        """
        self._errors = ErrorDict()
        if not self.is_bound: # Stop further processing.
            return
        self.cleaned_data = {}
        for name, field in self.fields.items():
            # value_from_datadict() gets the data from the data dictionaries.
            # Each widget type knows how to retrieve its own data, because some
            # widgets split data over several HTML fields.
            value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
            try:
                if isinstance(field, FileField):
                    initial = self.initial.get(name, field.initial)
                    value = field.clean(value, initial)
                else:
                    value = field.clean(value)
                self.cleaned_data[name] = value
                if hasattr(self, 'clean_%s' % name):
                    value = getattr(self, 'clean_%s' % name)()
                    self.cleaned_data[name] = value
            except ValidationError, e:
                self._errors[name] = e.messages
                if name in self.cleaned_data:
                    del self.cleaned_data[name]
        try:
            self.cleaned_data = self.clean()
        except ValidationError, e:
            self._errors[NON_FIELD_ERRORS] = e.messages
        if self._errors:
            delattr(self, 'cleaned_data')

    def clean(self):
        """
        Hook for doing any extra form-wide cleaning after Field.clean() been
        called on every field. Any ValidationError raised by this method will
        not be associated with a particular field; it will have a special-case
        association with the field named '__all__'.
        """
        return self.cleaned_data

    def is_multipart(self):
        """
        Returns True if the form needs to be multipart-encrypted, i.e. it has
        FileInput. Otherwise, False.
        """
        for field in self.fields.values():
            if field.widget.needs_multipart_form:
                return True
        return False

class Form(BaseForm):
    "A collection of Fields, plus their associated data."
    # This is a separate class from BaseForm in order to abstract the way
    # self.fields is specified. This class (Form) is the one that does the
    # fancy metaclass stuff purely for the semantic sugar -- it allows one
    # to define a form using declarative syntax.
    # BaseForm itself has no way of designating self.fields.
    __metaclass__ = DeclarativeFieldsMetaclass

class BoundField(StrAndUnicode):
    "A Field plus data"
    def __init__(self, form, field, name):
        self.form = form
        self.field = field
        self.name = name
        self.html_name = form.add_prefix(name)
        if self.field.label is None:
            self.label = pretty_name(name)
        else:
            self.label = self.field.label
        self.help_text = field.help_text or ''

    def __unicode__(self):
        """Renders this field as an HTML widget."""
        return self.as_widget()

    def _errors(self):
        """
        Returns an ErrorList for this field. Returns an empty ErrorList
        if there are none.
        """
        return self.form.errors.get(self.name, self.form.error_class())
    errors = property(_errors)

    def as_widget(self, widget=None, attrs=None):
        """
        Renders the field by rendering the passed widget, adding any HTML
        attributes passed as attrs.  If no widget is specified, then the
        field's default widget will be used.
        """
        if not widget:
            widget = self.field.widget
        attrs = attrs or {}
        auto_id = self.auto_id
        if auto_id and 'id' not in attrs and 'id' not in widget.attrs:
            attrs['id'] = auto_id
        if not self.form.is_bound:
            data = self.form.initial.get(self.name, self.field.initial)
            if callable(data):
                data = data()
        else:
            data = self.data
        return widget.render(self.html_name, data, attrs=attrs)

    def as_text(self, attrs=None):
        """
        Returns a string of HTML for representing this as an <input type="text">.
        """
        return self.as_widget(TextInput(), attrs)

    def as_textarea(self, attrs=None):
        "Returns a string of HTML for representing this as a <textarea>."
        return self.as_widget(Textarea(), attrs)

    def as_hidden(self, attrs=None):
        """
        Returns a string of HTML for representing this as an <input type="hidden">.
        """
        return self.as_widget(self.field.hidden_widget(), attrs)

    def _data(self):
        """
        Returns the data for this BoundField, or None if it wasn't given.
        """
        return self.field.widget.value_from_datadict(self.form.data, self.form.files, self.html_name)
    data = property(_data)

    def label_tag(self, contents=None, attrs=None):
        """
        Wraps the given contents in a , if the field has an ID attribute.
        Does not HTML-escape the contents. If contents aren't given, uses the
        field's HTML-escaped label.

        If attrs are given, they're used as HTML attributes on the  tag.
        """
        contents = contents or escape(self.label)
        widget = self.field.widget
        id_ = widget.attrs.get('id') or self.auto_id
        if id_:
            attrs = attrs and flatatt(attrs) or ''
            contents = '%s' % (widget.id_for_label(id_), attrs, contents)
        return mark_safe(contents)

    def _is_hidden(self):
        "Returns True if this BoundField's widget is hidden."
        return self.field.widget.is_hidden
    is_hidden = property(_is_hidden)

    def _auto_id(self):
        """
        Calculates and returns the ID attribute for this BoundField, if the
        associated Form has specified auto_id. Returns an empty string otherwise.
        """
        auto_id = self.form.auto_id
        if auto_id and '%s' in smart_unicode(auto_id):
            return smart_unicode(auto_id) % self.html_name
        elif auto_id:
            return self.html_name
        return ''
    auto_id = property(_auto_id)
<|endoftext|>


<commit_before>"""
Form classes
"""

from django.utils.datastructures import SortedDict
from fields import Field
from widgets import TextInput, Textarea
from util import ErrorDict, ErrorList, ValidationError

NON_FIELD_ERRORS = '__all__'

def pretty_name(name):
    "Converts 'first_name' to 'First name'"
    name = name[0].upper() + name[1:]
    return name.replace('_', ' ')

class SortedDictFromList(SortedDict):
    "A dictionary that keeps its keys in the order in which they're inserted."
    # This is different than django.utils.datastructures.SortedDict, because
    # this takes a list/tuple as the argument to __init__().
    def __init__(self, data=None):
        if data is None: data = []
        self.keyOrder = [d[0] for d in data]
        dict.__init__(self, dict(data))

class DeclarativeFieldsMetaclass(type):
    "Metaclass that converts Field attributes to a dictionary called 'fields'."
    def __new__(cls, name, bases, attrs):
        fields = [(name, attrs.pop(name)) for name, obj in attrs.items() if isinstance(obj, Field)]
        fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter))
        attrs['fields'] = SortedDictFromList(fields)
        return type.__new__(cls, name, bases, attrs)

class Form(object):
    "A collection of Fields, plus their associated data."
    __metaclass__ = DeclarativeFieldsMetaclass

    def __init__(self, data=None, auto_id=False): # TODO: prefix stuff
        self.ignore_errors = data is None
        self.data = data or {}
        self.auto_id = auto_id
        self.clean_data = None # Stores the data after clean() has been called.
        self.__errors = None # Stores the errors after clean() has been called.

    def __str__(self):
        return self.as_table()

    def __iter__(self):
        for name, field in self.fields.items():
            yield BoundField(self, field, name)

    def __getitem__(self, name):
        "Returns a BoundField with the given name."
        try:
            field = self.fields[name]
        except KeyError:
            raise KeyError('Key %r not found in Form' % name)
        return BoundField(self, field, name)

    def _errors(self):
        "Returns an ErrorDict for self.data"
        if self.__errors is None:
            self.full_clean()
        return self.__errors
    errors = property(_errors)

    def is_valid(self):
        """
        Returns True if the form has no errors. Otherwise, False. If errors are
        being ignored, returns False.
        """
        return not self.ignore_errors and not bool(self.errors)

    def as_table(self):
        "Returns this form rendered as HTML
s -- excluding the

."
        output = []
        if self.errors.get(NON_FIELD_ERRORS):
            # Errors not corresponding to a particular field are displayed at the top.
            output.append(u'

%s

' % u'\n'.join([u'
%s
' % e for e in self.errors[NON_FIELD_ERRORS]]))
        for name, field in self.fields.items():
            bf = BoundField(self, field, name)
            if bf.errors:
                output.append(u'

%s

' % u'\n'.join([u'
%s
' % e for e in bf.errors]))
            output.append(u'
%s:	%s
' % (pretty_name(name), bf))
        return u'\n'.join(output)

    def as_ul(self):
        "Returns this form rendered as HTML
s -- excluding the

."
        output = []
        if self.errors.get(NON_FIELD_ERRORS):
            # Errors not corresponding to a particular field are displayed at the top.
            output.append(u'

%s

' % u'\n'.join([u'
%s
' % e for e in self.errors[NON_FIELD_ERRORS]]))
        for name, field in self.fields.items():
            bf = BoundField(self, field, name)
            line = u'
'
            if bf.errors:
                line += u'
%s
' % u'\n'.join([u'
%s
' % e for e in bf.errors])
            line += u'%s: %s
' % (pretty_name(name), bf)
            output.append(line)
        return u'\n'.join(output)

    def full_clean(self):
        """
        Cleans all of self.data and populates self.__errors and self.clean_data.
        """
        self.clean_data = {}
        errors = ErrorDict()
        if self.ignore_errors: # Stop further processing.
            self.__errors = errors
            return
        for name, field in self.fields.items():
            value = self.data.get(name, None)
            try:
                value = field.clean(value)
                self.clean_data[name] = value
                if hasattr(self, 'clean_%s' % name):
                    value = getattr(self, 'clean_%s' % name)()
                self.clean_data[name] = value
            except ValidationError, e:
                errors[name] = e.messages
        try:
            self.clean_data = self.clean()
        except ValidationError, e:
            errors[NON_FIELD_ERRORS] = e.messages
        if errors:
            self.clean_data = None
        self.__errors = errors

    def clean(self):
        """
        Hook for doing any extra form-wide cleaning after Field.clean() been
        called on every field.
        """
        return self.clean_data

class BoundField(object):
    "A Field plus data"
    def __init__(self, form, field, name):
        self._form = form
        self._field = field
        self._name = name

    def __str__(self):
        "Renders this field as an HTML widget."
        # Use the 'widget' attribute on the field to determine which type
        # of HTML widget to use.
        value = self.as_widget(self._field.widget)
        if not isinstance(value, basestring):
            # Some Widget render() methods -- notably RadioSelect -- return a
            # "special" object rather than a string. Call the __str__() on that
            # object to get its rendered value.
            value = value.__str__()
        return value

    def _errors(self):
        """
        Returns an ErrorList for this field. Returns an empty ErrorList
        if there are none.
        """
        try:
            return self._form.errors[self._name]
        except KeyError:
            return ErrorList()
    errors = property(_errors)

    def as_widget(self, widget, attrs=None):
        attrs = attrs or {}
        auto_id = self.auto_id
        if not attrs.has_key('id') and not widget.attrs.has_key('id') and auto_id:
            attrs['id'] = auto_id
        return widget.render(self._name, self._form.data.get(self._name, None), attrs=attrs)

    def as_text(self, attrs=None):
        """
        Returns a string of HTML for representing this as an <input type="text">.
        """
        return self.as_widget(TextInput(), attrs)

    def as_textarea(self, attrs=None):
        "Returns a string of HTML for representing this as a <textarea>."
        return self.as_widget(Textarea(), attrs)

    def _auto_id(self):
        """
        Calculates and returns the ID attribute for this BoundField, if the
        associated Form has specified auto_id. Returns an empty string otherwise.
        """
        auto_id = self._form.auto_id
        if auto_id and '%s' in str(auto_id):
            return str(auto_id) % self._name
        elif auto_id:
            return self._name
        return ''
    auto_id = property(_auto_id)
<commit_msg>newforms: Added BoundField.label, which calculates the label of the field based on its name<commit_after>"""
Form classes
"""

from django.utils.datastructures import SortedDict
from fields import Field
from widgets import TextInput, Textarea
from util import ErrorDict, ErrorList, ValidationError

NON_FIELD_ERRORS = '__all__'

def pretty_name(name):
    "Converts 'first_name' to 'First name'"
    name = name[0].upper() + name[1:]
    return name.replace('_', ' ')

class SortedDictFromList(SortedDict):
    "A dictionary that keeps its keys in the order in which they're inserted."
    # This is different than django.utils.datastructures.SortedDict, because
    # this takes a list/tuple as the argument to __init__().
    def __init__(self, data=None):
        if data is None: data = []
        self.keyOrder = [d[0] for d in data]
        dict.__init__(self, dict(data))

class DeclarativeFieldsMetaclass(type):
    "Metaclass that converts Field attributes to a dictionary called 'fields'."
    def __new__(cls, name, bases, attrs):
        fields = [(name, attrs.pop(name)) for name, obj in attrs.items() if isinstance(obj, Field)]
        fields.sort(lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter))
        attrs['fields'] = SortedDictFromList(fields)
        return type.__new__(cls, name, bases, attrs)

class Form(object):
    "A collection of Fields, plus their associated data."
    __metaclass__ = DeclarativeFieldsMetaclass

    def __init__(self, data=None, auto_id=False): # TODO: prefix stuff
        self.ignore_errors = data is None
        self.data = data or {}
        self.auto_id = auto_id
        self.clean_data = None # Stores the data after clean() has been called.
        self.__errors = None # Stores the errors after clean() has been called.

    def __str__(self):
        return self.as_table()

    def __iter__(self):
        for name, field in self.fields.items():
            yield BoundField(self, field, name)

    def __getitem__(self, name):
        "Returns a BoundField with the given name."
        try:
            field = self.fields[name]
        except KeyError:
            raise KeyError('Key %r not found in Form' % name)
        return BoundField(self, field, name)

    def _errors(self):
        "Returns an ErrorDict for self.data"
        if self.__errors is None:
            self.full_clean()
        return self.__errors
    errors = property(_errors)

    def is_valid(self):
        """
        Returns True if the form has no errors. Otherwise, False. If errors are
        being ignored, returns False.
        """
        return not self.ignore_errors and not bool(self.errors)

    def as_table(self):
        "Returns this form rendered as HTML
s -- excluding the

."
        output = []
        if self.errors.get(NON_FIELD_ERRORS):
            # Errors not corresponding to a particular field are displayed at the top.
            output.append(u'

%s

' % u'\n'.join([u'
%s
' % e for e in self.errors[NON_FIELD_ERRORS]]))
        for name, field in self.fields.items():
            bf = BoundField(self, field, name)
            if bf.errors:
                output.append(u'

%s

' % u'\n'.join([u'
%s
' % e for e in bf.errors]))
            output.append(u'
%s:	%s
' % (bf.label, bf))
        return u'\n'.join(output)

    def as_ul(self):
        "Returns this form rendered as HTML
s -- excluding the

."
        output = []
        if self.errors.get(NON_FIELD_ERRORS):
            # Errors not corresponding to a particular field are displayed at the top.
            output.append(u'

%s

' % u'\n'.join([u'
%s
' % e for e in self.errors[NON_FIELD_ERRORS]]))
        for name, field in self.fields.items():
            bf = BoundField(self, field, name)
            line = u'
'
            if bf.errors:
                line += u'
%s
' % u'\n'.join([u'
%s
' % e for e in bf.errors])
            line += u'%s: %s
' % (bf.label, bf)
            output.append(line)
        return u'\n'.join(output)

    def full_clean(self):
        """
        Cleans all of self.data and populates self.__errors and self.clean_data.
        """
        self.clean_data = {}
        errors = ErrorDict()
        if self.ignore_errors: # Stop further processing.
            self.__errors = errors
            return
        for name, field in self.fields.items():
            value = self.data.get(name, None)
            try:
                value = field.clean(value)
                self.clean_data[name] = value
                if hasattr(self, 'clean_%s' % name):
                    value = getattr(self, 'clean_%s' % name)()
                self.clean_data[name] = value
            except ValidationError, e:
                errors[name] = e.messages
        try:
            self.clean_data = self.clean()
        except ValidationError, e:
            errors[NON_FIELD_ERRORS] = e.messages
        if errors:
            self.clean_data = None
        self.__errors = errors

    def clean(self):
        """
        Hook for doing any extra form-wide cleaning after Field.clean() been
        called on every field.
        """
        return self.clean_data

class BoundField(object):
    "A Field plus data"
    def __init__(self, form, field, name):
        self._form = form
        self._field = field
        self._name = name

    def __str__(self):
        "Renders this field as an HTML widget."
        # Use the 'widget' attribute on the field to determine which type
        # of HTML widget to use.
        value = self.as_widget(self._field.widget)
        if not isinstance(value, basestring):
            # Some Widget render() methods -- notably RadioSelect -- return a
            # "special" object rather than a string. Call the __str__() on that
            # object to get its rendered value.
            value = value.__str__()
        return value

    def _errors(self):
        """
        Returns an ErrorList for this field. Returns an empty ErrorList
        if there are none.
        """
        try:
            return self._form.errors[self._name]
        except KeyError:
            return ErrorList()
    errors = property(_errors)

    def as_widget(self, widget, attrs=None):
        attrs = attrs or {}
        auto_id = self.auto_id
        if not attrs.has_key('id') and not widget.attrs.has_key('id') and auto_id:
            attrs['id'] = auto_id
        return widget.render(self._name, self._form.data.get(self._name, None), attrs=attrs)

    def as_text(self, attrs=None):
        """
        Returns a string of HTML for representing this as an <input type="text">.
        """
        return self.as_widget(TextInput(), attrs)

    def as_textarea(self, attrs=None):
        "Returns a string of HTML for representing this as a <textarea>."
        return self.as_widget(Textarea(), attrs)

    def _label(self):
        return pretty_name(self._name)
    label = property(_label)

    def _auto_id(self):
        """
        Calculates and returns the ID attribute for this BoundField, if the
        associated Form has specified auto_id. Returns an empty string otherwise.
        """
        auto_id = self._form.auto_id
        if auto_id and '%s' in str(auto_id):
            return str(auto_id) % self._name
        elif auto_id:
            return self._name
        return ''
    auto_id = property(_auto_id)
<|endoftext|>


<commit_before>import types
import urllib
import datetime

from django.utils.functional import Promise
from django.utils.safestring import SafeData, mark_safe

class DjangoUnicodeDecodeError(UnicodeDecodeError):
    def __init__(self, obj, *args):
        self.obj = obj
        UnicodeDecodeError.__init__(self, *args)

    def __str__(self):
        original = UnicodeDecodeError.__str__(self)
        return '%s. You passed in %r (%s)' % (original, self.obj,
                type(self.obj))

class StrAndUnicode(object):
    """
    A class whose __str__ returns its __unicode__ as a UTF-8 bytestring.

    Useful as a mix-in.
    """
    def __str__(self):
        return self.__unicode__().encode('utf-8')

def smart_unicode(s, encoding='utf-8', strings_only=False, errors='strict'):
    """
    Returns a unicode object representing 's'. Treats bytestrings using the
    'encoding' codec.

    If strings_only is True, don't convert (some) non-string-like objects.
    """
    if isinstance(s, Promise):
        # The input is the result of a gettext_lazy() call.
        return s
    return force_unicode(s, encoding, strings_only, errors)

def force_unicode(s, encoding='utf-8', strings_only=False, errors='strict'):
    """
    Similar to smart_unicode, except that lazy instances are resolved to
    strings, rather than kept as lazy objects.

    If strings_only is True, don't convert (some) non-string-like objects.
    """
    if strings_only and isinstance(s, (types.NoneType, int, long, datetime.datetime, datetime.date, datetime.time, float)):
        return s
    try:
        if not isinstance(s, basestring,):
            if hasattr(s, '__unicode__'):
                s = unicode(s)
            else:
                s = unicode(str(s), encoding, errors)
        elif not isinstance(s, unicode):
            # Note: We use .decode() here, instead of unicode(s, encoding,
            # errors), so that if s is a SafeString, it ends up being a
            # SafeUnicode at the end.
            s = s.decode(encoding, errors)
    except UnicodeDecodeError, e:
        raise DjangoUnicodeDecodeError(s, *e.args)
    return s

def smart_str(s, encoding='utf-8', strings_only=False, errors='strict'):
    """
    Returns a bytestring version of 's', encoded as specified in 'encoding'.

    If strings_only is True, don't convert (some) non-string-like objects.
    """
    if strings_only and isinstance(s, (types.NoneType, int)):
        return s
    if isinstance(s, Promise):
        return unicode(s).encode(encoding, errors)
    elif not isinstance(s, basestring):
        try:
            return str(s)
        except UnicodeEncodeError:
            return unicode(s).encode(encoding, errors)
    elif isinstance(s, unicode):
        return s.encode(encoding, errors)
    elif s and encoding != 'utf-8':
        return s.decode('utf-8', errors).encode(encoding, errors)
    else:
        return s

def iri_to_uri(iri):
    """
    Convert an Internationalized Resource Identifier (IRI) portion to a URI
    portion that is suitable for inclusion in a URL.

    This is the algorithm from section 3.1 of RFC 3987.  However, since we are
    assuming input is either UTF-8 or unicode already, we can simplify things a
    little from the full method.

    Returns an ASCII string containing the encoded result.
    """
    # The list of safe characters here is constructed from the printable ASCII
    # characters that are not explicitly excluded by the list at the end of
    # section 3.1 of RFC 3987.
    if iri is None:
        return iri
    return urllib.quote(smart_str(iri), safe='/#%[]=:;$&()+,!?')

<commit_msg>Fixed #6301 -- Added '*' to the characters not converted by iri_to_uri(), as pointed out by samidh.<commit_after>import types
import urllib
import datetime

from django.utils.functional import Promise
from django.utils.safestring import SafeData, mark_safe

class DjangoUnicodeDecodeError(UnicodeDecodeError):
    def __init__(self, obj, *args):
        self.obj = obj
        UnicodeDecodeError.__init__(self, *args)

    def __str__(self):
        original = UnicodeDecodeError.__str__(self)
        return '%s. You passed in %r (%s)' % (original, self.obj,
                type(self.obj))

class StrAndUnicode(object):
    """
    A class whose __str__ returns its __unicode__ as a UTF-8 bytestring.

    Useful as a mix-in.
    """
    def __str__(self):
        return self.__unicode__().encode('utf-8')

def smart_unicode(s, encoding='utf-8', strings_only=False, errors='strict'):
    """
    Returns a unicode object representing 's'. Treats bytestrings using the
    'encoding' codec.

    If strings_only is True, don't convert (some) non-string-like objects.
    """
    if isinstance(s, Promise):
        # The input is the result of a gettext_lazy() call.
        return s
    return force_unicode(s, encoding, strings_only, errors)

def force_unicode(s, encoding='utf-8', strings_only=False, errors='strict'):
    """
    Similar to smart_unicode, except that lazy instances are resolved to
    strings, rather than kept as lazy objects.

    If strings_only is True, don't convert (some) non-string-like objects.
    """
    if strings_only and isinstance(s, (types.NoneType, int, long, datetime.datetime, datetime.date, datetime.time, float)):
        return s
    try:
        if not isinstance(s, basestring,):
            if hasattr(s, '__unicode__'):
                s = unicode(s)
            else:
                s = unicode(str(s), encoding, errors)
        elif not isinstance(s, unicode):
            # Note: We use .decode() here, instead of unicode(s, encoding,
            # errors), so that if s is a SafeString, it ends up being a
            # SafeUnicode at the end.
            s = s.decode(encoding, errors)
    except UnicodeDecodeError, e:
        raise DjangoUnicodeDecodeError(s, *e.args)
    return s

def smart_str(s, encoding='utf-8', strings_only=False, errors='strict'):
    """
    Returns a bytestring version of 's', encoded as specified in 'encoding'.

    If strings_only is True, don't convert (some) non-string-like objects.
    """
    if strings_only and isinstance(s, (types.NoneType, int)):
        return s
    if isinstance(s, Promise):
        return unicode(s).encode(encoding, errors)
    elif not isinstance(s, basestring):
        try:
            return str(s)
        except UnicodeEncodeError:
            return unicode(s).encode(encoding, errors)
    elif isinstance(s, unicode):
        return s.encode(encoding, errors)
    elif s and encoding != 'utf-8':
        return s.decode('utf-8', errors).encode(encoding, errors)
    else:
        return s

def iri_to_uri(iri):
    """
    Convert an Internationalized Resource Identifier (IRI) portion to a URI
    portion that is suitable for inclusion in a URL.

    This is the algorithm from section 3.1 of RFC 3987.  However, since we are
    assuming input is either UTF-8 or unicode already, we can simplify things a
    little from the full method.

    Returns an ASCII string containing the encoded result.
    """
    # The list of safe characters here is constructed from the printable ASCII
    # characters that are not explicitly excluded by the list at the end of
    # section 3.1 of RFC 3987.
    if iri is None:
        return iri
    return urllib.quote(smart_str(iri), safe='/#%[]=:;$&()+,!?*')

<|endoftext|>


<commit_before>from django.core import template_loader
from django.core.exceptions import Http404, ObjectDoesNotExist
from django.core.extensions import DjangoContext as Context
from django.models.core import sites
from django.utils import httpwrappers

def shortcut(request, content_type_id, object_id):
    from django.models.core import contenttypes
    try:
        content_type = contenttypes.get_object(id__exact=content_type_id)
        obj = content_type.get_object_for_this_type(id__exact=object_id)
    except ObjectDoesNotExist:
        raise Http404, "Content type %s object %s doesn't exist" % (content_type_id, object_id)
    if not hasattr(obj, 'get_absolute_url'):
        raise Http404, "%s objects don't have get_absolute_url() methods" % content_type.name
    object_domain = None
    if hasattr(obj, 'get_site_list'):
        site_list = obj.get_site_list()
        if site_list:
            object_domain = site_list[0].domain
    elif hasattr(obj, 'get_site'):
        try:
            object_domain = obj.get_site().domain
        except sites.SiteDoesNotExist:
            pass
    try:
        object_domain = sites.get_current().domain
    except sites.SiteDoesNotExist:
        pass
    if not object_domain:
        return httpwrappers.HttpResponseRedirect(obj.get_absolute_url())
    return httpwrappers.HttpResponseRedirect('http://%s%s' % (object_domain, obj.get_absolute_url()))

def page_not_found(request):
    """
    Default 404 handler, which looks for the requested URL in the redirects
    table, redirects if found, and displays 404 page if not redirected.

    Templates: `404`
    Context: None
    """
    from django.models.core import redirects
    from django.conf.settings import APPEND_SLASH, SITE_ID
    path = request.get_full_path()
    try:
        r = redirects.get_object(site_id__exact=SITE_ID, old_path__exact=path)
    except redirects.RedirectDoesNotExist:
        r = None
    if r is None and APPEND_SLASH:
        # Try removing the trailing slash.
        try:
            r = redirects.get_object(site_id__exact=SITE_ID, old_path__exact=path[:path.rfind('/')]+path[path.rfind('/')+1:])
        except redirects.RedirectDoesNotExist:
            pass
    if r is not None:
        if r == '':
            return httpwrappers.HttpResponseGone()
        return httpwrappers.HttpResponseRedirect(r.new_path)
    t = template_loader.get_template('404')
    c = Context(request)
    return httpwrappers.HttpResponseNotFound(t.render(c))

def server_error(request):
    """
    500 Error handler

    Templates: `500`
    Context: None
    """
    t = template_loader.get_template('500')
    c = Context(request)
    return httpwrappers.HttpResponseServerError(t.render(c))
<commit_msg>Changed django.views.defaults to use template.Context instead of core.extensions.DjangoContext<commit_after>from django.core import template_loader
from django.core.exceptions import Http404, ObjectDoesNotExist
from django.core.template import Context
from django.models.core import sites
from django.utils import httpwrappers

def shortcut(request, content_type_id, object_id):
    from django.models.core import contenttypes
    try:
        content_type = contenttypes.get_object(id__exact=content_type_id)
        obj = content_type.get_object_for_this_type(id__exact=object_id)
    except ObjectDoesNotExist:
        raise Http404, "Content type %s object %s doesn't exist" % (content_type_id, object_id)
    if not hasattr(obj, 'get_absolute_url'):
        raise Http404, "%s objects don't have get_absolute_url() methods" % content_type.name
    object_domain = None
    if hasattr(obj, 'get_site_list'):
        site_list = obj.get_site_list()
        if site_list:
            object_domain = site_list[0].domain
    elif hasattr(obj, 'get_site'):
        try:
            object_domain = obj.get_site().domain
        except sites.SiteDoesNotExist:
            pass
    try:
        object_domain = sites.get_current().domain
    except sites.SiteDoesNotExist:
        pass
    if not object_domain:
        return httpwrappers.HttpResponseRedirect(obj.get_absolute_url())
    return httpwrappers.HttpResponseRedirect('http://%s%s' % (object_domain, obj.get_absolute_url()))

def page_not_found(request):
    """
    Default 404 handler, which looks for the requested URL in the redirects
    table, redirects if found, and displays 404 page if not redirected.

    Templates: `404`
    Context: None
    """
    from django.models.core import redirects
    from django.conf.settings import APPEND_SLASH, SITE_ID
    path = request.get_full_path()
    try:
        r = redirects.get_object(site_id__exact=SITE_ID, old_path__exact=path)
    except redirects.RedirectDoesNotExist:
        r = None
    if r is None and APPEND_SLASH:
        # Try removing the trailing slash.
        try:
            r = redirects.get_object(site_id__exact=SITE_ID, old_path__exact=path[:path.rfind('/')]+path[path.rfind('/')+1:])
        except redirects.RedirectDoesNotExist:
            pass
    if r is not None:
        if r == '':
            return httpwrappers.HttpResponseGone()
        return httpwrappers.HttpResponseRedirect(r.new_path)
    t = template_loader.get_template('404')
    c = Context()
    return httpwrappers.HttpResponseNotFound(t.render(c))

def server_error(request):
    """
    500 Error handler

    Templates: `500`
    Context: None
    """
    t = template_loader.get_template('500')
    c = Context()
    return httpwrappers.HttpResponseServerError(t.render(c))
<|endoftext|>


<commit_before>"""CAS authentication middleware"""

from urllib import urlencode

from django.http import HttpResponseRedirect, HttpResponseForbidden
from django.conf import settings
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.contrib.auth.views import login, logout
from django.core.urlresolvers import reverse
from urlparse import urlparse, parse_qs
import re

from django_cas.views import login as cas_login, logout as cas_logout

__all__ = ['CASMiddleware']

class CASMiddleware(object):
    """Middleware that allows CAS authentication on admin pages"""

    def process_request(self, request):
        """Checks that the authentication middleware is installed"""

        error = ("The Django CAS middleware requires authentication "
                 "middleware to be installed. Edit your MIDDLEWARE_CLASSES "
                 "setting to insert 'django.contrib.auth.middleware."
                 "AuthenticationMiddleware'.")
        assert hasattr(request, 'user'), error

    def process_view(self, request, view_func, view_args, view_kwargs):
        """Forwards unauthenticated requests to the admin page to the CAS
        login URL, as well as calls to django.contrib.auth.views.login and
        logout.
        """
        if view_func == login:
            # how to get the next query of the URL?
            url = request.get_full_path()
            qs = parse_qs(urlparse(url).query)
            # print qs
            # print "appended QUERY string:", request.GET.urlencode()
            if REDIRECT_FIELD_NAME in qs:
                # print qs
                redirect = qs[REDIRECT_FIELD_NAME][0]
                res = re.match(r"^/loginredirect/(?P<identity>\w+)/", redirect)
                if res is not None and res.group('identity') == "student":
                    return cas_login(request)

            return login(request, *view_args, **view_kwargs)

        elif view_func == logout:
            return cas_logout(request, *view_args, **view_kwargs)

        if settings.CAS_ADMIN_PREFIX:
            if not request.path.startswith(settings.CAS_ADMIN_PREFIX):
                return None
        elif not view_func.__module__.startswith('django.contrib.admin.'):
            return None

        if request.user.is_authenticated():
            if request.user.is_staff:
                return None
            else:
                error = ('
Forbidden

You do not have staff '
                         'privileges.
')
                return HttpResponseForbidden(error)

        params = urlencode({REDIRECT_FIELD_NAME: request.get_full_path()})
        return HttpResponseRedirect(reverse(cas_login) + '?' + params)
<commit_msg>取消 CAS<commit_after>"""CAS authentication middleware"""

from urllib import urlencode

from django.http import HttpResponseRedirect, HttpResponseForbidden
from django.conf import settings
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.contrib.auth.views import login, logout
from django.core.urlresolvers import reverse
from urlparse import urlparse, parse_qs
import re

from django_cas.views import login as cas_login, logout as cas_logout

__all__ = ['CASMiddleware']

class CASMiddleware(object):
    """Middleware that allows CAS authentication on admin pages"""

    def process_request(self, request):
        """Checks that the authentication middleware is installed"""

        error = ("The Django CAS middleware requires authentication "
                 "middleware to be installed. Edit your MIDDLEWARE_CLASSES "
                 "setting to insert 'django.contrib.auth.middleware."
                 "AuthenticationMiddleware'.")
        assert hasattr(request, 'user'), error

    def process_view(self, request, view_func, view_args, view_kwargs):
        """Forwards unauthenticated requests to the admin page to the CAS
        login URL, as well as calls to django.contrib.auth.views.login and
        logout.
        """
        if view_func == login:
            # # how to get the next query of the URL?
            # url = request.get_full_path()
            # qs = parse_qs(urlparse(url).query)
            # # print qs
            # # print "appended QUERY string:", request.GET.urlencode()
            # if REDIRECT_FIELD_NAME in qs:
            #     # print qs
            #     redirect = qs[REDIRECT_FIELD_NAME][0]
            #     res = re.match(r"^/loginredirect/(?P<identity>\w+)/", redirect)
            #     if res is not None and res.group('identity') == "student":
            #         return cas_login(request)

            return login(request, *view_args, **view_kwargs)

        elif view_func == logout:
            return cas_logout(request, *view_args, **view_kwargs)

        if settings.CAS_ADMIN_PREFIX:
            if not request.path.startswith(settings.CAS_ADMIN_PREFIX):
                return None
        elif not view_func.__module__.startswith('django.contrib.admin.'):
            return None

        if request.user.is_authenticated():
            if request.user.is_staff:
                return None
            else:
                error = ('
Forbidden

You do not have staff '
                         'privileges.
')
                return HttpResponseForbidden(error)

        params = urlencode({REDIRECT_FIELD_NAME: request.get_full_path()})
        return HttpResponseRedirect(reverse(cas_login) + '?' + params)
<|endoftext|>


<commit_before>from __future__ import absolute_import

import logging
from enum import Enum as NativeEnum, IntEnum as NativeIntEnum

from django.utils import six
from django.utils.decorators import classproperty

from django_enumfield.db.fields import EnumField

logger = logging.getLogger(__name__)
RAISE = object()

class BlankEnum(NativeEnum):
    BLANK = ""

    @property
    def label(self):
        return ""

class ClassmethodShortcut(object):
    def __init__(self, property_name, class_method):
        self.property_name = property_name
        self.class_method = class_method

    def __get__(self, instance, owner):
        if instance is None:
            # class
            return getattr(owner, self.class_method)
        return getattr(instance, self.property_name)

class Enum(NativeIntEnum):
    """ A container for holding and restoring enum values """

    __labels__ = {}
    __default__ = None
    __transitions__ = {}

    # Hack to make both Enum.VALUE.name and Enum.name(Enum.VALUE) available
    name = ClassmethodShortcut("_name_", "get_name")
    label = ClassmethodShortcut("_label", "get_label")

    @classproperty
    def do_not_call_in_templates(self):
        # Fix for Django templates so that any lookups of enums won't fail
        # More info: https://stackoverflow.com/questions/35953132/how-to-access-enum-types-in-django-templates
        return True

    @classproperty
    def values(cls):
        return {member.value: member for member in cls}

    @property
    def _label(self):
        """ Get human readable label for the matching Enum.Value.
        :return: label for value
        :rtype: str
        """
        label = self.__class__.__labels__.get(self.value, self.name)
        return six.text_type(label)

    def __hash__(self):
        path, (val,), _ = self.deconstruct()
        return hash("{}.{}".format(path, val))

    def deconstruct(self):
        """
        See "Adding a deconstruct() method" in
        https://docs.djangoproject.com/en/1.8/topics/migrations/
        """
        c = self.__class__
        path = "{}.{}".format(c.__module__, c.__name__)
        return path, [self.value], {}

    @classmethod
    def items(cls):
        """
        :return: List of tuples consisting of every enum value in the form
            [('NAME', value), ...]
        :rtype: list
        """
        items = [(member.name, member.value) for member in cls]
        return sorted(items, key=lambda x: x[1])

    @classmethod
    def choices(cls, blank=False):
        """ Choices for Enum
        :return: List of tuples (<value>, <member>)
        :rtype: list
        """
        choices = sorted([(member.value, member) for member in cls], key=lambda x: x[0])
        if blank:
            choices.insert(0, (BlankEnum.BLANK.value, BlankEnum.BLANK))
        return choices

    @classmethod
    def default(cls):
        """ Default Enum value. Set default value to `__default__` attribute
        of your enum class or override this method if you need another
        default value.
        Usage:
            IntegerField(choices=my_enum.choices(), default=my_enum.default(), ...
        :return Default value, which is the first one by default.
        :rtype: enum member
        """
        if cls.__default__ is not None:
            return cls(cls.__default__)

    @classmethod
    def field(cls, **kwargs):
        """ A shortcut for field declaration
        Usage:
            class MyModelStatuses(Enum):
                UNKNOWN = 0

            class MyModel(Model):
                status = MyModelStatuses.field()

        :param kwargs: Arguments passed in EnumField.__init__()
        :rtype: EnumField
        """
        return EnumField(cls, **kwargs)

    @classmethod
    def get(cls, name_or_numeric, default=None):
        """ Get Enum.Value object matching the value argument.
        :param name_or_numeric: Integer value or attribute name
        :type name_or_numeric: int or str
        :param default: The default to return if the value passed is not
            a valid enum value
        :type default: Any
        :rtype: Enum.Value
        """
        if isinstance(name_or_numeric, cls):
            return name_or_numeric

        if isinstance(name_or_numeric, int):
            try:
                return cls(name_or_numeric)
            except ValueError:
                pass
        elif isinstance(name_or_numeric, six.string_types):
            try:
                return cls[name_or_numeric]
            except KeyError:
                pass

        return default

    @classmethod
    def get_name(cls, name_or_numeric):
        """ Get Enum.Value name matching the value argument.
        :param name_or_numeric: Integer value or attribute name
        :type name_or_numeric: int or str
        :return: The name or None if not found
        """
        value = cls.get(name_or_numeric)
        if value is not None:
            return value.name

    @classmethod
    def get_label(cls, name_or_numeric):
        """ Get Enum.Value label matching the value argument.
        :param name_or_numeric: Integer value or attribute name
        :type name_or_numeric: int or str
        :return: The label or None if not found
        """
        value = cls.get(name_or_numeric)
        if value is not None:
            return value._label

    @classmethod
    def is_valid_transition(cls, from_value, to_value):
        """ Will check if to_value is a valid transition from from_value.
        Returns true if it is a valid transition.

        :param from_value: Start transition point
        :param to_value: End transition point
        :type from_value: int
        :type to_value: int
        :return: Success flag
        :rtype: bool
        """
        if isinstance(from_value, cls):
            from_value = from_value.value
        if isinstance(to_value, cls):
            to_value = to_value.value

        return (
            from_value == to_value
            or not cls.__transitions__
            or (from_value in cls.transition_origins(to_value))
        )

    @classmethod
    def transition_origins(cls, to_value):
        """ Returns all values the to_value can make a transition from.
        :param to_value End transition point
        :type to_value: int
        :rtype: list
        """
        if isinstance(to_value, cls):
            to_value = to_value.value
        return cls.__transitions__.get(to_value, [])
<commit_msg>Remove Enum.__hash__ function<commit_after>from __future__ import absolute_import

import logging
from enum import Enum as NativeEnum, IntEnum as NativeIntEnum

from django.utils import six
from django.utils.decorators import classproperty

from django_enumfield.db.fields import EnumField

logger = logging.getLogger(__name__)
RAISE = object()

class BlankEnum(NativeEnum):
    BLANK = ""

    @property
    def label(self):
        return ""

class ClassmethodShortcut(object):
    def __init__(self, property_name, class_method):
        self.property_name = property_name
        self.class_method = class_method

    def __get__(self, instance, owner):
        if instance is None:
            # class
            return getattr(owner, self.class_method)
        return getattr(instance, self.property_name)

class Enum(NativeIntEnum):
    """ A container for holding and restoring enum values """

    __labels__ = {}
    __default__ = None
    __transitions__ = {}

    # Hack to make both Enum.VALUE.name and Enum.name(Enum.VALUE) available
    name = ClassmethodShortcut("_name_", "get_name")
    label = ClassmethodShortcut("_label", "get_label")

    @classproperty
    def do_not_call_in_templates(self):
        # Fix for Django templates so that any lookups of enums won't fail
        # More info: https://stackoverflow.com/questions/35953132/how-to-access-enum-types-in-django-templates
        return True

    @classproperty
    def values(cls):
        return {member.value: member for member in cls}

    @property
    def _label(self):
        """ Get human readable label for the matching Enum.Value.
        :return: label for value
        :rtype: str
        """
        label = self.__class__.__labels__.get(self.value, self.name)
        return six.text_type(label)

    def deconstruct(self):
        """
        See "Adding a deconstruct() method" in
        https://docs.djangoproject.com/en/1.8/topics/migrations/
        """
        c = self.__class__
        path = "{}.{}".format(c.__module__, c.__name__)
        return path, [self.value], {}

    @classmethod
    def items(cls):
        """
        :return: List of tuples consisting of every enum value in the form
            [('NAME', value), ...]
        :rtype: list
        """
        items = [(member.name, member.value) for member in cls]
        return sorted(items, key=lambda x: x[1])

    @classmethod
    def choices(cls, blank=False):
        """ Choices for Enum
        :return: List of tuples (<value>, <member>)
        :rtype: list
        """
        choices = sorted([(member.value, member) for member in cls], key=lambda x: x[0])
        if blank:
            choices.insert(0, (BlankEnum.BLANK.value, BlankEnum.BLANK))
        return choices

    @classmethod
    def default(cls):
        """ Default Enum value. Set default value to `__default__` attribute
        of your enum class or override this method if you need another
        default value.
        Usage:
            IntegerField(choices=my_enum.choices(), default=my_enum.default(), ...
        :return Default value, which is the first one by default.
        :rtype: enum member
        """
        if cls.__default__ is not None:
            return cls(cls.__default__)

    @classmethod
    def field(cls, **kwargs):
        """ A shortcut for field declaration
        Usage:
            class MyModelStatuses(Enum):
                UNKNOWN = 0

            class MyModel(Model):
                status = MyModelStatuses.field()

        :param kwargs: Arguments passed in EnumField.__init__()
        :rtype: EnumField
        """
        return EnumField(cls, **kwargs)

    @classmethod
    def get(cls, name_or_numeric, default=None):
        """ Get Enum.Value object matching the value argument.
        :param name_or_numeric: Integer value or attribute name
        :type name_or_numeric: int or str
        :param default: The default to return if the value passed is not
            a valid enum value
        :type default: Any
        :rtype: Enum.Value
        """
        if isinstance(name_or_numeric, cls):
            return name_or_numeric

        if isinstance(name_or_numeric, int):
            try:
                return cls(name_or_numeric)
            except ValueError:
                pass
        elif isinstance(name_or_numeric, six.string_types):
            try:
                return cls[name_or_numeric]
            except KeyError:
                pass

        return default

    @classmethod
    def get_name(cls, name_or_numeric):
        """ Get Enum.Value name matching the value argument.
        :param name_or_numeric: Integer value or attribute name
        :type name_or_numeric: int or str
        :return: The name or None if not found
        """
        value = cls.get(name_or_numeric)
        if value is not None:
            return value.name

    @classmethod
    def get_label(cls, name_or_numeric):
        """ Get Enum.Value label matching the value argument.
        :param name_or_numeric: Integer value or attribute name
        :type name_or_numeric: int or str
        :return: The label or None if not found
        """
        value = cls.get(name_or_numeric)
        if value is not None:
            return value._label

    @classmethod
    def is_valid_transition(cls, from_value, to_value):
        """ Will check if to_value is a valid transition from from_value.
        Returns true if it is a valid transition.

        :param from_value: Start transition point
        :param to_value: End transition point
        :type from_value: int
        :type to_value: int
        :return: Success flag
        :rtype: bool
        """
        if isinstance(from_value, cls):
            from_value = from_value.value
        if isinstance(to_value, cls):
            to_value = to_value.value

        return (
            from_value == to_value
            or not cls.__transitions__
            or (from_value in cls.transition_origins(to_value))
        )

    @classmethod
    def transition_origins(cls, to_value):
        """ Returns all values the to_value can make a transition from.
        :param to_value End transition point
        :type to_value: int
        :rtype: list
        """
        if isinstance(to_value, cls):
            to_value = to_value.value
        return cls.__transitions__.get(to_value, [])
<|endoftext|>


<commit_before>default_app_config = 'django_jinja.apps.DjangoJinjaAppConfig'
<commit_msg>Avoid Django 3.2+ deprecation warning about unnecessary default_app_configs<commit_after>from django import VERSION

if VERSION < (3, 2):
    default_app_config = 'django_jinja.apps.DjangoJinjaAppConfig'
<|endoftext|>