В поле ModelChoiceField не удается присвоить начальные данные
В моем приложении пользователь может создавать и редактировать отчеты. При рендеринге страницы редактирования отчета, поля заполняются начальными данными.
Я столкнулся со следующей проблемой - одно из полей (operator) не отображает то значение которое я передаю ему во вьюшке через аргумент initial. Вместо этого поле либо остается пустым, либо принимает дефолтное значение - в зависимости от настроек. Все остальные поля заполняются начальными данными без ошибок.
Я использую наборы форм (Formsets), проблемное поле - поле выбора ModelChoiceField. Начальные данные берутся из json массива.
Насколько я смог разобраться в HTML коде - в проблемном поле operator, в тэге "option"(его вроде бы генерирует виджет Select) не хватает атрибута "selected". Но как этот атрибут туда поместить непонятно. Добавляет путаницы то что поле eq_type, которое также является полем выбора, отображается без ошибок. "selected" в нем появляется напротив нужного варианта.
Может быть кто-то сталкивался с подобным? Пожалуйста, подскажите в чем может быть проблема.
Фрагмент forms.py:
class AMSEquipmentForm(forms.Form):
EQ_TYPES = (
('panel_antenna', 'панельная антенна'),
('RRL_antenna', 'РРЛ антенна'),
('radio_module', 'радиомодуль')
)
eq_type = forms.ChoiceField(choices=EQ_TYPES, label='Тип')
height = forms.IntegerField(label='Высота')
proportions = forms.IntegerField(label='Размеры')
amount = forms.IntegerField(label='Количество')
manufacturer = forms.CharField(max_length=50, label='Производитель')
model = forms.CharField(max_length=50, label='Модель')
operator = forms.ModelChoiceField(queryset=Operator.objects.all(), label='Оператор') # Проблемное поле
note = forms.CharField(max_length=100, label='Примечание')
eq_type.widget.attrs.update({'class': 'formset-field'})
height.widget.attrs.update({'class': 'formset-field'})
proportions.widget.attrs.update({'class': 'formset-field'})
amount.widget.attrs.update({'class': 'formset-field'})
manufacturer.widget.attrs.update({'class': 'formset-field'})
model.widget.attrs.update({'class': 'formset-field'})
operator.widget.attrs.update({'class': 'formset-field'}) # Проблемное поле
note.widget.attrs.update({'class': 'formset-field'})
Фрагмент views.py:
def report_update(request, pk):
EquipmentFormset = formset_factory(AMSEquipmentForm, max_num=1)
report = Report.objects.get(idReport=pk)
form = ReportModelForm(instance=report)
formset = EquipmentFormset(initial=json.loads(report.reportEquipAms), prefix='reports_report') # Здесь передаю начальные данные
if request.method == 'POST':
form = ReportModelForm(request.POST or None)
formset = EquipmentFormset(request.POST or None, prefix='reports_report')
if form.is_valid() and formset.is_valid():
report.reportYear = form.cleaned_data['reportYear']
report.reportObject_id = form.cleaned_data['reportObject']
report.reportTemplate_id = form.cleaned_data['reportTemplate']
report.reportTeam_id = form.cleaned_data['reportTeam']
report.reportEquipment_id = form.cleaned_data['reportEquipment']
report.reportWind = form.cleaned_data['reportWind']
report.reportWeather = form.cleaned_data['reportWeather']
report.reportSoil = form.cleaned_data['reportSoil']
report.reportTemp = form.cleaned_data['reportTemp']
report.reportWeather3 = form.cleaned_data['reportWeather3']
report.reportElVoltage = form.cleaned_data['reportElVoltage']
report.reportElCableL = form.cleaned_data['reportElCableL']
report.reportElCableR = form.cleaned_data['reportElCableR']
report.reportElRope = form.cleaned_data['reportElRope']
report.reportElBus = form.cleaned_data['reportElBus']
report.reportMeasuresDate = form.cleaned_data['reportMeasuresDate']
report.reportData = form.cleaned_data['reportData']
report.reportEquipAms = json.dumps(formset.cleaned_data, cls=OperatorEncoder, ensure_ascii=False)
report.save()
return redirect('reports:report-list')
context = {
'report': report,
'form': form,
'formset': formset
}
return render(request, "reports/report_update.html", context)
Фрагмент models.py:
class Operator(models.Model):
idOperator = models.AutoField(primary_key=True)
operatorName = models.CharField(max_length=45)
operatorReport = models.ForeignKey("Report", null=True, on_delete=models.SET_NULL)
operatorContract = models.ForeignKey("Contract",null=True, on_delete=models.SET_NULL)
def __str__(self):
return self.operatorName
class Meta:
verbose_name = "Оператор"
verbose_name_plural = "Операторы"
Отображение полей в браузере, если смотреть через инспектор кода:
поле eq_type:
<select name="reports_report-1-eq_type" class="formset-field" id="id_reports_report-1-eq_type">
<option value="panel_antenna">панельная антенна</option>
<option value="RRL_antenna" selected>РРЛ антенна</option>
<option value="radio_module">радиомодуль</option>
</select>
поле operator:
<select name="reports_report-1-operator" class="formset-field" id="id_reports_report-1-operator">
<option value="8">Оператор3</option>
<option value="9">Оператор2</option>
<option value="10">Оператор1</option>
</select>
Фрагмент html шаблона, который отвечает за форму:
{% for equipment in formset %}
<tr class="item">
<td>
{{ equipment.eq_type }}
</td>
<td>
{{ equipment.height }}
</td>
<td>
{{ equipment.proportions }}
</td>
<td>
{{ equipment.amount }}
</td>
<td>
{{ equipment.manufacturer }}
</td>
<td>
{{ equipment.model }}
</td>
<td>
{{ equipment.operator }}
</td>
<td>
{{ equipment.note }}
</td>
<td>
<button type="button" class="btn btn-danger btn-sm remove-form-row" id="{{ formset.prefix }}">Delete</button>
</td>
</tr>
{% endfor %}
Ответы (1 шт):
@NKSM подсказал мне в комментариях, что я должен передать первичный ключ объекта Operator в поле «operator». Я передавал туда название объекта.
Переписал OperatorEncoder:
class OperatorEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, Operator):
return obj.idOperator
return json.JSONEncoder.default(self, obj)
и все заработало как нужно.