Repository: adamchainz/django-mysql

Issue:
ORDER BY FIELD(name, val, val, val...)
As suggested by @danielroseman.

As of Django 1.8, `order_by` can take expressions - https://docs.djangoproject.com/en/1.8/ref/models/querysets/#order-by , thus the `FIELD` function ( https://mariadb.com/kb/en/mariadb/field/ ) should be implemented, which can be used for getting results ordered in a specific fashion. This tutorial covers it: http://www.electrictoolbox.com/mysql-order-specific-field-values/ . 

It may also be useful to supply a shortcut that puts the unspecified values at the end, rather than the beginning, as noted in the second half of the tutorial. It could maybe be done without the double ordering forwards and backwards by using an IF, i.e. something like `IF(@tmp:=FIELD(x, val1, val2) > 0, @tmp, 99999999999)`. Might be faster that way.



Discussion:

Resolving patch:
diff --git a/django_mysql/models/functions.py b/django_mysql/models/functions.py
--- a/django_mysql/models/functions.py
+++ b/django_mysql/models/functions.py
@@ -91,6 +91,32 @@ def __init__(self, *expressions, **kwargs):
                                        output_field=output_field)
 
 
+class ELT(Func):
+    function = 'ELT'
+
+    def __init__(self, num, expressions):
+        value_exprs = []
+        for v in expressions:
+            if not hasattr(v, 'resolve_expression'):
+                v = Value(v)
+            value_exprs.append(v)
+
+        super(ELT, self).__init__(num, *value_exprs, output_field=CharField())
+
+
+class Field(Func):
+    function = 'FIELD'
+
+    def __init__(self, field, values, **kwargs):
+        values_exprs = []
+        for v in values:
+            if not hasattr(v, 'resolve_expression'):
+                v = Value(v)
+            values_exprs.append(v)
+
+        super(Field, self).__init__(field, *values_exprs)
+
+
 # Encryption Functions
 
 


Test patch:
diff --git a/tests/django_mysql_tests/test_functions.py b/tests/django_mysql_tests/test_functions.py
--- a/tests/django_mysql_tests/test_functions.py
+++ b/tests/django_mysql_tests/test_functions.py
@@ -7,15 +7,20 @@
 from django.test import TestCase
 
 from django_mysql.models.functions import (
-    Abs, ConcatWS, Ceiling, CRC32, Floor, Greatest, Least, MD5, Round, SHA1,
-    SHA2, Sign
+    Abs, ConcatWS, Ceiling, CRC32, ELT, Field, Floor, Greatest, Least, MD5,
+    Round, SHA1, SHA2, Sign
 )
 
 from django_mysql_tests.models import Alphabet
 
 
-@skipIf(django.VERSION <= (1, 8),
-        "Requires Database Functions from Django 1.8+")
+requiresDatabaseFunctions = skipIf(
+    django.VERSION <= (1, 8),
+    "Requires Database Functions from Django 1.8+"
+)
+
+
+@requiresDatabaseFunctions
 class ComparisonFunctionTests(TestCase):
 
     def test_greatest(self):
@@ -33,8 +38,7 @@ def test_least(self):
         self.assertEqual(ab.worst, -1)
 
 
-@skipIf(django.VERSION <= (1, 8),
-        "Requires Database Functions from Django 1.8+")
+@requiresDatabaseFunctions
 class NumericFunctionTests(TestCase):
 
     def test_abs(self):
@@ -93,8 +97,7 @@ def test_sign(self):
         self.assertEqual(ab.csign, -1)
 
 
-@skipIf(django.VERSION <= (1, 8),
-        "Requires Database Functions from Django 1.8+")
+@requiresDatabaseFunctions
 class StringFunctionTests(TestCase):
 
     def test_concat_ws(self):
@@ -154,9 +157,35 @@ def test_concat_ws_then_lookups_from_textfield(self):
         )
         self.assertEqual(ab.de, 'AAA:BBB')
 
+    def test_elt_simple(self):
+        Alphabet.objects.create(a=2)
+        ab = Alphabet.objects.annotate(elt=ELT('a', ['apple', 'orange'])).get()
+        self.assertEqual(ab.elt, 'orange')
+        ab = Alphabet.objects.annotate(elt=ELT('a', ['apple'])).get()
+        self.assertEqual(ab.elt, None)
+
+    def test_field_simple(self):
+        Alphabet.objects.create(d='a')
+        ab = Alphabet.objects.annotate(dp=Field('d', ['a', 'b'])).first()
+        self.assertEqual(ab.dp, 1)
+        ab = Alphabet.objects.annotate(dp=Field('d', ['b', 'a'])).first()
+        self.assertEqual(ab.dp, 2)
+        ab = Alphabet.objects.annotate(dp=Field('d', ['c', 'd'])).first()
+        self.assertEqual(ab.dp, 0)
+
+    def test_order_by(self):
+        Alphabet.objects.create(a=1, d='AAA')
+        Alphabet.objects.create(a=2, d='CCC')
+        Alphabet.objects.create(a=4, d='BBB')
+        Alphabet.objects.create(a=3, d='BBB')
+        avalues = list(
+            Alphabet.objects.order_by(Field('d', ['AAA', 'BBB']), 'a')
+                            .values_list('a', flat=True)
+        )
+        self.assertEqual(avalues, [2, 1, 3, 4])
+
 
-@skipIf(django.VERSION <= (1, 8),
-        "Requires Database Functions from Django 1.8+")
+@requiresDatabaseFunctions
 class EncryptionFunctionTests(TestCase):
 
     def test_md5_string(self):