Reputation: 862
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
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