Django detecting not needed changes
<p>django 1.10, py 3.5</p>

<p>Here's my Enum-like class:</p>

<pre><code>@deconstructible
class EnumType(object):
    @classmethod
    def choices(cls):
        attrs = [i for i in cls.__dict__.keys() if i[:1] != '_' and i.isupper()]
        return tuple((cls.__dict__[attr], cls.__dict__[attr]) for attr in attrs)

    def __eq__(self, other):
        return self.choices() == other.choices()
</code></pre>

<p>Here's an example of the class:</p>

<pre><code>class TransmissionType(EnumType):
    TRANSMISSION_PROGRAM = 'TRANSMISSION_PROGRAM'
    INFO_PROGRAM = 'INFO_PROGRAM'
    SPORT_PROGRAM = 'SPORT_PROGRAM'
</code></pre>

<p>Here's how i use it on a model:</p>

<pre><code>type = models.TextField(choices=TransmissionType.choices(), db_index=True, default=None)
</code></pre>

<p>I think I made everything right according to the current <a href="https://docs.djangoproject.com/en/1.10/topics/migrations/#adding-a-deconstruct-method" rel="nofollow">django deconstruct docs</a> but apparently makemigration script still creates everytime migration like this:</p>

<pre><code>operations = [
    migrations.AlterField(
        model_name='transmission',
        name='type',
        field=models.TextField(choices=[('TRANSMISSION_PROGRAM', 'TRANSMISSION_PROGRAM'), ('INFO_PROGRAM', 'INFO_PROGRAM'), ('SPORT_PROGRAM', 'SPORT_PROGRAM')], db_index=True, default=None),
    ),
]
</code></pre>

<p>Edit1:
expected behaviour - when class members does not change the generated migaration should not include AlterField</p>

<p>Dictionaries have an arbitrary order, so your tuple has an arbitrary order as well. Especially on Python 3.3+ the order is likely to change because it uses a <a href="http://stackoverflow.com/a/27522708/2615075">random hash seed</a>. As such, the order of the tuple is different as well, and tuples with the same items but a different order don't compare equal. Django detects this change and creates a new migration.</p>

<p>To fix this, simply sort the keys before constructing the tuple:</p>

<pre><code>@deconstructible
class EnumType(object):
    @classmethod
    def choices(cls):
        attrs = [i for i in cls.__dict__.keys() if i[:1] != '_' and i.isupper()]
        return tuple((cls.__dict__[attr], cls.__dict__[attr]) for attr in sorted(attrs))

    def __eq__(self, other):
        return self.choices() == other.choices()
</code></pre>

python django enums migration