1

I am using django-select2 with forms for creating a drop-down list, using ModelSelect2Widget.

My problem is that I filter on the field 'name' and several objects can have the same 'name' but different 'value' (see model description below). This leads to the same name appearing several times in the dropdown menu.

I would like to remove these duplicates. I tried to use .distinct('name') for selecting the queryset, but it doesn't seem to work. example of result obtained with ModelSelect2Widget

Below is the description of the code I use:

I have two models linked by a foreign key

models.py

class Who(models.Model):
    name = models.CharField(max_length=256)
    value = models.CharField(max_length=256)

    def __str__(self);
        return str(self.name)

class Data(models.Model):
    who = models.ForeignKey(Who)

And I use the form described here:

forms.py

from django_select2.forms import ModelSelect2Widget
...

class CreateDataForm(ModelForm):
    class Meta:
        model = Data
        fields = ('who',)
        widgets = {'who': ModelSelect2Widget(
            queryset=Who.objects.all().distinct('name'),
            search_fields=['name_icontains']
        )}

Does anyone know how I could remove these duplicates?

fduf
  • 33
  • 7
  • What `Who` model do you need? What is the criteria to choice one ? Why they are more than one `Station to depart` with the same name? – dani herrera Nov 18 '19 at 11:16
  • There are several station de depart (departure station) with the same name because several bus stops can have the same name but be placed at different locations (for instance one at one side of the road, the other at the other side) – fduf Nov 18 '19 at 13:41
  • Did you consider, maybe, do you have a bad design? It looks do you need to split `Depart` in two models: `Depart` and `DepartLocation`. Then reference `Depart` instead `DepartLocation`. Looking your [post](https://stackoverflow.com/a/58916199/842935) it seems you are 'out of the way' and writing anti-patterns on your code. – dani herrera Nov 18 '19 at 14:26
  • yes, this is clearly possible ! (however, these are not the real models, I tried to simplify as much possible to go straight to the point for the question) I still need to learn how to build clean model architecture – fduf Nov 18 '19 at 15:54

2 Answers2

1

I finally found the source of the problem.

In django_select2 ModelSelect2Mixin, the function filter_queryset which is used to filter the values to show on the dropdown menu ends by "return queryset.filter(select).distinct()". This problem is that this .distinct() prevails on the one from the queryset (.distinct("name"))

In order to solve my problem, I had to override the filter queryset function, and remove the .distinct():

class ModelSelect2WidgetWithoutDistinct(ModelSelect2Widget):
    """In dropdown list, shows objects from queryset without the initial .distinct()."""

    def filter_queryset(self, request, term, queryset=None, **dependent_fields):
        """Same function as in select2 ModelSelect2Mixin except last line."""
        if queryset is None:
            queryset = self.get_queryset()
        search_fields = self.get_search_fields()
        select = Q()
        term = term.replace('\t', ' ')
        term = term.replace('\n', ' ')
        for t in [t for t in term.split(' ') if not t == '']:
            select &= reduce(lambda x, y: x | Q(**{y: t}), search_fields,
                                Q(**{search_fields[0]: t}))
        if dependent_fields:
            select &= Q(**dependent_fields)

        return queryset.filter(select)  # initially queryset.filter(select).distinct()

fduf
  • 33
  • 7
0
# You can define queryset like this

class CreateDataForm(ModelForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['who'].queryset = Who.objects.distinct()

    class Meta:
        model = Data
        fields = ('who',)
Mukul Kumar
  • 2,033
  • 1
  • 7
  • 14
  • Thank you for your answer. However, I couldn't make this solution work neither. I just found that the problem is that in django_select2 ModelSelect2Mixin, the function filter_queryset which is used to filter the values to show on the dropdown menu ends by "return queryset.filter(select).distinct()". This ".distinct()" overrides the one used to define the queryset. – fduf Nov 18 '19 at 13:37