Home > front end >  ValueError: Cannot assign must be a model instance
ValueError: Cannot assign must be a model instance

Time:01-15

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
  •  Tags:  
  • Related