Ovo trenutno radi ovako: kad god se vrednost u combobox-u promeni, šalje se AJAX request koji poziva view koji renderuje još jedan <select> ako kategorija ima 'dece', u suprotnom vraća HttpResponse koji prestaje da 'kači' listener. Koristim django-mptt da modelujem svoje kategorije.
Dakle, imam jedan view za dodavanje proizvoda i hoću da budem siguran da je najuža kategorija (mora da bude leaf node) selektovana i da dodelim tu kategoriju svojoj Product instanci.
Evo ga taj view:
Code:
@login_required
def product_create_view(request):
if request.method == 'POST':
create_product_form = CreateProductForm(request.POST)
else:
create_product_form = CreateProductForm()
return render(request, 'products/product_create.html', {'form': create_product_form})
@login_required
def product_create_view(request):
if request.method == 'POST':
create_product_form = CreateProductForm(request.POST)
else:
create_product_form = CreateProductForm()
return render(request, 'products/product_create.html', {'form': create_product_form})
Evo je forma:
Code:
from django.forms import ModelForm
from .models import Product, Category
class CreateProductForm(ModelForm):
class Meta:
model = Product
fields = (
'title',
'description',
'price',
'year',
'category',
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['category'].queryset = Category.objects.filter(parent=None)
from django.forms import ModelForm
from .models import Product, Category
class CreateProductForm(ModelForm):
class Meta:
model = Product
fields = (
'title',
'description',
'price',
'year',
'category',
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['category'].queryset = Category.objects.filter(parent=None)
products/product_create.html:
Code:
{% extends 'pages/base.html' %}
{% block content %}
<h1>Create a product</h1>
<form method='POST' id='productForm' data-products-url="{% url 'products:ajax_load_categories' %}">
<label>Title</label>
{{ form.title }}
<label>Description</label>
{{ form.description }}
<label>Price</label>
{{ form.price }}
<label>Year</label>
{{ form.year }}
<label>Category</label>
{{ form.category }}
</form>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
var $r_ = function() {
var url = $("#productForm").attr("data-products-url");
var categoryId = $(this).val();
var toRemove = $(this).nextAll('select');
$.ajax({
url: url,
data: {
'category': categoryId
},
success: function (data) {
if (data != 'leaf_node') {
toRemove.remove();
$("#productForm").append(data);
}
else {
toRemove.remove();
}
}
});
} //end of $r_
$(document).on('change', 'select', $r_);
</script>
{% endblock %}
{% extends 'pages/base.html' %}
{% block content %}
<h1>Create a product</h1>
<form method='POST' id='productForm' data-products-url="{% url 'products:ajax_load_categories' %}">
<label>Title</label>
{{ form.title }}
<label>Description</label>
{{ form.description }}
<label>Price</label>
{{ form.price }}
<label>Year</label>
{{ form.year }}
<label>Category</label>
{{ form.category }}
</form>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
var $r_ = function() {
var url = $("#productForm").attr("data-products-url");
var categoryId = $(this).val();
var toRemove = $(this).nextAll('select');
$.ajax({
url: url,
data: {
'category': categoryId
},
success: function (data) {
if (data != 'leaf_node') {
toRemove.remove();
$("#productForm").append(data);
}
else {
toRemove.remove();
}
}
});
} //end of $r_
$(document).on('change', 'select', $r_);
</script>
{% endblock %}
Ovo je ajax_load_categories view:
Code:
def load_categories(request):
category_id = request.GET.get('category')
subcategories = Category.objects.get(id=category_id).get_children()
if subcategories:
return render(request, 'products/category_dropdown_list_options.html', {'subcategories': subcategories})
return HttpResponse('leaf_node')
def load_categories(request):
category_id = request.GET.get('category')
subcategories = Category.objects.get(id=category_id).get_children()
if subcategories:
return render(request, 'products/category_dropdown_list_options.html', {'subcategories': subcategories})
return HttpResponse('leaf_node')
products/category_dropdown_list_options.html:
Code:
<select>
<option value="">---------</option>
{% for subcategory in subcategories %}
<option value="{{ subcategory.pk }}">{{ subcategory.name }}</option>
{% endfor %}
</select>
<select>
<option value="">---------</option>
{% for subcategory in subcategories %}
<option value="{{ subcategory.pk }}">{{ subcategory.name }}</option>
{% endfor %}
</select>
I za svaki slučaju Category model:
Code:
class Category(MPTTModel):
parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children')
name = models.CharField(max_length=255)
slug = models.SlugField()
class Meta:
unique_together = (('parent', 'slug',))
verbose_name_plural = 'categories'
class MPTTMeta:
order_insertion_by = ['name']
def __str__(self):
return self.name
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super().save(*args, **kwargs)
def get_slug_list(self):
ancestors = self.get_ancestors(include_self=True)
slugs = [ancestor.slug for ancestor in ancestors]
new_slugs = []
for idx, ancestor in enumerate(slugs, 1):
new_slugs.append('/'.join(slugs[:idx]))
return new_slugs
def get_recursive_product_count(self):
return Product.objects.filter(category__in=self.get_descendants(include_self=True)).count()
class Category(MPTTModel):
parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children')
name = models.CharField(max_length=255)
slug = models.SlugField()
class Meta:
unique_together = (('parent', 'slug',))
verbose_name_plural = 'categories'
class MPTTMeta:
order_insertion_by = ['name']
def __str__(self):
return self.name
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super().save(*args, **kwargs)
def get_slug_list(self):
ancestors = self.get_ancestors(include_self=True)
slugs = [ancestor.slug for ancestor in ancestors]
new_slugs = []
for idx, ancestor in enumerate(slugs, 1):
new_slugs.append('/'.join(slugs[:idx]))
return new_slugs
def get_recursive_product_count(self):
return Product.objects.filter(category__in=self.get_descendants(include_self=True)).count()