Search code examples
djangodjango-autocomplete-light

django-autocomplete-light dependant filter over 3 models


I have 3 models.

class FilterDefaultValues(BaseModel):
    value = models.CharField(max_length=255, null=True, blank=True)
    display_value = models.CharField(max_length=255, null=True, blank=True)

class Filter(BaseModel):
    name = models.CharField(max_length=255)
    default_value = models.ManyToManyField(FilterDefaultValues, blank=True)
    
class ProductFilter(models.Model):
    product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='filters')
    filter = models.ForeignKey(Filter, on_delete=models.CASCADE)
    value = models.ForeignKey(FilterDefaultValues, blank=True, on_delete=models.RESTRICT)

urls.py

class LinkedDataView(autocomplete.Select2QuerySetView):
    def get_queryset(self):
        qs = super(LinkedDataView, self).get_queryset()
        filter = self.forwarded.get('filter', None)

        if filter:
            qs = qs.filter(filter_id=filter)

        return qs


urlpatterns = [
    url(
        '^linked_data/$',
        LinkedDataView.as_view(model=ProductFilter),
        name='linked_data'
    ),
]

forms.py

class ProductFilterForm(forms.ModelForm):
    def clean_test(self):
        filter = self.cleaned_data.get('filter', None)
        value = self.cleaned_data.get('value', None)

        if value and filter and value.filter != filter:
            raise forms.ValidationError('Wrong owner for test')

        return value

    class Meta:
        model = ProductFilter
        fields = ('product', 'filter', 'value', 'value_type', 'product_images', 'is_variation')
        widgets = {
            'value': autocomplete.ModelSelect2(url='linked_data',
                                               forward=('filter',))
        }

    class Media:
        js = (
            'linked_data.js',
        )

What I want to is in the admin panel when the user selects a filter, then the value field must be populated with appropriate default values.

But now what I get is: When user selects filter then only productfilter items are populated here.

What I am missing here?


Solution

  • This is because your LinkedDataView.get_queryset() method is using the model ProductFilter, when it should be using the FilterDefaultValues model instead.

    If you look in your urls.py file, the LinkedDataView is initialized with model=ProductFilter. This means that the LinkedDataView.get_queryset() method will only return ProductFilter instances.

    Instead, you want to be able to show suggestions for FilterDefaultValues instances that are referenced (many-to-many) by Filter instances through the Filter.default_value attribute.

    To do this, you'll need to create a new autocomplete view that operates on FilterDefaultValues:

    urls.py

    class FilterDefaultValuesAutocompleteView(autocomplete.Select2QuerySetView):
        def get_queryset(self):
            qs = super().get_queryset()
            filter_id = self.forwarded.get("filter", None)
            if filter_id:
                qs = qs.filter(filter__id=filter_id)
            return qs
    
    
    urlpatterns = [
        url(
            "^filter_default_values_autocomplete/$",
            FilterDefaultValuesAutocompleteView.as_view(model=FilterDefaultValues),
            name="filter_default_values_autocomplete",
        ),
    ]
    

    And then update your form class:

    forms.py

    class ProductFilterForm(forms.ModelForm):
        class Meta:
            model = ProductFilter
            fields = ...
            widgets = {
                "value": autocomplete.ModelSelect2(
                    url="filter_default_values_autocomplete",
                    forward=["filter"],
                )
            }