django-modeltranslation icon indicating copy to clipboard operation
django-modeltranslation copied to clipboard

Translated model SelectField not sorted in the correct language

Open EParisot opened this issue 3 years ago • 7 comments

Hi, I have a Select with objects that are countries, so model is called Country with a "name" and "code" attributes. Names are perfectly translated but their order in a select box rendered from a model form, sorted alphabetically in english (default language), whatever I select in languages (except english of course)...

Code from models.py :

class Country(models.Model):
	code = models.CharField(max_length=4, unique=True)
	name = models.CharField(max_length=255)

	def __str__(self):
		return (self.name)

	class Meta:
		ordering = ['name']

Code from translations.py

class CountryTranslationOptions(TranslationOptions):
    fields = ('name',)
translator.register(Country, CountryTranslationOptions)

Code from forms.py

class CountriesSelectForm(forms.Form):
	country = forms.ModelChoiceField(queryset=Country.objects.all().order_by("name"),
			                widget=forms.Select(),
			                label=_('Country'),
			                required=False)
        )

EParisot avatar Jun 12 '21 15:06 EParisot

Hi @EParisot, did you find a solution to this issue?

ant-louis avatar Apr 03 '22 13:04 ant-louis

Nop, we decided it was not vital but the problem is still there...

EParisot avatar Apr 03 '22 13:04 EParisot

@antoiloui in fact I just came accross it today, and fixed it by sorting the select options on the client side. See following js code (usage: sortSelect(document.getElementById("select_id"));)

    function sortSelect(selElem) {
		var selected = selElem.options[selElem.selectedIndex].value;
		var tmpAry = new Array();
		for (var i=0;i<selElem.options.length;i++) {
			tmpAry[i] = new Array();
			tmpAry[i][0] = selElem.options[i].text;
			tmpAry[i][1] = selElem.options[i].value;
		}
		tmpAry.sort();
		if (selElem.options.length > 0) {
			selElem.options[0] = null;
		}
		for (var i=0;i<tmpAry.length;i++) {
			var op = new Option(tmpAry[i][0], tmpAry[i][1]);
			selElem.options[i] = op;
			if (tmpAry[i][1] == selected) {
				selElem.selectedIndex = i;
			}
		}
		return;
	}

EParisot avatar Apr 10 '22 17:04 EParisot

Amazing, thanks for sharing your solution @EParisot!

ant-louis avatar Apr 11 '22 08:04 ant-louis

For future reference, one way to do this server-side is to Coalesce the translated fields together. Here is a gist showcasing this approach:

https://gist.github.com/LeMinaw/cdc9c92113f5d5a0af067c5846e90f32

LeMinaw avatar Aug 22 '22 12:08 LeMinaw

@LeMinaw thank you for showing how it can be done.

Does it mean that using order_by() with a translated field is not supported by django-modeltranslation if not all your DB entries have a translation for the field?

For example all the entries have a value for english (default language), but not all have a french translation. Is it possible to get a correct ordering for french, i.e. the ordering is based on french and if not available, it defaults to english?

If I understood correctly, this is what your code achieves, but I'm confused as why it wouldn't be the default behaviour.

malikbeytrison avatar Oct 04 '23 18:10 malikbeytrison

Yes, what you describe is roughly what the above code does.

To me modeltranslation not ordering querysets this way by default sound reasonable: ordering and querysets annotations have a performance cost, and it is clearly stated in the docs that the status of original translated fields is unspecified.

Now one can discuss if the library should provide a way to perform this kind of ordering through the translated model manager.

LeMinaw avatar Oct 08 '23 18:10 LeMinaw