Как передать переменную контекста из представления в настраиваемое поле / виджет в шаблоне Django?
Я определил настраиваемое поле и виджет для формы, которая обслуживается подклассами UpdateView
, Итак, как то так:
myapp/forms.py
:
from .form_fields import MyCustomField
from .widgets import MyCustomWidget
class MyModelForm(forms.ModelForm):
my_field = MyCustomField(queryset=MyModel.objects.all(), widget=MyCustomWidget)
myapp/views.py
:
from django.views.generic import UpdateView
from .forms import MyModelForm
class MyView(UpdateView):
form_class = MyModelForm
myapp/widgets.py
:
from django.forms import Widget
from django.template.loader import render_to_string
from django.utils.safestring import mark_safe
class MyCustomWidget(Widget):
context_data = { 'custom_data': custom_data }
html_output = render_to_string('myapp/widgets/my_custom_widget.html', context_data)
return mark_safe(html_output)
В принципе, я хочу быть в состоянии пройти custom_data
от моего представления (например, из хранилища сеансов или экземпляра формы) до виджета.
2 ответа
Я понял; Я не совсем уверен, что это лучший / рекомендуемый способ сделать это, но он работает.
Сначала, по мнению, обновить get_form_kwargs()
с пользовательскими данными. Например, в моем случае я хотел использовать дополнительные данные из экземпляра, прикрепленного к форме.
# myapp/views.py
from .forms import MyModelForm
class MyView(UpdateView):
form_class = MyModelForm
def get_form_kwargs(self):
kwargs = super(MyView, self).get_form_kwargs()
form_instance = kwargs.get('instance')
extra_widget_data = form_instance.widget_data
kwargs.update({ 'extra_widget_data': extra_widget_data })
return kwargs
Далее в вашей форме __init__()
, вставьте kwarg и прикрепите его к пользовательскому виджету в поле:
# myapp/forms.py
from django import forms
from .widgets import MyCustomWidget
class MyModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
extra_widget_data = kwargs.pop('extra_widget_data')
super(MyModelForm, self).__init__(*args, **kwargs)
self.fields['my_custom_field'] = MyCustomField(
widget=MyCustomWidget(extra_widget_data=extra_widget_data)
)
Наконец, в вашем пользовательском классе виджетов, возьмите переменную в __init__()
:
# myapp/widgets.py
from django.forms import Widget
from django.template.loader import render_to_string
from django.utils.safestring import mark_safe
class MyCustomWidget(Widget):
def __init__(self, attrs=None, extra_widget_data=None):
super(MyCustomWidget, self).__init__()
self.extra_widget_data = extra_widget_data
def render(self, name, value, attrs=None):
context_data = { 'custom_data': self.extra_widget_data }
html_output = render_to_string('myapp/widgets/my_custom_widget.html', context_data)
return mark_safe(html_output)
Теперь {{ custom_data }}
переменная шаблона доступна в отображаемом HTML из myapp/widgets/my_custom_widget.html
,
Я работал над аналогичной проблемой, и я думаю, что нашел немного более надежный и универсальный способ решения этой проблемы.
В вашем решении вы переопределяете render() для замены дополнительных данных контекста на ваш виджет.
Однако, когда я изучал эту концепцию в исходном коде Django, я обнаружил, что разработчики фреймворка Django на самом деле решают эту проблему таким образом, что я считаю лучшей практикой.
Вместо переопределения render() они переопределяют get_context(), как показано в классе ChoiceWidget по адресу https://docs.djangoproject.com/en/1.11/_modules/django/forms/widgets/
Благодаря такому подходу вам не нужно беспокоиться о переопределении кода рендеринга и идентификации шаблона, что, вероятно, лучше оставить профессионалам.
Вы также можете перезаписать метод Widget get_context:
# myapp/widgets.py
from django.forms import Widget
from django.template.loader import render_to_string
from django.utils.safestring import mark_safe
class MyCustomWidget(Widget):
template_name = 'myapp/widgets/my_custom_widget.html'
def __init__(self, attrs=None, extra_widget_data=None):
self.extra_widget_data = extra_widget_data
super(MyCustomWidget, self).__init__()
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
context['custom_data'] = self.extra_widget_data
return context