Django - 自定义 ModelMultipleChoiceField 无法根据父模型对选择进行分类
Posted
技术标签:
【中文标题】Django - 自定义 ModelMultipleChoiceField 无法根据父模型对选择进行分类【英文标题】:Django - Custom ModelMultipleChoiceField can't categorize choices based on their parent model 【发布时间】:2019-06-14 07:14:27 【问题描述】:以下可用编辑!
我的目标:
类别 1
----选项1
----选项2
--选项3
类别2
----选项1
----选项2
等等
我有一个父模型 (Venue) 和一个子模型 (Amenity)。一个场地可以有很多便利设施。
在配置我的初始数据并使用 form.as_p 呈现时,一切正常。
但是当我尝试渲染自己的自定义表单以便应用循环时,它不会预先填充它们。
这是我的模板:
<form method="POST" class="ui form">
% csrf_token %
% for category in categories %
<h4 class="ui horizontal divider header">
<i class="list icon"></i>
category.category
</h4>
<p class="ui center aligned text"><u>category.description</u></p>
% for amenity in category.amenity_set.all %
<div class="inline field">
<label for="choices_amenity.id"></label>
<div class="ui checkbox">
<input id="choices_amenity.id" type="checkbox" value="amenity.id" name="choices">
<label><span data-tooltip="amenity.description" data-position="top left">amenity</span></label>
</div>
</div>
% endfor %
% endfor %
<button type="submit" name="submit" class="ui button primary">Next</button>
</form>
我的模型表单:
class AmenitiesForm(ModelForm):
class Meta:
model = Venue
fields = ('choices',)
choices = forms.ModelMultipleChoiceField(Amenity.objects.all(), widget=forms.CheckboxSelectMultiple,)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if kwargs.get('instance'):
initial = kwargs.setdefault('initial', )
initial['choices'] = [c.pk for c in kwargs['instance'].amenity_set.all()]
forms.ModelForm.__init__(self, *args, **kwargs)
def save(self, commit=True):
instance = forms.ModelForm.save(self)
instance.amenity_set.clear()
instance.amenity_set.add(*self.cleaned_data['choices'])
return instance
和我的views.py:
class AddAmenitiesView(LoginRequiredMixin, CreateView):
"""
AddAmenitiesView is the view that prompts the user to select the amenities of their venue.
"""
model = Venue
form_class = AmenitiesForm
template_name = 'venues/add_amenities.html'
def parent_venue(self):
"""
returns the parent_venue based on the kwargs
:return:
"""
parent_venue = Venue.objects.get(id=self.kwargs["venue_id"])
return parent_venue
def get_initial(self):
initial = super().get_initial()
initial['choices'] = self.parent_venue().amenity_set.all()
return initial
def form_valid(self, form):
venue = Venue.objects.get(id=self.kwargs['venue_id'])
form.instance = venue
# form.instance.owner = self.request.user
return super().form_valid(form)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["parent_venue"] = self.parent_venue()
context["categories"] = AmenitiesCategory.objects.all()
return context
def get_success_url(self):
return reverse('add-amenities', kwargs='venue_id': self.object.id,)
我想这与我的模板有关,因为正常渲染表单,它确实预先填充了模型。
感谢您抽出宝贵时间!
编辑: 通过下面 Raydel Miranda 的回答,我设法编辑了表单的呈现方式:
forms.py:
class CustomAmenitiesSelectMultiple(CheckboxSelectMultiple):
"""
CheckboxSelectMultiple Parent: https://docs.djangoproject.com/en/2.1/_modules/django/forms/widgets/#CheckboxSelectMultiple
checkbox_select.html: https://github.com/django/django/blob/master/django/forms/templates/django/forms/widgets/checkbox_select.html
multiple_input.html: https://github.com/django/django/blob/master/django/forms/templates/django/forms/widgets/multiple_input.html
checkbox_option.html: https://github.com/django/django/blob/master/django/forms/templates/django/forms/widgets/checkbox_option.html
input_option.html: https://github.com/django/django/blob/master/django/forms/templates/django/forms/widgets/input_option.html
"""
template_name = "forms/widgets/custom_checkbox_select.html"
option_template_name = 'forms/widgets/custom_checkbox_option.html'
class AmenitiesForm(ModelForm):
class Meta:
model = Venue
fields = ('choices',)
choices = forms.ModelMultipleChoiceField(Amenity.objects.all(), widget=CustomAmenitiesSelectMultiple,)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if kwargs.get('instance'):
initial = kwargs.setdefault('initial', )
initial['choices'] = [c.pk for c in kwargs['instance'].amenity_set.all()]
forms.ModelForm.__init__(self, *args, **kwargs)
def save(self, commit=True):
instance = forms.ModelForm.save(self)
instance.amenity_set.clear()
instance.amenity_set.add(*self.cleaned_data['choices'])
return instance
custom_checkbox_select.html:
% with id=widget.attrs.id %
<div class="inline field">
<div % if id % id=" id " % endif %% if widget.attrs.class % class=" widget.attrs.class " % endif %>
% for group, options, index in widget.optgroups %% if group %
<div>
group
<div>
% if id % id=" id _ index " % endif %>% endif %% for option in options %
<div class="checkbox">% include option.template_name with widget=option %</div>
% endfor %% if group %
</div>
</div>
% endif %% endfor %
</div>
</div>
% endwith %
custom_checkbox_option.html:
<label% if widget.attrs.id % for=" widget.attrs.id "% endif %>% endif %% include "django/forms/widgets/input.html" %% if widget.wrap_label % widget.label </label>
根据要求,还有我的models.py:
class TimeStampedModel(models.Model):
"""
An abstract base class model that provides self-updating
"created" and "modified" fields.
"""
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class VenueType(TimeStampedModel):
type = models.CharField(max_length=250)
description = models.TextField()
def __str__(self):
return self.type
class Venue(TimeStampedModel):
owner = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=250)
type = models.ForeignKey(VenueType, on_delete=models.CASCADE)
total_capacity = models.PositiveIntegerField(default=0)
description = models.TextField(blank=False)
contact_number = PhoneNumberField(blank=True)
contact_email = models.EmailField(blank=True)
published = models.BooleanField(default=False)
def __str__(self):
return self.name
class AmenitiesCategory(TimeStampedModel):
category = models.CharField(max_length=250)
description = models.TextField()
def __str__(self):
return self.category
class Amenity(TimeStampedModel):
category = models.ForeignKey(AmenitiesCategory, on_delete=models.CASCADE)
venues = models.ManyToManyField(Venue, blank=True)
space = models.ManyToManyField(Space, blank=True)
name = models.CharField(max_length=250)
description = models.TextField()
def __str__(self):
return self.name
class Meta:
ordering = ['category']
【问题讨论】:
【参考方案1】:您说在配置我的初始数据并使用 form.as_p 呈现它时,一切都按预期工作,如果是这样,请使用 form.choices 来呈现该字段.
<form method="POST" class="ui form">
% csrf_token %
form.choices
<button type="submit" name="submit" class="ui button primary">Next</button>
</form>
然后,您需要有一个带有自己模板的自定义CheckboxSelectMultiple
(以防您想要向用户展示自定义演示文稿),并在您的表单中使用它:
自定义CheckboxSelectMultiple
可以是:
class MyCustomCheckboxSelectMultiple(CheckboxSelectMultiple):
template_name = "project/template/custom/my_checkbox_select_multiple.html"
形式如下:
class AmenitiesForm(ModelForm):
# ...
choices = forms.ModelMultipleChoiceField(Amenity.objects.all(), widget=forms.MyCustomCheckboxSelectMultiple)
# ...
如何实现模板my_checkbox_select_multiple.html
,由你决定。
如果您使用的是 1.11 之前的 Django,请访问此链接以了解您为自定义小部件模板而要做的其他事情。
Django widget override template
希望对您有所帮助!
【讨论】:
谢谢你,我现在试试看,会告诉你的! @TonyKyriakidis,没什么,根据我的经验,覆盖小部件模板总是更好,它不仅是您可以告诉如何渲染的数据,还有错误。另一方面,自定义小部件变成了一段更可重用的代码。 我还没有设法让它工作。遍历类别并展示他们的便利设施是欺骗我的部分 我想看看你的模型。以上是关于Django - 自定义 ModelMultipleChoiceField 无法根据父模型对选择进行分类的主要内容,如果未能解决你的问题,请参考以下文章