3

i have 3 Models: Category, SubCategory and Product.

SubCategory is related to Category with a Foreignkey, and Product is related to Category and Subcategory through a ForeignKey.

What My Db looks like:

Category: Kids
    -->SubCategory: Beanies
Category: Men
    -->SubCategory: Hoodies

Now, When i want to add a Product and i select Kids, in the Product admin page, i only want related subcategories to show.

What I have tried:

  1. using formfield_for_foreignkey but i can't seem to grasp how it works.

  2. I came across this question Django Admin Show / Hide Fields If Specific Value Is Selected In A Dropdown but i think that it's more field related than value related.

Suggestions I have found online:

  • use ajax to fetch the subcategories and plug them in the child field.

My models:

class Category(models.Model):
    name = models.CharField(max_length=250, db_index=True, unique=True)
    slug = models.SlugField(max_length=250, db_index=True, unique=True)

class SubCategory(models.Model):
    parent = models.ForeignKey(Category, on_delete=models.CASCADE)
    name = models.CharField(max_length=250, db_index=True)
    slug = models.SlugField(max_length=250, db_index=True)

class Product(models.Model):  
    parent = models.ForeignKey(Category, on_delete=models.CASCADE, related_name="category")
    child = models.ForeignKey(SubCategory, on_delete=models.CASCADE)
    name = models.CharField(max_length=250, unique=True, db_index=True)
    slug = models.SlugField(max_length=250, unique=True, db_index=True)

My Admin Models:

@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
    list_display = [
        'name', 'slug'
    ]

@admin.register(SubCategory)
class SubCategoryAdmin(admin.ModelAdmin):
    prepopulated_fields = {'slug':('name',)}
    list_display = [
        'name', 'slug', 'parent',
    ]
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    list_display = [
        'name', 'slug', 'child', 
    ]
    prepopulated_fields = {'slug':('name',)}
    list_per_page = 20

    class Media:
        js = ('javascript/hide_child_if_parent_is_none.js', )
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Zack
  • 73
  • 10

1 Answers1

0

i fixed it by using Django Rest Framework & AJAX, an interesting experience it was. Here's the code

in SubCategory Model i have edited my parent field by adding related_name to it and removing it from Product.parent

parent = models.ForeignKey(Category, on_delete=models.CASCADE, related_name="children")

My Serializers:

from rest_framework import serializers
from catalogue.models import Category, SubCategory
from drf_writable_nested import WritableNestedModelSerializer
# Create your serializers here.

class SubCategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = SubCategory
        fields = ('id', 'name',)

class CategorySerializer(WritableNestedModelSerializer):
    children = SubCategorySerializer(many=True)
    class Meta:
        model = Category
        fields = ('id', 'name', 'children')

my AJAX call:

(function($) {
    $(function() {
        var selectField = $('#id_parent'),verified = $('#id_child');

        function toggleVerified(value) {
            if (value) {
                verified.show();
                console.log(value);
            } else {
                verified.hide();
            };
        };

        function getCookie(name) {
            var cookieValue = null;
            if (document.cookie && document.cookie !== '') {
                var cookies = document.cookie.split(';');
                for (var i = 0; i < cookies.length; i++) {
                    var cookie = cookies[i].trim();
                    if (cookie.substring(0, name.length + 1) === (name + '=')) {
                        cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                        break;
                    }
                }
            }
            return cookieValue;
        };
        var csrftoken = getCookie('csrftoken');

        function plugOptions(array, target){
            var children = array.children;
            //console.log(children);
            target.empty();
            target.append(new Option("---------", false))
            $.each(children, function(key){
                target.append(new Option(children[key].name, children[key].id));
            });
        };

        // show/hide on load based on pervious value of selectField
        toggleVerified(selectField.val());

        // show/hide on change
        selectField.change(function() {
            toggleVerified($(this).val());

            $.ajaxSetup({
                beforeSend: function(xhr, settings) {
                    xhr.setRequestHeader("X-CSRFToken", csrftoken);
                }
            });
            $.ajax({
                type: 'GET',
                url: '/my_api/categories/'+$(this).val(),
                contentType: "application/json; charset=utf-8",
                success: function(resp) {
                    console.log("success"),
                    plugOptions(resp, verified)
                },
                error: function(jqXHR, textStatus, errorThrown) {
                    console.log(jqXHR),
                    console.log(textStatus),
                    console.log(errorThrown)
               }
            });
        });
    });
})(django.jQuery);
Zack
  • 73
  • 10