Mamed
Mamed

Reputation: 95

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?

Upvotes: 3

Views: 309

Answers (1)

damon
damon

Reputation: 15128

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"],
            )
        }

Upvotes: 1

Related Questions