禁用默认行为 - ModelForm Select 小部件选择填充了引用的对象
Disable default behavior - ModelForm Select widget choices filled with referenced objects
我在 Django 中使用基于模型的表单。所以它看起来像这样:
240 class EventDetailForm(NgFormValidationMixin, NgModelForm):
241 def __init__(self, *args, **kwargs):tha
242 super(EventDetailForm, self).__init__(*args, **kwargs)
243 self.fields['gallery'].queryset = Gallery.objects.none()
244
245 class Meta:
246 model = Event
247 fields = ('title', 'description', 'end_date', 'start_date', 'gallery', 'cover_photo')
248 widgets = {
249 'title': forms.TextInput(attrs={
250 'editable-detail': '',
251 }),
252 'description': forms.TextInput(attrs={
253 'class': 'panel-body',
254 'id': 'event-description-editable',
255 'editable-detail': '',
256 }),
257 'cover_photo': SelectWithDefaultOptions(attrs={
258 'class': 'chosen-select-no-single',
259 'id': 'select-cover-photo',
260 'data-placeholder': 'Select Cover Photo',
261 'style': 'width: 200px;',
262 'tabindex': '-1',
263 }),
264 'start_date': DateTimeWidget(attrs = {
265 'class': 'datetimepicker col-xs-6',
266 'id': 'event-start-date-editable',
267 'editable-detail': '',
268 }),
269 'end_date': DateTimeWidget(attrs = {
270 'class': 'datetimepicker col-xs-6',
271 'id': 'event-end-date-editable',
272 'editable-detail': '',
273 }),
274 'gallery': SelectWithDefaultOptions(attrs={
275 'class': 'chosen-select-no-single',
276 'id': 'select-galley',
277 'data-placeholder': 'Select Gallery',
278 'style': 'width: 200px;',
279 'gallery-select': '',
280 'tabindex': '-1',
281 'organisator-profile-specific': '',
282 }
283 }
所以发生的是我的图库和 cover_photo select 小部件充满了这两种类型的所有现有对象(因为它们实际上是其他模型的外键)。
我想防止这种情况发生,正如您在第 243 行看到的那样,我尝试删除当前的查询集(尝试使用清理选项也产生相同的结果),效果很好。问题是,如您所见,我使用自定义 select 小部件,我在其中设置了一些默认选项。它看起来像这样:
62 class SelectWithDefaultOptions(forms.Select):
63 def __init__(self, attrs=None, choices=()):
64 super(SelectWithDefaultOptions, self).__init__(attrs, choices)
65
66 choices = ('', 'empty') + choices
67 choices = ('None', 'no selection') + choices
问题是我用上面提到的方法删除了那些值。
所以我对自己说 "Well, I will get the needed values, erase all and put the preferred back"。试过了,结果发现实际上 Django 放入的对象正在删除已经设置的对象。 (在 init 方法通过后添加默认的)
所以我想 "Well, if I set choices=() in the initialisation of the widget (line 274), Django should not set any other values on top of that, because this will violate my choices" 所以我试了一下,但事实证明 Django 实际上并不关心我希望有什么选择并且表现得一样。
也试过设置字段的'initial'属性,还是没有结果。
那么,如何防止 DJango 默认行为将引用的对象放入我的 select 的选择列表中?
谢谢。
Meta
中定义的 widget
不知何故导致了这个问题。如果您只是将其移动到 __init__
,它就会起作用。首先更新您的小部件,def __init__(self, attrs=None, choices=())
行中的关键字 choices
意味着默认情况下的选择是空的,但任何实例都可以通过传递一些值来覆盖它。所以你需要明确地设置它为空元组:
class SelectWithDefaultOptions(forms.Select):
def __init__(self, attrs=None, choices=()):
super(SelectWithDefaultOptions, self).__init__(attrs, choices)
choices = () # explicitly setting choices to empty here
choices += (('', 'empty'),)
choices += (('None', 'no selection'),)
self.choices = choices
现在更新您的表单,将小部件分配给 __init__
中的 gallery
字段,而不是 Meta
class:
class EventDetailForm(NgFormValidationMixin, NgModelForm):
def __init__(self, *args, **kwargs):
super(EventDetailForm, self).__init__(*args, **kwargs)
self.fields['gallery'].widget = SelectWithDefaultOptions(attrs={
'class': 'chosen-select-no-single',
'id': 'select-galley',
'data-placeholder': 'Select Gallery',
'style': 'width: 200px;',
'gallery-select': '',
'tabindex': '-1',
'organisator-profile-specific': '',
}
class Meta:
model = Event
fields = ('title', 'description', 'end_date', 'start_date', 'gallery', 'cover_photo')
或者您根本不需要任何自定义小部件。只需在表单 __init__
方法中设置选项,并在 Meta
中将 SelectWithDefaultOptions
小部件名称替换为 forms.Select
(这样更简洁):
class EventDetailForm(NgFormValidationMixin, NgModelForm):
def __init__(self, *args, **kwargs):
super(EventDetailForm, self).__init__(*args, **kwargs)
self.fields['gallery'].widget.choices = (('', 'Empty',),)
class Meta:
model = Event
fields = ('title', 'description', 'end_date', 'start_date', 'gallery', 'cover_photo')
widgets = {
'gallery': forms.Select(attrs={
'class': 'chosen-select-no-single',
'id': 'select-galley',
'data-placeholder': 'Select Gallery',
'style': 'width: 200px;',
'gallery-select': '',
'tabindex': '-1',
'organisator-profile-specific': '',
}),
}
在@AamirAdnan 的帮助下,我就是这样修复它的:
65 class SelectWithDefaultOptions(forms.Select):
66 def render(self, name, value, attrs=None, choices=()):
67 choices = ()
68 choices += (('empty', ''),)
69 choices += (('no selection', 'None'),)
70 self.choices = choices
71
72 if value is None:
73 value = ''
74
75 final_attrs = self.build_attrs(attrs, name=name)
76 output = [format_html('<select{0}>', flatatt(final_attrs))]
77 options = self.render_options((), [value])
78
79 if options:
80 output.append(options)
81 output.append('</select>')
82
83 return mark_safe('\n'.join(output))
刚刚改进了小部件渲染功能。
这与要求的不完全一样,但它确实有效。发生的事情是:
1. Django将choices值设置为引用对象
2.在widget渲染之前,将它们更改为正确的选择
我在 Django 中使用基于模型的表单。所以它看起来像这样:
240 class EventDetailForm(NgFormValidationMixin, NgModelForm):
241 def __init__(self, *args, **kwargs):tha
242 super(EventDetailForm, self).__init__(*args, **kwargs)
243 self.fields['gallery'].queryset = Gallery.objects.none()
244
245 class Meta:
246 model = Event
247 fields = ('title', 'description', 'end_date', 'start_date', 'gallery', 'cover_photo')
248 widgets = {
249 'title': forms.TextInput(attrs={
250 'editable-detail': '',
251 }),
252 'description': forms.TextInput(attrs={
253 'class': 'panel-body',
254 'id': 'event-description-editable',
255 'editable-detail': '',
256 }),
257 'cover_photo': SelectWithDefaultOptions(attrs={
258 'class': 'chosen-select-no-single',
259 'id': 'select-cover-photo',
260 'data-placeholder': 'Select Cover Photo',
261 'style': 'width: 200px;',
262 'tabindex': '-1',
263 }),
264 'start_date': DateTimeWidget(attrs = {
265 'class': 'datetimepicker col-xs-6',
266 'id': 'event-start-date-editable',
267 'editable-detail': '',
268 }),
269 'end_date': DateTimeWidget(attrs = {
270 'class': 'datetimepicker col-xs-6',
271 'id': 'event-end-date-editable',
272 'editable-detail': '',
273 }),
274 'gallery': SelectWithDefaultOptions(attrs={
275 'class': 'chosen-select-no-single',
276 'id': 'select-galley',
277 'data-placeholder': 'Select Gallery',
278 'style': 'width: 200px;',
279 'gallery-select': '',
280 'tabindex': '-1',
281 'organisator-profile-specific': '',
282 }
283 }
所以发生的是我的图库和 cover_photo select 小部件充满了这两种类型的所有现有对象(因为它们实际上是其他模型的外键)。 我想防止这种情况发生,正如您在第 243 行看到的那样,我尝试删除当前的查询集(尝试使用清理选项也产生相同的结果),效果很好。问题是,如您所见,我使用自定义 select 小部件,我在其中设置了一些默认选项。它看起来像这样:
62 class SelectWithDefaultOptions(forms.Select):
63 def __init__(self, attrs=None, choices=()):
64 super(SelectWithDefaultOptions, self).__init__(attrs, choices)
65
66 choices = ('', 'empty') + choices
67 choices = ('None', 'no selection') + choices
问题是我用上面提到的方法删除了那些值。 所以我对自己说 "Well, I will get the needed values, erase all and put the preferred back"。试过了,结果发现实际上 Django 放入的对象正在删除已经设置的对象。 (在 init 方法通过后添加默认的) 所以我想 "Well, if I set choices=() in the initialisation of the widget (line 274), Django should not set any other values on top of that, because this will violate my choices" 所以我试了一下,但事实证明 Django 实际上并不关心我希望有什么选择并且表现得一样。 也试过设置字段的'initial'属性,还是没有结果。
那么,如何防止 DJango 默认行为将引用的对象放入我的 select 的选择列表中?
谢谢。
Meta
中定义的 widget
不知何故导致了这个问题。如果您只是将其移动到 __init__
,它就会起作用。首先更新您的小部件,def __init__(self, attrs=None, choices=())
行中的关键字 choices
意味着默认情况下的选择是空的,但任何实例都可以通过传递一些值来覆盖它。所以你需要明确地设置它为空元组:
class SelectWithDefaultOptions(forms.Select):
def __init__(self, attrs=None, choices=()):
super(SelectWithDefaultOptions, self).__init__(attrs, choices)
choices = () # explicitly setting choices to empty here
choices += (('', 'empty'),)
choices += (('None', 'no selection'),)
self.choices = choices
现在更新您的表单,将小部件分配给 __init__
中的 gallery
字段,而不是 Meta
class:
class EventDetailForm(NgFormValidationMixin, NgModelForm):
def __init__(self, *args, **kwargs):
super(EventDetailForm, self).__init__(*args, **kwargs)
self.fields['gallery'].widget = SelectWithDefaultOptions(attrs={
'class': 'chosen-select-no-single',
'id': 'select-galley',
'data-placeholder': 'Select Gallery',
'style': 'width: 200px;',
'gallery-select': '',
'tabindex': '-1',
'organisator-profile-specific': '',
}
class Meta:
model = Event
fields = ('title', 'description', 'end_date', 'start_date', 'gallery', 'cover_photo')
或者您根本不需要任何自定义小部件。只需在表单 __init__
方法中设置选项,并在 Meta
中将 SelectWithDefaultOptions
小部件名称替换为 forms.Select
(这样更简洁):
class EventDetailForm(NgFormValidationMixin, NgModelForm):
def __init__(self, *args, **kwargs):
super(EventDetailForm, self).__init__(*args, **kwargs)
self.fields['gallery'].widget.choices = (('', 'Empty',),)
class Meta:
model = Event
fields = ('title', 'description', 'end_date', 'start_date', 'gallery', 'cover_photo')
widgets = {
'gallery': forms.Select(attrs={
'class': 'chosen-select-no-single',
'id': 'select-galley',
'data-placeholder': 'Select Gallery',
'style': 'width: 200px;',
'gallery-select': '',
'tabindex': '-1',
'organisator-profile-specific': '',
}),
}
在@AamirAdnan 的帮助下,我就是这样修复它的:
65 class SelectWithDefaultOptions(forms.Select):
66 def render(self, name, value, attrs=None, choices=()):
67 choices = ()
68 choices += (('empty', ''),)
69 choices += (('no selection', 'None'),)
70 self.choices = choices
71
72 if value is None:
73 value = ''
74
75 final_attrs = self.build_attrs(attrs, name=name)
76 output = [format_html('<select{0}>', flatatt(final_attrs))]
77 options = self.render_options((), [value])
78
79 if options:
80 output.append(options)
81 output.append('</select>')
82
83 return mark_safe('\n'.join(output))
刚刚改进了小部件渲染功能。
这与要求的不完全一样,但它确实有效。发生的事情是:
1. Django将choices值设置为引用对象
2.在widget渲染之前,将它们更改为正确的选择