Пользовательское поле администратора Django, извлекающее результат последнего связанного объекта по условию - PullRequest
0 голосов
/ 04 октября 2018

У меня есть три модели

class Customer(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    # some other fields, which don't matter

class ActivityStatus(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    name = models.CharField(max_length=5)

class Activities(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    status = models.ForeignKey(ActivityStatus)
    planned_execution_date = models.DateField()

также у меня есть две модели в admin

class CustomerAdmin(admin.ModelAdmin):
    fields = () # doesn't matter really
    list_display() # doesn't matter really

class ActivityAdmin(admin.ModelAdmin):
    fields = () # doesn't matter really
    list_display() # doesn't matter really

Я хочу добавить настраиваемое поле (last_activity_planned_date), которое будет отображаться и сортироваться в changelist_view of CustomerAdmin,Вот условие: - показать запланированную дату выполнения последней операции клиента, но только если эта операция имеет status_id = 2. В противном случае ничего не показывать

Пример 1

Клиент 1имеет 2 действия - последнее имеет status_id = 2 и запланировано_для выполнения = 2017-09-17

changelist_view:

| id | last_activity_planned_date |

| 1 | 2017-09-17 |

Пример 2

У клиента 1 есть 2 действия - у последнего есть status_id = 3 и планируемая_экспозиция_дата = 2017-09-27

| id| last_activity_planned_date |

| 1 | null |

Ответы [ 2 ]

0 голосов
/ 07 октября 2018

Используя этот ответ Мне удалось найти решение

from django.db.models.expressions import Subquery, OuterRef
ACTIVITY_STATUS_NEW = 2

def get_queryset(self, request):
    queryset = super().get_queryset(request)
    activities_per_customer = Activities.objects.filter(customer_id=OuterRef('pk'))
    latest_activity_per_customer = (activities_per_customer.order_by('pk', '-created')).distinct('pk')
    latest_active_activity_per_customer =(latest_activity_per_customer.filter(status_id=ACTIVITY_STATUS_NEW))
    queryset = queryset.annotate(_last_activity_planned_date=Subquery(latest_active_activity_per_customer.values('planned_execution_date')[:1]),)

    return queryset

, а затем

list_display = ('id', 'last_activity_planned_date')

def last_activity_planned_date(self, obj):
    return obj._last_activity_planned_date

last_activity_planned_date.short_description = "Optional description"
0 голосов
/ 04 октября 2018

Вы можете добавить настраиваемые поля в list_display:

class CustomerAdmin(admin.ModelAdmin):
    fields = () 
    list_display = ('last_activity_planned_date',)

    def last_activity_planned_date(self, obj):
        latest_activity = obj.activities.order_by('-planned_execution_date').first()
        if latest_activity and latest_activity.status_id == 2:
            return latest_activity.planned_execution_date
        else:
            return None

Однако сделать этот столбец можно было бы сделать довольно сложным.Даже в сыром SQL это кажется сложным запросом.В Django 2.1 была добавлена ​​поддержка выражений запросов в admin_order_field, но я все еще не могу найти приличный SQL-запрос для такой сортировки.

Решение состоит в том, чтобы реплицировать эту информацию в поле Customer.last_activity_planned_date(с помощью db_index=True для повышения эффективности заказа).

Если вы можете позволить, чтобы эта информация обновлялась только периодически, вы можете настроить cronjob для ее обновления каждые 15 мин, 1 ч или 1 день (в соответствии с вашими потребностями иресурсы, необходимые для обновления).

Если вам нужна эта информация, чтобы она всегда была точной и без задержек, вам придется обновлять ее каждый раз при создании, обновлении или удалении записи Activities.Существуют различные способы сделать это в зависимости от ваших потребностей, включая триггеры SQL и сигналы Django .Это работает довольно хорошо, но не на сто процентов уверен, поэтому даже если вы используете это решение, я бы посоветовал настроить ежедневный cronjob, чтобы убедиться, что все данные согласованы.

...