I'm doing a query to get the chocies from a charfield and assign them. But at the moment I want to save the form in my view I am getting this error. What I can do?
ValueError: Cannot assign "'17'": "Con_Transaccioncab.doc" must be a "Adm_Documento" instance.
In the form I retrieve and assign the options to the field
form.py
class Con_MovimientosForm(ModelForm):
class Meta:
model = Con_Transaccioncab
fields = '__all__'
exclude = ['cab_estado']
widgets = {
'emp_id': HiddenInput(),
'per_id': HiddenInput(),
'suc_id': HiddenInput(),
'cab_numero': HiddenInput(),
'cab_estadocon': RadioSelect,
'cab_escambpat': CheckboxInput,
'cab_fecha': DatePickerInput(format=Form_CSS.fields_date_format,
options=Form_CSS.fields_date_opts,
attrs={'value': Form_CSS.fields_current_date}),
}
def __init__(self, *args, **kwargs):
self.AIGN_EMP_ID = kwargs.pop("AIGN_EMP_ID")
self.AIGN_PER_ID = kwargs.pop("AIGN_PER_ID")
# --------------------------------------------------------------------------------------
self.AIGN_OPCIONES = kwargs.pop("AIGN_OPCIONES")
self.PERMISOS = [] # para recuperar los permisos de la tabla
__json_values = json.loads(json.dumps(self.AIGN_OPCIONES))
self.PERMISOS = recuperarPermisos(__json_values, self._meta.model.get_table_name(self._meta.model))
# --------------------------------------------------------------------------------------
super(Con_MovimientosForm, self).__init__(*args, **kwargs)
self.fields['cab_estadocon'].required = False
self.fields['cab_numero'].required = False
# here I am assigned the choices to the field
self.fields['doc'] = ChoiceField(label='Acción: ', choices=self.get_choices(), required=False)
for form in self.visible_fields():
form.field.widget.attrs['placeholder'] = Form_CSS.fields_placeholder form.field.label.lower()
form.field.widget.attrs['autocomplete'] = Form_CSS.fields_autocomplete
form.field.widget.attrs['class'] = Form_CSS.fields_attr_class
self.helper = FormHelper(self)
self.helper.form_method = 'post'
self.helper.form_id = Form_CSS.getFormID(self)
self.helper.attrs = Form_CSS.form_attrs
self.helper.form_tag = True
self.helper.form_error_title = Form_CSS.form_err_title
self.helper.form_class = Form_CSS.form_class
self.helper.label_class = Form_CSS.fields_label_class
self.helper.field_class = 'col-sm-5'
self.helper.layout = Layout(
Div(
DivHeaderWithButtons(instance_pk=self.instance.pk, remove_delete=True, remove_print=False,
permisos=self.PERMISOS),
Div(
Div(
Div('cab_numero',
'cab_fecha',
'doc', # this is the field of which the error is giving
'cab_observacion',
Div('cab_estadocon', style='pointer-events: none;'),
'per_id',
'suc_id',
'emp_id',
css_class='col-sm'),
Div('cab_beneficia',
'cab_essaldoini',
'cab_escambpat',
'cab_elaborado',
'cab_revisado',
'cab_aprovado',
css_class='col-sm'),
css_class='row'
),
css_class='card-body', id='body_id'
),
css_class='card'
),
Div(
DivGridHeaderWithButtons(instance_pk=self.instance.pk,
grid_opts=get_MovimientosDetForm(self.AIGN_PER_ID, None)),
)
)
def clean(self):
super(Con_MovimientosForm, self).clean()
return self.cleaned_data
# This is the query where I get the choices
def get_choices(self):
all_tipoaux = Adm_DocumentoPeriodo.objects.select_related('doc').filter \
(per=self.AIGN_PER_ID).select_related('doc__mod').filter(doc__mod__mod_codigov='CON').values("doc__doc_id",
"doc__doc_nombre")
DOC = [(int(d['doc__doc_id']), str(d['doc__doc_nombre'])) for d in all_tipoaux]
return DOC
these are the 3 models that I am using for this example.
models.py
class Con_Transaccioncab(models.Model):
ESTADO_CHOICES = [
('S', 'Sin Contabilizar'),
('C', 'Contabilizado'),
('I', 'Incompleto'),
('A', 'Anulado'),
]
cab_id = models.BigAutoField(primary_key=True)
cab_numero = models.BigIntegerField(verbose_name='#')
cab_fecha = models.DateField(verbose_name='Fecha')
cab_estadocon = models.CharField(
max_length=2,
choices=ESTADO_CHOICES,
default="I", verbose_name="Estado")
cab_observacion = models.CharField(max_length=128, blank=True, null=True, verbose_name='Observación')
cab_elaborado = models.CharField(max_length=32, blank=True, null=True, verbose_name='Elaborado por')
cab_revisado = models.CharField(max_length=32, blank=True, null=True, verbose_name='Revisado por')
cab_aprovado = models.CharField(max_length=32, blank=True, null=True, verbose_name='Aprobado por')
cab_beneficia = models.CharField(max_length=32, blank=True, null=True, verbose_name='Beneficiario')
cab_essaldoini = models.BooleanField(verbose_name='Saldos iniciales',default=False)
cab_escambpat = models.BooleanField(verbose_name='Cambio patrimonio',default=False)
doc = models.ForeignKey(Adm_Documento, models.DO_NOTHING, db_column='doc_id', verbose_name="Documento")
per_id = models.ForeignKey(Adm_Periodo, models.DO_NOTHING, db_column='per_id')
suc_id = models.ForeignKey(Adm_Sucursal, models.DO_NOTHING, db_column='suc_id')
emp_id = models.ForeignKey(Adm_Empresa, models.DO_NOTHING, db_column='emp_id')
cab_estado = models.SmallIntegerField(default=True)
class Meta:
managed = False
verbose_name = 'Movimientos'
db_table = 'con\".\"transaccioncab'
unique_together = (('cab_id', 'doc_id', 'per_id', 'suc_id', 'emp_id'),)
class Adm_Documento(models.Model):
class Choices_DocTipo(models.TextChoices):
Ingreso = 'I', _('Ingreso')
Egreso = 'E', _('Egreso')
Transferencia = 'T', _('Transferencia')
EntregaPedido = 'P', _('Entrega pedido')
class Choices_DocMovDebCre(models.TextChoices):
Cheque = 'CHE', _('Cheque')
Deposito = 'DEP', _('Depósito')
NotaCredito = 'NDC', _('Transferencia')
NotaDebito = 'NDD', _('Entrega pedido')
Ninguno = '', _('Ninguno')
doc_id = models.BigAutoField(primary_key=True)
doc_codigov = models.CharField(max_length=32, verbose_name='Código')
doc_nombre = models.CharField(max_length=64, verbose_name='Nombre')
doc_tipo = models.CharField(max_length=1, blank=True, null=True, verbose_name='Tipo',
choices=Choices_DocTipo.choices)
doc_orden = models.SmallIntegerField(verbose_name='Orden')
doc_actcosto = models.BooleanField(verbose_name='Actualiza costo')
doc_movcre = models.CharField(max_length=3, blank=True, null=True, verbose_name='Mov. Crédito',
choices=Choices_DocMovDebCre.choices, default=Choices_DocMovDebCre.Ninguno)
doc_movdeb = models.CharField(max_length=3, blank=True, null=True, verbose_name='Mov. Débito',
choices=Choices_DocMovDebCre.choices, default=Choices_DocMovDebCre.Ninguno)
tcc = models.ForeignKey(Sis_TemplateContabCab, models.DO_NOTHING, blank=True, null=True,
verbose_name=Sis_TemplateContabCab._meta.verbose_name)
mod = models.ForeignKey(Sis_Modulo, models.DO_NOTHING, verbose_name=Sis_Modulo._meta.verbose_name)
emp = models.ForeignKey(Adm_Empresa, models.DO_NOTHING, verbose_name=Adm_Empresa._meta.verbose_name)
doc_estado = models.SmallIntegerField(default=True)
class Meta:
managed = False
db_table = 'adm\".\"documento'
unique_together = (('doc_id', 'emp'), ('doc_codigov', 'emp'))
verbose_name = 'Documento'
verbose_name_plural = 'Documentos'
def __str__(self):
return self.doc_nombre
class Adm_DocumentoPeriodo(models.Model):
dop_id = models.BigAutoField(primary_key=True)
doc = models.ForeignKey(Adm_Documento, models.DO_NOTHING,db_column='doc_id')
per = models.ForeignKey(Adm_Periodo, models.DO_NOTHING,db_column='per_id')
emp = models.ForeignKey(Adm_Empresa, models.DO_NOTHING,db_column='emp_id')
dop_secuencia = models.BigIntegerField(verbose_name='Secuencia')
dop_estado = models.SmallIntegerField(default=True)
class Meta:
managed = False
db_table = 'adm\".\"documento_periodo'
unique_together = (('per', 'emp', 'doc'),)
The error occurs in the view at form.is_valid()
View.py
class Con_MovimientosUpdateView(UpdateView):
model = Con_Transaccioncab
form_class = Con_MovimientosForm
template_name = 'con_movimientos/form.html'
success_url = reverse_lazy('con_man:con_movimientos_list')
error_url = reverse_lazy('con_man:con_movimientos_add')
def dispatch(self, request, *args, **kwargs):
for k in self.kwargs.keys():
self.kwargs[k] = _e.decrypt(self.kwargs[k])
self.object = self.get_object()
return super().dispatch(request, *args, **kwargs)
def get_form_kwargs(self):
kwargs = super(Con_MovimientosUpdateView, self).get_form_kwargs()
kwargs.update({'AIGN_EMP_ID': self.request.session['AIGN_EMP_ID']})
kwargs.update({'AIGN_PER_ID': self.request.session['AIGN_PER_ID']})
kwargs.update({'AIGN_OPCIONES': self.request.session['AIGN_OPCIONES']})
return kwargs
def post(self, request, *args, **kwargs):
r = {'a': 1}
extra_errors = []
form = self.get_form()
if 'SAVE' in request.POST and form.is_valid(): # In this line the error occurs
if 'cab_escambpat' in request.POST and request.POST['es_patri'] == 'false':
return JsonResponse({'__all__': ["Error debe existir almenos una cuenta de patrimonio"]})
else:
try:
df_movDetCuenta = pd.DataFrame(json.loads(request.POST['movimientos_grid']))
df_movDetCuenta["det_desc"].fillna('', inplace=True)
df_movDetCuenta["det_refdoc"].fillna('', inplace=True)
with transaction.atomic():
con_transaccioncab = self.get_object()
for index, row in df_movDetCuenta.iterrows():
if float(row.det_valor) > 0:
cueDet = Con_Transacciondet()
if not pd.isna(row.det_id):
cueDet.det_id = row.det_id
cueDet.det_estado = 0 if row._action == 'E' else 1
else:
cueDet.det_estado = 1
cueDet.cue_id_id = row.cue_id_id
cueDet.cab_id_id = con_transaccioncab.cab_id
cueDet.suc_id = con_transaccioncab.suc_id
cueDet.emp_id = con_transaccioncab.emp_id
cueDet.per_id = con_transaccioncab.per_id
cueDet.doc_id = con_transaccioncab.doc_id
cueDet.det_fecha = con_transaccioncab.cab_fecha
cueDet.det_desc = row.det_desc
cueDet.det_istransf = row.det_istransf
cueDet.det_refdoc = row.det_refdoc
cueDet.det_tipomov = row.det_tipomov
cueDet.det_valor = row.det_valor
cueDet.save()
form.save()
messages.success(request, CRUD_MSG.SAVE)
return JsonResponse(r)
except django.db.models.query_utils.InvalidQuery as e:
extra_errors.append(str(e))
self.object = None
context = self.get_context_data(**kwargs)
context['form'] = form
errors = form._errors.setdefault("__all__", ErrorList())
errors.extend(extra_errors)
return JsonResponse(r)
CodePudding user response:
In Django, ForeigField
references the object itself not its id. So calling it doc_id
is incorrect and confusing. What you should do is call the field doc
. Then setting doc_id
would work as you'd expect.
With what you have now you'd need to use doc_id_id
to set the actual id which is... you know...
CodePudding user response:
I managed to solve the problem, apparently, in the forms it is also necessary to write the _id
for it to work correctly. In my case doc
to doc_id
class Con_MovimientosForm(ModelForm):
class Meta:
model = Con_Transaccioncab
fields = '__all__'
exclude = ['cab_estado']
widgets = {
'emp_id': HiddenInput(),
'per_id': HiddenInput(),
'suc_id': HiddenInput(),
'cab_numero': HiddenInput(),
'cab_estadocon': RadioSelect,
'cab_escambpat': CheckboxInput,
'cab_fecha': DatePickerInput(format=Form_CSS.fields_date_format,
options=Form_CSS.fields_date_opts,
attrs={'value': Form_CSS.fields_current_date}),
}
def __init__(self, *args, **kwargs):
# Obtiene la variable de sesion desde la vista y la asigna al self
self.AIGN_EMP_ID = kwargs.pop("AIGN_EMP_ID")
self.AIGN_PER_ID = kwargs.pop("AIGN_PER_ID")
# --------------------------------------------------------------------------------------
self.AIGN_OPCIONES = kwargs.pop("AIGN_OPCIONES")
self.PERMISOS = [] # para recuperar los permisos de la tabla
__json_values = json.loads(json.dumps(self.AIGN_OPCIONES))
self.PERMISOS = recuperarPermisos(__json_values, self._meta.model.get_table_name(self._meta.model))
# --------------------------------------------------------------------------------------
super(Con_MovimientosForm, self).__init__(*args, **kwargs)
# Cambiar atributos especificos
# self.fields['cab_estadocon'].widget.attrs['disabled'] = True
self.fields['cab_estadocon'].required = False
self.fields['cab_numero'].required = False
self.fields['doc_id'] = ChoiceField(label='Acción: ', choices=self.get_choices(), required=False)
# Cambiar atributos genericos
for form in self.visible_fields():
form.field.widget.attrs['placeholder'] = Form_CSS.fields_placeholder form.field.label.lower()
form.field.widget.attrs['autocomplete'] = Form_CSS.fields_autocomplete
form.field.widget.attrs['class'] = Form_CSS.fields_attr_class
self.helper = FormHelper(self)
self.helper.form_method = 'post'
self.helper.form_id = Form_CSS.getFormID(self)
self.helper.attrs = Form_CSS.form_attrs
self.helper.form_tag = True
self.helper.form_error_title = Form_CSS.form_err_title
self.helper.form_class = Form_CSS.form_class
self.helper.label_class = Form_CSS.fields_label_class
self.helper.field_class = 'col-sm-5'
self.helper.layout = Layout(
Div(
DivHeaderWithButtons(instance_pk=self.instance.pk, remove_delete=True, remove_print=False,
permisos=self.PERMISOS),
Div(
Div(
Div('cab_numero',
'cab_fecha',
'doc_id',
'cab_observacion',
Div('cab_estadocon', style='pointer-events: none;'),
'per_id',
'suc_id',
'emp_id',
css_class='col-sm'),
Div('cab_beneficia',
'cab_essaldoini',
'cab_escambpat',
'cab_elaborado',
'cab_revisado',
'cab_aprovado',
css_class='col-sm'),
css_class='row'
),
css_class='card-body', id='body_id'
),
css_class='card'
),
Div(
DivGridHeaderWithButtons(instance_pk=self.instance.pk,
grid_opts=get_MovimientosDetForm(self.AIGN_PER_ID, None)),
# aqui va el detalle
)
)
def clean(self):
super(Con_MovimientosForm, self).clean()
return self.cleaned_data
# Select de 3 tablas para obtener el nombre y valor del documento de la tabla adm_documento_periodo
def get_choices(self):
all_tipoaux = Adm_DocumentoPeriodo.objects.select_related('doc').filter \
(per=self.AIGN_PER_ID).select_related('doc__mod').filter(doc__mod__mod_codigov='CON').values("doc__doc_id",
"doc__doc_nombre")
DOC = [(int(d['doc__doc_id']), str(d['doc__doc_nombre'])) for d in all_tipoaux]
return DOC