django.forms生成HTML,django.forms.formsets

[文档]@html_safe

class BaseFormSet:

"""

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, form_kwargs=None):

self.is_bound = data is not None or files is not None

self.prefix = prefix or self.get_default_prefix()

self.auto_id = auto_id

self.data = data or {}

self.files = files or {}

self.initial = initial

self.form_kwargs = form_kwargs or {}

self.error_class = error_class

self._errors = None

self._non_form_errors = None

def __str__(self):

return self.as_table()

def __iter__(self):

"""Yield the forms in the order they should be rendered."""

return iter(self.forms)

def __getitem__(self, index):

"""Return the form at the given index, based on the rendering order."""

return self.forms[index]

def __len__(self):

return len(self.forms)

def __bool__(self):

"""

Return True since all formsets have a management form which is not

included in the length.

"""

return True

@cached_property

def management_form(self):

"""Return the ManagementForm instance for this FormSet."""

if self.is_bound:

form = ManagementForm(self.data, auto_id=self.auto_id, prefix=self.prefix)

if not form.is_valid():

raise ValidationError(

_('ManagementForm data is missing or has been tampered with'),

code='missing_management_form',

)

else:

form = ManagementForm(auto_id=self.auto_id, prefix=self.prefix, initial={

TOTAL_FORM_COUNT: self.total_form_count(),

INITIAL_FORM_COUNT: self.initial_form_count(),

MIN_NUM_FORM_COUNT: self.min_num,

MAX_NUM_FORM_COUNT: self.max_num

})

return form

def total_form_count(self):

"""Return the total number of forms in this FormSet."""

if self.is_bound:

# return absolute_max if it is lower than the actual total form

# count in the data; this is DoS protection to prevent clients

# from forcing the server to instantiate arbitrary numbers of

# forms

return min(self.management_form.cleaned_data[TOTAL_FORM_COUNT], self.absolute_max)

else:

initial_forms = self.initial_form_count()

total_forms = max(initial_forms, self.min_num) + self.extra

# Allow all existing related objects/inlines to be displayed,

# but don't allow extra beyond max_num.

if initial_forms > self.max_num >= 0:

total_forms = initial_forms

elif total_forms > self.max_num >= 0:

total_forms = self.max_num

return total_forms

def initial_form_count(self):

"""Return the number of forms that are required in this FormSet."""

if self.is_bound:

return self.management_form.cleaned_data[INITIAL_FORM_COUNT]

else:

# Use the length of the initial data if it's there, 0 otherwise.

initial_forms = len(self.initial) if self.initial else 0

return initial_forms

@cached_property

def forms(self):

"""Instantiate forms at first property access."""

# DoS protection is included in total_form_count()

forms = [self._construct_form(i, **self.get_form_kwargs(i))

for i in range(self.total_form_count())]

return forms

def get_form_kwargs(self, index):

"""

Return additional keyword arguments for each individual formset form.

index will be None if the form being constructed is a new empty

form.

"""

return self.form_kwargs.copy()

def _construct_form(self, i, **kwargs):

"""Instantiate and return the i-th form instance in a formset."""

defaults = {

'auto_id': self.auto_id,

'prefix': self.add_prefix(i),

'error_class': self.error_class,

# Don't render the HTML 'required' attribute as it may cause

# incorrect validation for extra, optional, and deleted

# forms in the formset.

'use_required_attribute': False,

}

if self.is_bound:

defaults['data'] = self.data

defaults['files'] = self.files

if self.initial and 'initial' not in kwargs:

try:

defaults['initial'] = self.initial[i]

except IndexError:

pass

# Allow extra forms to be empty, unless they're part of

# the minimum forms.

if i >= self.initial_form_count() and i >= self.min_num:

defaults['empty_permitted'] = True

defaults.update(kwargs)

form = self.form(**defaults)

self.add_fields(form, i)

return form

@property

def initial_forms(self):

"""Return a list of all the initial forms in this formset."""

return self.forms[:self.initial_form_count()]

@property

def extra_forms(self):

"""Return a list of all the extra forms in this formset."""

return self.forms[self.initial_form_count():]

@property

def empty_form(self):

form = self.form(

auto_id=self.auto_id,

prefix=self.add_prefix('__prefix__'),

empty_permitted=True,

use_required_attribute=False,

**self.get_form_kwargs(None)

)

self.add_fields(form, None)

return form

@property

def cleaned_data(self):

"""

Return 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]

@property

def deleted_forms(self):

"""Return a list of forms that have been marked for deletion."""

if not self.is_valid() or not self.can_delete:

return []

# 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 self._should_delete_form(form):

self._deleted_form_indexes.append(i)

return [self.forms[i] for i in self._deleted_form_indexes]

@property

def ordered_forms(self):

"""

Return a list of form in the order specified by the incoming data.

Raise an AttributeError if ordering 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 self._should_delete_form(form):

continue

self._ordering.append((i, form.cleaned_data[ORDERING_FIELD_NAME]))

# After we're done populating self._ordering, sort it.

# 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

# blank.

def compare_ordering_key(k):

if k[1] is None:

return (1, 0) # +infinity, larger than any number

return (0, k[1])

self._ordering.sort(key=compare_ordering_key)

# Return a list of form.cleaned_data dicts in the order specified by

# the form data.

return [self.forms[i[0]] for i in self._ordering]

@classmethod

def get_default_prefix(cls):

return 'form'

def non_form_errors(self):

"""

Return an ErrorList of errors that aren't associated with a particular

form -- i.e., from formset.clean(). Return an empty ErrorList if there

are none.

"""

if self._non_form_errors is None:

self.full_clean()

return self._non_form_errors

@property

def errors(self):

"""Return a list of form.errors for every form in self.forms."""

if self._errors is None:

self.full_clean()

return self._errors

[文档] def total_error_count(self):

"""Return the number of errors across all forms in the formset."""

return len(self.non_form_errors()) +\

sum(len(form_errors) for form_errors in self.errors)

def _should_delete_form(self, form):

"""Return whether or not the form was marked for deletion."""

return form.cleaned_data.get(DELETION_FIELD_NAME, False)

def is_valid(self):

"""Return True if every form in self.forms is valid."""

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

# This triggers a full clean.

self.errors

for i in range(0, self.total_form_count()):

form = self.forms[i]

if self.can_delete and self._should_delete_form(form):

# This form is going to be deleted so any of its errors

# shouldn't cause the entire formset to be invalid.

continue

forms_valid &= form.is_valid()

return forms_valid and not self.non_form_errors()

def full_clean(self):

"""

Clean all of self.data and populate self._errors and

self._non_form_errors.

"""

self._errors = []

self._non_form_errors = self.error_class()

empty_forms_count = 0

if not self.is_bound: # Stop further processing.

return

for i in range(0, self.total_form_count()):

form = self.forms[i]

# Empty forms are unchanged forms beyond those with initial data.

if not form.has_changed() and i >= self.initial_form_count():

empty_forms_count += 1

# Accessing errors calls full_clean() if necessary.

# _should_delete_form() requires cleaned_data.

form_errors = form.errors

if self.can_delete and self._should_delete_form(form):

continue

self._errors.append(form_errors)

try:

if (self.validate_max and

self.total_form_count() - len(self.deleted_forms) > self.max_num) or \

self.management_form.cleaned_data[TOTAL_FORM_COUNT] > self.absolute_max:

raise ValidationError(ngettext(

"Please submit%dor fewer forms.",

"Please submit%dor fewer forms.", self.max_num) % self.max_num,

code='too_many_forms',

)

if (self.validate_min and

self.total_form_count() - len(self.deleted_forms) - empty_forms_count < self.min_num):

raise ValidationError(ngettext(

"Please submit%dor more forms.",

"Please submit%dor more forms.", self.min_num) % self.min_num,

code='too_few_forms')

# Give self.clean() a chance to do cross-form validation.

self.clean()

except ValidationError as e:

self._non_form_errors = self.error_class(e.error_list)

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 accessible

via formset.non_form_errors()

"""

pass

def has_changed(self):

"""Return True if data in any form differs from initial."""

return any(form.has_changed() for form in self)

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 is not None and index < self.initial_form_count():

form.fields[ORDERING_FIELD_NAME] = IntegerField(label=_('Order'), initial=index + 1, required=False)

else:

form.fields[ORDERING_FIELD_NAME] = IntegerField(label=_('Order'), required=False)

if self.can_delete:

form.fields[DELETION_FIELD_NAME] = BooleanField(label=_('Delete'), required=False)

def add_prefix(self, index):

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

def is_multipart(self):

"""

Return True if the formset needs to be multipart, i.e. it

has FileInput, or False otherwise.

"""

if self.forms:

return self.forms[0].is_multipart()

else:

return self.empty_form.is_multipart()

@property

def 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 self.empty_form.media

def as_table(self):

"Return 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 = ' '.join(form.as_table() for form in self)

return mark_safe(str(self.management_form) + '\n' + forms)

def as_p(self):

"Return this formset rendered as HTML

s."

forms = ' '.join(form.as_p() for form in self)

return mark_safe(str(self.management_form) + '\n' + forms)

def as_ul(self):

"Return this formset rendered as HTML

s."

forms = ' '.join(form.as_ul() for form in self)

return mark_safe(str(self.management_form) + '\n' + forms)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值