Form与ModelForm中的插件使用
生活随笔
收集整理的這篇文章主要介紹了
Form与ModelForm中的插件使用
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一、Form插件的使用
(一)widget參數
from .models import *
from django import forms
from django.forms import widgets
class BookForm(forms.Form):
email=forms.EmailField()
title = forms.CharField(max_length=32,label="書籍名稱")
price = forms.DecimalField(max_digits=4, decimal_places=2,label="價格") # 34.91
pub_date = forms.DateField(label="日期",
widget=widgets.TextInput(attrs={"type":"date"}) #插件
)
book_type=forms.ChoiceField(choices=((1,"自然科學"),(2,"社會學科"),(3,"其他")))
publish=forms.ModelChoiceField(queryset=Publish.objects.all())
authors=forms.ModelMultipleChoiceField(queryset=Author.objects.all())
在date字段中使用了插件,參數是widget,可以從源碼角度來看看是為什么?
class DateField(BaseTemporalField):
widget = DateInput
input_formats = formats.get_format_lazy('DATE_INPUT_FORMATS')
default_error_messages = {
'invalid': _('Enter a valid date.'),
}
def to_python(self, value):
"""
Validate that the input can be converted to a date. Return a Python
datetime.date object.
"""
if value in self.empty_values:
return None
if isinstance(value, datetime.datetime):
return value.date()
if isinstance(value, datetime.date):
return value
return super().to_python(value)
def strptime(self, value, format):
return datetime.datetime.strptime(value, format).date()
DateField
DateField的基類是Field,在Field中接收插件參數為widget:
class Field:
widget = TextInput # Default widget to use when rendering this type
...
...
def __init__(self, *, required=True, widget=None, label=None, initial=None,
help_text='', error_messages=None, show_hidden_initial=False,
validators=(), localize=False, disabled=False, label_suffix=None):
self.required, self.label, self.initial = required, label, initial
self.show_hidden_initial = show_hidden_initial
self.help_text = help_text
self.disabled = disabled
self.label_suffix = label_suffix
widget = widget or self.widget
if isinstance(widget, type):
widget = widget()
else:
widget = copy.deepcopy(widget)
...
...
super().__init__()
顯然,參數中傳入的是widgets.TextInput,所以進入到widgets模塊下找TextInput類:
class TextInput(Input):
input_type = 'text'
template_name = 'django/forms/widgets/text.html'
其基類是Input:
class Input(Widget):
"""
Base class for all <input> widgets.
"""
input_type = None # Subclasses must define this.
template_name = 'django/forms/widgets/input.html'
def __init__(self, attrs=None):
if attrs is not None:
attrs = attrs.copy()
self.input_type = attrs.pop('type', self.input_type)
super().__init__(attrs)
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
context['widget']['type'] = self.input_type
return context
Input
基類中取出input_type,并且拷貝attrs屬性。
(二)觸發widget渲染
渲染text.html,其實就是渲染input.html
<input type="{{ widget.type }}" name="{{ widget.name }}"
{% if widget.value != None %}
value="{{ widget.value|stringformat:'s' }}"
{% endif %}
{% include "django/forms/widgets/attrs.html" %} />
attrs.html
{% for name, value in widget.attrs.items %}
{% if value is not False %}
{{ name }}
{% if value is not True %}="{{ value|stringformat:'s' }}"{% endif %}
{% endif %}
{% endfor %}
可是,在什么時候觸發渲染呢?
其實就是在渲染每一BoundField對象(BoundField類位于django.forms.boundfield.BoundField),所以可以看看BoundField類中的方法:
class BoundField:
"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)
self.html_initial_name = form.add_initial_prefix(name)
self.html_initial_id = form.add_initial_prefix(self.auto_id)
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 __str__(self):
"""Render this field as an HTML widget."""
if self.field.show_hidden_initial:
return self.as_widget() + self.as_hidden(only_initial=True)
return self.as_widget() #在這里就和widgets聯系上了
#調用widget中的render
def as_widget(self, widget=None, attrs=None, only_initial=False):
"""
Render the field by rendering the passed widget, adding any HTML
attributes passed as attrs. If a widget isn't specified, use the
field's default widget.
"""
if not widget:
widget = self.field.widget
if self.field.localize:
widget.is_localized = True
attrs = attrs or {}
attrs = self.build_widget_attrs(attrs, widget)
auto_id = self.auto_id
if auto_id and 'id' not in attrs and 'id' not in widget.attrs:
if not only_initial:
attrs['id'] = auto_id
else:
attrs['id'] = self.html_initial_id
if not only_initial:
name = self.html_name
else:
name = self.html_initial_name
kwargs = {}
if func_supports_parameter(widget.render, 'renderer') or func_accepts_kwargs(widget.render):
kwargs['renderer'] = self.form.renderer
else:
warnings.warn(
'Add the `renderer` argument to the render() method of %s. '
'It will be mandatory in Django 2.1.' % widget.__class__,
RemovedInDjango21Warning, stacklevel=2,
)
return widget.render(
name=name,
value=self.value(),
attrs=attrs,
**kwargs
)
調用WIdget中的render(Widget位于django.forms.widgets.Widget):
def render(self, name, value, attrs=None, renderer=None):
"""Render the widget as an HTML string."""
context = self.get_context(name, value, attrs)
return self._render(self.template_name, context, renderer)
def _render(self, template_name, context, renderer=None):
if renderer is None:
renderer = get_default_renderer()
return mark_safe(renderer.render(template_name, context))
從這里可以知道widget的name就是self.name,是forms字段名稱,value是self.initial。
def value(self):
"""
Return the value for this BoundField, using the initial value if
the form is not bound or the data otherwise.
"""
data = self.initial
if self.form.is_bound:
data = self.field.bound_data(self.data, data)
return self.field.prepare_value(data)
value
所以,pub_date標簽渲染為:
<input type="date" name="pub_date" >
二、ModelForm插件的使用
from django.forms import widgets as wid
class BookForm(ModelForm):
class Meta:
model=Book
fields="__all__"
labels={"title":"書籍名稱", "price":"價格"}
widgets={
"title":wid.TextInput(attrs={"class":"form-control"}),
"price":wid.TextInput(attrs={"class":"form-control"}),
"pub_date":wid.TextInput(attrs={"class":"form-control","type":"date"}),
"publish":wid.Select(attrs={"class":"form-control"}),
"authors":wid.SelectMultiple(attrs={"class":"form-control"}),
}
在ModelForm中使用widgets參數,看看源碼中參數的定義:
#自定義ModelForm中Meta中的可傳參數
class ModelFormOptions:
def __init__(self, options=None):
self.model = getattr(options, 'model', None)
self.fields = getattr(options, 'fields', None)
self.exclude = getattr(options, 'exclude', None)
self.widgets = getattr(options, 'widgets', None) #插件參數
self.localized_fields = getattr(options, 'localized_fields', None)
self.labels = getattr(options, 'labels', None)
self.help_texts = getattr(options, 'help_texts', None)
self.error_messages = getattr(options, 'error_messages', None)
self.field_classes = getattr(options, 'field_classes', None)
另外,在自定義ModelForm的元類中已經生成了對應fields,并且每個字段與widget完成了映射。
class ModelFormMetaclass(DeclarativeFieldsMetaclass):
def __new__(mcs, name, bases, attrs):
...
...
#得到字段
fields = fields_for_model(
opts.model, opts.fields, opts.exclude, opts.widgets,
formfield_callback, opts.localized_fields, opts.labels,
opts.help_texts, opts.error_messages, opts.field_classes,
# limit_choices_to will be applied during ModelForm.__init__().
apply_limit_choices_to=False,
)
...
...
new_class.base_fields = fields
return new_class
這樣剩下的可以參考Form中widget的流程了。
總結
以上是生活随笔為你收集整理的Form与ModelForm中的插件使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何给图片添加黑色边框
- 下一篇: 在web中清除Cookie