Как отсортировать набор вопросов по выбору атрибутов

У меня есть модель на Django (5.0.4) с выбираемым месяцем и годом, например, так:

class Item(models.Model):
    MONTHS_CHOICES = [
        ('JANUARY', 'January'),
        ('FEBRUARY', 'February'),
        ('MARCH', 'March'),
        ('APRIL', 'April'),
        ('MAY', 'May'),
        ('JUNE', 'June'),
        ('JULY', 'July'),
        ('AUGUST', 'August'),
        ('SEPTEMBER', 'September'),
        ('OCTOBER', 'October'),
        ('NOVEMBER', 'November'),
        ('DECEMBER', 'December')
    ]
    month = models.CharField(max_length=9, choices=MONTHS_CHOICES, default="JANUARY",  db_index=True)
    year = models.IntegerField(default=0)
    (...)

Если я попытаюсь упорядочить элементы по месяцам и годам Item.objects.all().order_by("-year", "month"). Логически месяцы будут упорядочены в алфавитном порядке.

Что я могу сделать, чтобы предметы были заказаны в том же порядке, в котором у меня есть месяцы на MONTHS_CHOICES?

Вариант B - просто переделать модели, включив в них DateField только год и месяц, как этот вопрос здесь, и упорядочить их по этому полю

Я также пробовал решения на этой странице, но мне ничего не помогло.

У меня еще не было возможности проверить это на практике, но я предполагаю использовать название месяца в качестве "ключа" в словаре, где соответствующее "значение" - это порядковое значение соответствующего месяца (например, Jan -> 1, Feb -> 2, и т.д.)

Вот мои мысли о том, как это может выглядеть:

class Item(models.Model):
    MONTHS_CHOICES = [
        ('JANUARY', 'January'),
        ('FEBRUARY', 'February'),
        ('MARCH', 'March'),
        ('APRIL', 'April'),
        ('MAY', 'May'),
        ('JUNE', 'June'),
        ('JULY', 'July'),
        ('AUGUST', 'August'),
        ('SEPTEMBER', 'September'),
        ('OCTOBER', 'October'),
        ('NOVEMBER', 'November'),
        ('DECEMBER', 'December')
    ]
    MONTHS_ORDER = {
        'JANUARY': 1,
        'FEBRUARY': 2,
        'MARCH': 3, 
        'APRIL': 4, 
        'MAY': 5,
        'JUNE': 6,
        'JULY': 7, 
        'AUGUST': 8, 
        'SEPTEMBER': 9, 
        'OCTOBER': 10,
        'NOVEMBER': 11,
        'DECEMBER': 12
    }
    month = models.CharField(max_length=9, choices=MONTHS_CHOICES, default="JANUARY",  db_index=True)
    month_numeric = models.IntegerField(editable=False, db_index=True)
    year = models.IntegerField(default=0)

    def (self, *args, **kwargs):
        self.month_numeric = self.MONTHS_ORDER[self.month]
        super().save(*args, **kwargs)

ПРИМЕЧАНИЕ: Если у вас уже есть данные в вашей БД, вам нужно будет отредактировать ваш файл миграции, чтобы установить это автоматически для объектов, уже хранящихся в вашей БД. Опять же, это не проверялось, но я предполагаю что-то вроде следующего:

def populate_month_numeric(apps, schema_editor):
    Item = apps.get_model('YOUR APP NAME - CHANGE THIS!', 'Item')
    for item in Item.objects.all():
        item.month_numeric = Item.MONTHS_ORDER[item.month]
        item.save() 

Затем, запустив makemigrations, откройте созданный файл миграции и в поле Migration класса operations добавьте в конце следующее:

migrations.RunPython(populate_month_numeric)

Затем, конечно же, выполните функцию migrate, и ваши запросы должны быть такими, как показано ниже:

items = Item.objects.all().order_by("-year", "month_numeric")

Как я уже сказал, сам я еще не запускал этот код, но этот аспект отображения 'Key' -> 'Value' - это то, как я решал подобные проблемы в прошлом!

Если у вас есть свободный выбор в поле DB, то не используйте CharField с названием месяца. Используйте IntegerField с вариантами выбора:

MONTHS_CHOICES = [
    ( 1, 'January'),
    ( 2, 'February'),
    ( 3, 'March'),
    ...
]

Числа произвольные, если по какой-то причине (налоговые годы в Великобритании!) вы хотите, чтобы апрель был 1, а март - 12, закодируйте это.

Для шаблонов, отображающих это поле, или кода Python, где требуется название месяца, используйте

{{ item_instance.get_month_display }}

or в Python

month_name = item_instance.get_month_display()
Вернуться на верх