禁用默认行为 - 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渲染之前,将它们更改为正确的选择