преобразовать поле в целое число перед использованием в админке, упорядочив по этому полю - PullRequest
0 голосов
/ 03 февраля 2020

На данный момент, в моем админе, я заказываю YelpCompanys по annual_revenue. Некоторые годовые доходы содержат символы, которые не являются числами. У меня есть функция в модели, которая преобразует annual_revenue s в целые числа. Как я могу использовать эту функцию в моей функции заказа в админке? Любая помощь приветствуется - спасибо заранее. Вот мой код:

models.py

class YelpCompany(models.Model):
    title = models.CharField(max_length=255)
    url = models.URLField(max_length=255, unique=True)
    messaged = models.BooleanField(default=False)
    date_created = models.DateTimeField(auto_now_add=True)
    city = models.CharField(max_length=255, blank=True, null=True)
    company_type = models.CharField(max_length=255,blank=True,null=True)
    not_available = models.BooleanField(default=False)
    annual_revenue = models.CharField(max_length=255, blank=True,null=True)

    def __str__(self):
        return self.title

    def revenue_to_int(self):
        try:
            return int(self.annual_revenue)
        except Exception as e:
            if 'less than' in self.revenue:
                return self.revenue.split('less than ')[1].replace('$','').strip()
            elif 'million' in self.revenue:
                return self.revenue.split('to')[0].replace('$','').strip()
            else:
                return 0

admin.py

@admin.register(YelpCompany)
class YelpCompanyAdmin(admin.ModelAdmin):
    def get_queryset(self, request):
        qs = super().get_queryset(request)
        return qs.filter(messaged=False,not_available=False)
    list_display = ('title','url','messaged','city','annual_revenue','not_available')
    ordering = ('annual_revenue',)

1 Ответ

0 голосов
/ 03 февраля 2020

@ Arakkl_Abu ответ правильный. Я немного поиграл и попытался перестроить ваш метод python с помощью функций базы данных. Это может быть что-то похожее на это:

#admin.py

from django.db.models import Case, Value, When, IntegerField, FloatField, F
from django.db.models.functions import Substr, StrIndex, Cast, Replace, Length

class YelpCompanyAdmin(admin.ModelAdmin):

    list_display = ('title','url','messaged','city','annual_revenue','not_available', 'show_annual_revenue1')

    def get_queryset(self, request):
        qs = YelpCompany.objects.filter(messaged=False,not_available=False).annotate(
            field_case=Case(
                When(annual_revenue__contains="less than", then=Value(1)),
                When(annual_revenue__contains="million", then=Value(2)),
                When(annual_revenue__iregex=r'^[a-zA-Z]+/$', then=Value(3)),
                When(annual_revenue=None, then=Value(3)),
                When(annual_revenue=r'^[/w]+/$', then=Value(3)),
                default=Value(4),
                output_field=IntegerField()

            )
        ).annotate(
            index_nr=Case(
                When(field_case=1, then=StrIndex('annual_revenue', Value('less than')) + Length(Value('less than'))),
                When(field_case=2, then=StrIndex('annual_revenue', Value('up to')) + Length(Value('up to'))),
                When(field_case=3, then=Value(0)),
                default=Value(-1),
                output_field=IntegerField()
            )
        ).annotate(
            annual_revenue1=Case(
                When(index_nr__gt=0, then=Cast(Replace(
                    Substr(F('annual_revenue'), F("index_nr")), Value('$'), Value('')), 
                    output_field=FloatField())),
                When(index_nr=0, then=Value(0)),
                default=Cast(F('annual_revenue'), output_field=FloatField())
            )
        )

        return qs

    def show_annual_revenue1(self, inst):
        return inst.annual_revenue1

    show_annual_revenue1.admin_order_field = 'annual_revenue1'

Аннотации создают новую аннотацию annual_revenue1, содержащую только цифру c значения year_revenue. Это можно использовать для заказа. У ModelAdmin выше есть новый столбец на экране списка, который называется show_annual_revenue1, который используется для упорядочивания по умолчанию.

Пост, посвященный использованию аннотаций для упорядочения, равен здесь .

Несколько слов объяснения:

Первая аннотация 'Case' сортирует записи 'year_revenue' по группам: 1. Поле содержит «меньше чем», 2. Поле содержит «миллион», 3. Поле содержит буквы, None или пусто, 4. Если ничего из вышеперечисленного не применимо, предполагается, что поле содержит значение цифры c. Возможно, вам придется адаптировать его к вашему особому варианту использования, если применяются другие варианты.

Во второй аннотации 'Case' мы находим индекс для извлечения подстроки, аналогично вашей команде split(). Поля, которые не содержат действительного значения цифр c, определенного в первой аннотации Case, или значения чисел c помечены index_nr '0' или '-1'.

В третьей аннотации В блоке мы извлекаем подстроку Numberri c или просто возвращаем 0 (если поле не содержит действительного значения NUMERI c) или возвращаем значение поля, если оно может использоваться как есть. Возвращенные значения приводятся к значениям нумерации c, поэтому мы получаем правильную сортировку.

Подход 2

Недостаток подхода, приведенного выше, заключается в том, что он не очень гибкий и довольно "лонги sh". Другим подходом может быть удаление всех строк в начале:

#admin.py

from django.db.models import Case, Value, When, F, FloatField
from django.db.models.functions import Cast, Replace

#admin.py

from django.db.models import Case, Value, When, IntegerField, FloatField, F
from django.db.models.functions import Substr, StrIndex, Cast, Replace, Length

class YelpCompanyAdmin(admin.ModelAdmin):

    list_display = ('title','url','messaged','city','annual_revenue','not_available', 'show_annual_revenue1')

    def get_queryset(self, request):
        replace_strings = ['million', 'milion', 'up to', '$', 'less than', 'False']

        qs = YelpCompany.objects.filter(messaged=False,not_available=False)

        qs = qs.annotate(
            annual_revenue1 = F('annual_revenue')
        )

        for s in replace_strings:
            qs = qs.annotate(
                annual_revenue1 = Replace(F('annual_revenue1'), Value(s), Value(''))
            )

        qs = qs.annotate(
            annual_revenue1 = Case(
                When(annual_revenue1=None, then=Value(0.0)),
                default=Cast(Trim(F('annual_revenue1')), FloatField())
            )
        )

        return qs.order_by('annual_revenue1')

    def show_annual_revenue1(self, inst):
        return inst.annual_revenue1
    show_annual_revenue1.admin_order_field = 'annual_revenue1'

Это дает гибкость для добавления любых строк, которые могут появиться в списке. Вы можете создать свой список строк замены динамически из исходного набора запросов, если вам нравится:

replace_strings2 = []

for q in qs.values_list('annual_revenue', flat=True):
    if not q is None:
        s = ''.join([x for x in q if not q.isdigit()])
        replace_strings2.extend(s.split())

replace_strings = []
for s in replace_strings2:
    try:
        float(s)
    except ValueError:
        replace_strings.append(s)
...