Search code examples
djangodjango-modelsdjango-formsmodelform

how to make one form field compulsory to select or fill in other in django model form


i am creating where a one form field is dependent on other means only when one form is filled then only he can select or fill the other form field of the same model in django

from multiselectfield import MultiSelectField
class student(models.Model):
    lunch_choice = [ 
        ('Sandwich', 'Sandwich'),
        ('Salad', 'Salad'),
        ('omlete', 'omlete'),
    ]
    name = models.CharField(max_length=70, blank=False)
    classs =  models.CharField(max_length70, blank=True)
    lunch =  MultiSelectField(choices=lunch_choice, blank=True)

    def __str__(self):
        return self.name

i am using this model as form what i want if user want to submit the form and want select choices from lunch he has to fill the class field otherwise he will not be allowed to submit the form or he has to leave both the field then submit the form any ideas and suggestion will be appreciated i want the class field must be filed to select from lunch multiselect field


Solution

  • I did it without using MultiSelectField, because I don't know how to do it with it. Used the "clean" example taken from here If 'classs' is not filled in at the time of submitting, a ValidationError will be fired with output to the screen and the form will not submit. Also added a 'lunch' check for an empty value. You can uncomment the triple quotes in 'def clean' and see how it works without it.

    By code: the 'student' model is created. Based on it, the class MyForm is created in the forms.py file. The template(tam_form.html) and the form class are passed to the view class(MyFormCreateView).

    My application is called bboard, so the template path looks like this: template_name = 'bboard/tam_form.html' you must replace 'bboard' with your own.

    success_url = reverse_lazy('adding') where we return the result, in this case in the same form.

    urls.py

    from django.urls import path
    from .views import *
    
    urlpatterns = [
        path('adding/', MyFormCreateView.as_view(), name='adding'),
    ]
    

    templates(tam_form.html)

    <h2>form</h2>
    <form method="post">
          {% csrf_token %}
          {{ form.as_p }}
          <input type="submit" value="adding">
    </form>
    

    models.py

    from django.db import models
    
    class student(models.Model):
        lunch_choice = [
            ('Sandwich', 'Sandwich'),
            ('Salad', 'Salad'),
            ('omlete', 'omlete'),
        ]
        name = models.CharField(max_length=70, blank=False)
        classs =  models.CharField(max_length=70, blank=True)
        lunch =  models.CharField(max_length=30, choices=lunch_choice, blank=True)
    
        def __str__(self):
            return self.name
    

    forms.py

    from django.forms import ModelForm
    from django import forms
    from .models import student
    from django.core.exceptions import ValidationError
    
    class MyForm(ModelForm):
        class Meta:
            model = student
            fields = ('name', 'classs', 'lunch')
    
        #"""
        def clean(self):
            cleaned_data = super().clean()
            classs = cleaned_data.get("classs")
            lunch = cleaned_data.get("lunch")
    
            if classs:
                print('classs field is filled')
            else:
                raise ValidationError(
                        "fill in the field classs"
                    )
            if lunch == '':
                raise ValidationError(
                    "fill in lunch"
                )
        #"""
    

    views.py

    from django.views.generic.edit import CreateView
    from django.urls import reverse_lazy
    from .forms import MyForm
    
    class MyFormCreateView(CreateView):
        template_name = 'bboard/tam_form.html'
        form_class = MyForm
        success_url = reverse_lazy('adding')
    
        def get_context_data(self, **kwargs):
            context = super().get_context_data(**kwargs)
            return context
    

    Update 09/15/2022. if you need it with MultiSelectField, then I did this by changing the model, the rest remains unchanged:

    class student(models.Model):
        lunch_choice = [
            ('Sandwich', 'Sandwich'),
            ('Salad', 'Salad'),
            ('omlete', 'omlete'),
        ]
        name = models.CharField(max_length=70, blank=False)
        classs =  models.CharField(max_length=70, blank=True)
        lunch = MultiSelectField(choices=lunch_choice, null=True, blank=True, max_choices=3, max_length=30)
    
        def __str__(self):
            return self.name
    

    Update 09/16/2022.

    Fixed the 'def clean' function, if the 'lunch' field is not empty, the length is greater than 0 and it does not equal an empty string, and 'classs' is an empty string, a ValidationError is raised with a 'fill in classes' page output. The form is not submitted when the button is clicked. If the 'classes' field is filled in and clicked submit, the data is written to the database. You can see it in the gif I attached.

    Also added another option, if the 'classs' field is not filled, then when the button is pressed, the remaining fields become inactive. If you fill in the 'classs' field and click the button, the fields will be unlocked. This option is commented out.

    from django.forms import ModelForm
    from django import forms
    from .models import student
    from django.core.exceptions import ValidationError
    
    class MyForm(ModelForm):
        class Meta:
            model = student
            fields = ('name', 'classs', 'lunch')
    
        
        def clean(self):
            cleaned_data = super().clean()
            classs = cleaned_data.get("classs")
            lunch = cleaned_data.get("lunch")
    
            if len(lunch) > 0 and lunch!= '' and classs == '':
                raise ValidationError(
                    "fill in classs"
                )
        
    
        """
        def clean(self):
            cleaned_data = super().clean()
            classs = cleaned_data.get("classs")
            lunch = cleaned_data.get("lunch")
    
            if classs:
                self.fields['lunch'].widget.attrs.update({'enabled': 'enabled'})
                self.fields['name'].widget.attrs.update({'enabled': 'enabled'})
            if len(lunch) > 0 and classs == '':
                self.fields['lunch'].widget.attrs.update({'disabled': 'disabled'})
                self.fields['name'].widget.attrs.update({'disabled': 'disabled'})
                raise ValidationError(
                    "fill in the field classs"
                )
        """
    

    enter image description here