Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django: render inherited (nested) values as a list in forms.ModelForm

I'm trying to display a dropdown list in my form, from my model 'TipoDocumento' (I need to display the column 'nombre_corto' as a list).

1) My model name is "Cocinera", Cocinera inherits from my model "Usuario".
2) "Usuario" inherits it's 'documento' field from my model "Documento".
3) "Documento" model inherits it's 'tipo_documento' field from "TipoDocumento".

But I cannot render 'tipo_documento', as a list, from my 'Cocinera' model through my 'CocineraForm' form. I get an error, detailed at the end.

All my models are in 'nucleo' app. Only the form that gets rendered is in my other app 'app_administrador'.

================================

Nucleo - The app is just named nucleo

================================

MODELS:

Model 'TipoDocumento'

from django.db import models

class TipoDocumento(models.Model):
        nombre_corto = models.CharField(blank=False, null=False, max_length=25)
        nombre_largo = models.CharField(blank=False, null=False, max_length=100)

    def __str__(self):
        return self.nombre_corto

Model 'Documento'

class Documento(models.Model):

    def __str__(self):
        return self.codigo

    tipo_documento = models.ForeignKey(TipoDocumento, on_delete=models.SET_NULL, null=True)
    codigo = models.CharField(max_length=25)

Model 'Usuario':

class Usuario(models.Model):
    class Meta:
        abstract = True

    nombre = models.CharField(blank=False, null=False, max_length=200)
    apellido_paterno = models.CharField(blank=False, null=False, max_length=100)
    apellido_materno = models.CharField(blank=True, null=False, max_length=100)
    fecha_de_nacimiento = models.DateField(blank=False, null=False)
    documento = models.OneToOneField(Documento, on_delete=models.CASCADE, blank=False, null=True)

Model 'Cocinera':

class Cocinera(Usuario):

    habilidades = models.ForeignKey(Habilidad, blank=True, null=True)
    experiencia_con_mascotas = models.BooleanField(default=False)


    def __str__(self):
        return self.nombre

================================

app_administrador

================================

My FORM

class CocineraForm(forms.ModelForm):
    class Meta:
        model = Cocinera
        fields = ['nombre', 'apellido_paterno', 'apellido_materno', 'tipo_documento', 'documento', 'fecha_de_nacimiento', 'direccion', 'telefono_o_celular' , 'latitud_y_longitud',
                  'genero', 'antecedentes_policiales', 'foto']
        widgets = {
            'fecha_de_nacimiento': DateInput()
        }

Related question: According to this Use Django ModelChoice field to create pulldown to lookup table?

I've added:

tipo_documento = forms.ModelChoiceField(queryset=TipoDocumento.objects.all(),
                                             empty_label=None)

But still when I run my app, I get:

File "/home/ogonzales/Escritorio/web_envs/
cocineras_env/lib/python3.5/site-packages/django/forms/models.py",
line 277, in __new__raise FieldError(message)
django.core.exceptions.FieldError: Unknown field(s) 
(tipo_documento) specified for Cocinera
like image 463
Omar Gonzales Avatar asked Nov 22 '17 21:11

Omar Gonzales


2 Answers

Of course you can add an additional field on the form as you already did.

But you are not allowed to add the non-model field 'tipo_documento' in the list of fields, as this refers only to fields defined in your model.

So you should be good to go with:

class CocineraForm(forms.ModelForm):
class Meta:
    model = Cocinera
    fields = ['nombre', 'apellido_paterno', 'apellido_materno', 'documento',
              'fecha_de_nacimiento', 'direccion', 'telefono_o_celular', 'latitud_y_longitud',
              'genero', 'antecedentes_policiales', 'foto']
    widgets = {
        'fecha_de_nacimiento': DateInput()
    }

    tipo_documento = forms.ModelChoiceField(queryset=TipoDocumento.objects.all(),
                                    empty_label=None)

Note the missing 'tipo_documento' from the fields list.

like image 114
normic Avatar answered Oct 15 '22 05:10

normic


So, I've found the answer: I needed to work with 2 forms, not just 1, and render both in the 'template'.

Steps:

1.- I needed to create an additional form "DocumentoForm", apart from the "CocineraForm".

class CocineraForm(forms.ModelForm):
    class Meta:
        model = Cocinera
        fields = ['nombre', 'apellido_paterno', 'apellido_materno', 'fecha_de_nacimiento', 'direccion', 'telefono_o_celular', 'latitud_y_longitud',
                  'genero', 'foto']



#New DocumentoForm

class DocumentoForm(forms.ModelForm):
    class Meta:
        model = Documento
        fields = ['tipo_documento', 'codigo']

2.- I needed to validate all fields of both forms. Create a "Documento" instance from the form "DocumentoForm", without saving it to the DB (commit='False'). And I needed to add this 'Documento' instance to the 'cocinera' model as a field. Just here I should save the 'CocinerForm' with '.save()'.

class RegistroView(View):
    def get(self, request):
        cocinera_form = CocineraForm()
        documento_form = DocumentoForm()
        context = {'cocinera_form': cocinera_form, 'documento_form': documento_form}
        return render(request, 'app_administrador/crear-registro-como-secretaria.html', context)

    def post(self, request):
        cocinera_form = CocineraForm(request.POST, request.FILES)
        documento_form = DocumentoForm(request.POST)

        if all((cocinera_form.is_valid(), documento_form.is_valid())):
            documento = documento_form.save()
            cocinera = cocinera_form.save(commit=False) #Don't save it to the DB. Just store the variable for now.
            cocinera.documento = documento #adding documento to 'Cocinera' model through CocineraForm.
            cocinera.save()
        return HttpResponseRedirect('/')

3.- Work with 2 forms in the template.

<form action="" method="post">
 {% csrf_token %}
 {{ cocinera_form }}
 {{ documento_form }}
</form>
like image 25
Omar Gonzales Avatar answered Oct 15 '22 05:10

Omar Gonzales