D_P
D_P

Reputation: 862

Why my django form cleaned data returns None everytime?

I am using the self-referential Foreign Key in my models. In the views when I used the form.cleaned_data.get('parent') it returns None but when I used request.POST.get('parent') it returns the value. Why the cleaned_data is not working here?

models

class Category(models.Model):
    parent = models.ForeignKey('self', blank=True, null=True, on_delete=models.SET_NULL)
    slug = models.SlugField(unique=True, max_length=50)
    title = models.CharField(max_length=255, unique=True)
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

forms

class CreateCategoryForm(forms.ModelForm):
    class Meta:
        model = Category
        fields = ['title', 'slug']

views

def create_category(request):
    form = CreateCategoryForm()
    categories = Category.objects.order_by('-created')
    if request.method == 'POST':
        form = CreateCategoryForm(request.POST)
        if form.is_valid():
            # parent = request.POST.get('parent') # works
            # parent = form.cleaned_data.get('parent') # returns None
            category = form.save(commit=False)
            category.parent = form.cleaned_data.get('parent')
            category.save()
            messages.success(request, 'Created Successfully.')
            return redirect('list_categories')
    return render(request, 'add_category.html', {'form':form, 'categories':categories})

template

       <form action="" method="post">
              {% csrf_token %}

            <div class="form-group">
              <label>Category Name</label>
              <input type="text" name="title" class="form-control"
              value="{{form.title.value|default_if_none:''}}"/>
                {% if form.title.errors %}
                 <span class="error">{{form.title.errors|striptags}}</span>
                {% endif %}
            </div>
            <div class="form-group">
            <label for="select" class="">Category Parent</label>
                <select name="parent" id="exampleSelect"
              class="form-control">
                 <option selected>Select</option>
                {% for category in categories %}
              <option value="{{category.pk}}">{{category.title}}</option>
                {% endfor %}
            </select>
            </div>

Upvotes: 0

Views: 1918

Answers (1)

user1600649
user1600649

Reputation:

So your first issue, is that you don't include the field in the modelform, so fix that:

class CreateCategoryForm(forms.ModelForm):
    class Meta:
        model = Category
        fields = ['title', 'slug', 'parent']

Second is that you render the form field yourself in the template, thus not making use of Django's attribute handling. To fix that:

            <div class="form-group">
                <label for="{{form.parent.id_for_label}}">Category Parent</label>
                {{form.parent}}
            </div>

Now the select will not have a the correct html class, but we can pass this to the form field. Complete solution:

class CreateCategoryForm(forms.ModelForm):
    # ModelForm will take care of setting required to False
    parent = forms.ModelChoiceField(queryset=Category.objects.order_by('-created'), attrs={'class': 'form-control'})
    class Meta:
        model = Category
        fields = ['title', 'slug', 'parent']

# view
def create_category(request):
    form = CreateCategoryForm()
    if request.method == 'POST':
        form = CreateCategoryForm(request.POST)
        if form.is_valid():
            category = form.save(commit=False)
            category.parent = form.cleaned_data.get('parent')
            category.save()
            messages.success(request, 'Created Successfully.')
            return redirect('list_categories')
    return render(request, 'add_category.html', {'form':form})

Upvotes: 1

Related Questions