Search code examples
djangodjango-formsdjango-viewsdjango-validation

How to validate if a field is not blank in a form when it's set Blank = True in the model


I have a model which is filled in different steps by different forms. Fields that are not filled in the first step need to be set Blank = True so you can submit the form. When I try to fill those fields later, the form lets the user leave them blank, which is undesirable. How can I make them mandatory in the subsequent forms?

I've tried implementing a Validation method (clean_almacen) like the one below, but it does nothing.

class RecepcionForm(ModelForm):
    def clean_almacen(self):
        data = self.cleaned_data['almacen']
        # Check if the field is empty.
        if data == '':
            raise ValidationError(_('¡Seleccione un almacén!'))
        return data

    def clean_usuario(self):
        if not self.cleaned_data['usuario_recepcion']:
            return User()
        return self.cleaned_data['usuario_recepcion']

    class Meta:
        model = Pedido
        fields = ['almacen']

Also, setting the field Blank = False and null = True will make this work, but it will make mandatory to assign a value to the field when you edit the object in the admin page (which is undesirable too).

This is my code:

models.py

class Pedido(models.Model):
    nombre = models.CharField(max_length=40, help_text=_('Nombre del producto.'))
    referencia = models.CharField(max_length=20, help_text=_('Referencia del fabricante.'))
    cpm = models.CharField(max_length=20, default ='A la espera.',help_text=_('Código del CPM asignado a este pedido.'), null = True, blank = True, verbose_name = _('CPM'))
    fecha = models.DateTimeField(auto_now_add=True)
    fecha_cpm = models.DateTimeField(blank=True, null=True, verbose_name=_('Fecha asignación CPM'))
    unidades = models.IntegerField(default = 1)
    usuario = models.ForeignKey(User, on_delete=models.CASCADE, blank = True)
    autogestion = models.BooleanField(default = False, verbose_name='Autogestión', help_text = _("Marca esta casilla si vas a procesar tú mismo el pedido."))
    usuario_recepcion = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True, related_name='recepcion', verbose_name=_('Recepcionado por'))
    fecha_recepcion = models.DateTimeField(blank=True, null=True)

    ESTADO_PEDIDO = (
        ('n', _('Pendiente')),
        ('p', _('Proforma solicitada')),
        ('c', _('CPM solicitado')),
        ('v', _('Para validar')),
        ('r', _('Recibido')),
        ('b', _('Bloqueado')),
    )

    estado = models.CharField(
        max_length=1,
        choices=ESTADO_PEDIDO,
        blank=False,
        default='n',
        help_text=_('Estado del pedido'),
    )

    fabricante = models.ForeignKey('Fabricante', null = True, on_delete=models.SET_NULL)
    centro_gasto = models.ForeignKey('CentroGasto', null = True, on_delete=models.SET_NULL, verbose_name = _('Centro de Gasto'))
    almacen = models.ForeignKey('Almacen', null = True, on_delete=models.SET_NULL, blank = True)
    direccion = models.ForeignKey('Direccion', default = 'CIBM', null = True, on_delete=models.SET_NULL, verbose_name = _('Dirección de entrega'))
    codigo = models.CharField(max_length=20, blank=True, default=keygen())
    bloqueo = models.TextField(blank=True, verbose_name=_('Incidencias'), help_text = _('Anote las incidencias relacionadas con el pedido para que puedan ser solucionadas'))

views.py

@permission_required('gestion.puede_editar_cpm')
def añadir_cpm(request, pk):
    instance = get_object_or_404(Pedido, id=pk)
    if request.method == "POST":
        form = CPMForm(request.POST, instance=instance)
        if form.is_valid():
            model_instance = form.save(commit=False)
            model_instance.estado = 'v'
            model_instance.fecha_cpm = datetime.now()
            model_instance.save(update_fields=['estado', 'fecha_cpm', 'cpm'])
            return redirect('/')
    else:
        form = CPMForm()
        return render(request, "gestion/cpm_edit.html", {'form': form})

@permission_required('gestion.puede_editar_cpm')
def cpm_block(request, pk):
    instance = get_object_or_404(Pedido, id=pk)
    if request.method == "POST":
        form = CPMBlockForm(request.POST, instance=instance)
        if form.is_valid():
            model_instance = form.save(commit=False)
            model_instance.estado = 'b'
            model_instance.save(update_fields=['estado', 'bloqueo'])
            return redirect('/')
    else:
        form = CPMBlockForm()
        return render(request, "gestion/cpm_block.html", {'form': form})

@login_required
def recepcion(request, pk):
    instance = get_object_or_404(Pedido, id=pk)
    if request.method == "POST":
        form = RecepcionForm(request.POST, instance=instance)
        if form.is_valid():
            model_instance = form.save(commit=False)
            model_instance.usuario_recepcion = request.user
            model_instance.estado = 'r'
            model_instance.fecha_recepcion = datetime.now()
            model_instance.save(update_fields=['usuario_recepcion', 'almacen', 'fecha_recepcion', 'estado'])
            return redirect('/')
    else:
        form = RecepcionForm()
        return render(request, "gestion/pedido_recepcionar.html", {'form': form})

forms.py

class PedidoForm(ModelForm):
    def clean_usuario(self):
        if not self.cleaned_data['usuario']:
            return User()
        return self.cleaned_data['usuario']

    class Meta:
        model = Pedido
        exclude = ['codigo', 'fecha', 'cpm', 'almacen', 'estado', 'usuario']

class RecepcionForm(ModelForm):
    def clean_almacen(self):
        data = self.cleaned_data['almacen']
        # Check if a date is not in the past.
        if data == '':
            raise ValidationError(_('¡Seleccione un almacén!'))
        return data

    def clean_usuario(self):
        if not self.cleaned_data['usuario_recepcion']:
            return User()
        return self.cleaned_data['usuario_recepcion']

    class Meta:
        model = Pedido
        fields = ['almacen']

class CPMForm(ModelForm):

    class Meta:
        model = Pedido
        fields = ['cpm']

class CPMBlockForm(ModelForm):

    class Meta:
        model = Pedido
        fields = ['bloqueo']

I'm sorry for the long code, I don't know what could be useful or not. I hope you guys can help me. Thanks in advance.


Solution

  • You would override the field definitions in the subsequent forms. You can do this declaratively:

    class CPMForm(ModelForm):
        cpm = forms.CharField(required=True, max_length=20, initial='A la espera.', help_text=_('Código del CPM asignado a este pedido.'), label=_('CPM'))
    
        class Meta:
            model = Pedido
            fields = ['cpm']
    

    or programmatically:

    class CPMBlockForm(ModelForm):
    
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.fields['bloqueo'].required = True
    
        class Meta:
            model = Pedido
            fields = ['bloqueo']