Как вывести в Django admin поля из двух моделей?
Имеется 2 модели ClientComment и Client. Как я могу через (ClientAdmin) list_display вывести комментарии (последний) из ClientComment? Что-бы к каждому клиенту соответственно был выведен последний комментарий пользователя (допустим между столбцами номера телефона и датой рождений).
models.py
from django.db import models
from django_extensions.db.fields import RandomCharField
from phonenumber_field.modelfields import PhoneNumberField
from django.utils import timezone
from django.contrib.auth.models import User
from django.urls import reverse
from pytils import *
class ClientComment(models.Model):
client = models.ForeignKey('Client', on_delete=models.PROTECT, related_name="comments")
client_comment_body = models.TextField(verbose_name="Комментарий")
client_comment_user = models.ForeignKey(User, on_delete=models.PROTECT, verbose_name="Автор коментария", null=True,
blank=True)
client_comment_created_at = models.DateTimeField(auto_now_add=True, verbose_name="Дата создания")
client_comment_update_at = models.DateTimeField(auto_now=True, verbose_name="Обновлено")
client_comment_slug = RandomCharField(length=15, unique=True)
class Meta:
verbose_name = "Комментарий"
verbose_name_plural = "Комментарии"
ordering = ['-client_comment_created_at']
def __str__(self):
return 'Коментарий создан {} в {}'.format(self.client_comment_user, self.client_comment_created_at)
def get_absolute_url(self):
return reverse("client", kwargs={"slug": self.client_comment_slug})
class Client(models.Model):
NEW = "NW"
INTERESTING = "IN"
NOT_INTERESTING = "NI"
NOT_ANSWER = "NO"
FALSE_NUMBER = "FN"
CALL_BACK = "CB"
DEPOSIT = "DP"
OFF_TELEPHONE = "OF"
LAWYER = "LW"
CLIENT_STATUS_CHOICES = [
(NEW, 'Новый'),
(INTERESTING, 'Интересный'),
(NOT_INTERESTING, 'Не инетесный'),
(NOT_ANSWER, 'Не отвечает'),
(FALSE_NUMBER, 'Не верный номер'),
(CALL_BACK, 'Перезвонить'),
(DEPOSIT, 'Депозит'),
(OFF_TELEPHONE, 'Выключен'),
(LAWYER, 'Аналитик'),
]
last_name = models.CharField(max_length=45, verbose_name="Фамилия")
first_name = models.CharField(max_length=45, verbose_name="Имя")
middle_name = models.CharField(max_length=45, blank=True, verbose_name="Отчество")
email = models.EmailField(max_length=255, unique=True, verbose_name="Электронная почта")
phone = PhoneNumberField(unique=True, verbose_name="Номер телефона")
date_of_birth = models.DateField(null=True, blank=True, verbose_name="Дата рождения")
client_status = models.CharField(max_length=2, choices=CLIENT_STATUS_CHOICES, default=NEW,
verbose_name="Статус клиента")
client_created_at = models.DateTimeField(auto_now_add=True, verbose_name="Дата создания")
client_update_at = models.DateTimeField(auto_now=True, verbose_name="Обновлено")
is_processed = models.BooleanField(default=False, verbose_name="Обработано")
client_slug = RandomCharField(length=15, unique=True)
class Meta:
verbose_name = "Клиент"
verbose_name_plural = "Клиенты"
ordering = ["client_created_at"]
def get_full_name(self):
full_name = '%s %s' % (self.first_name, self.last_name)
return full_name.strip()
def get_absolute_url(self):
return reverse("client", kwargs={"slug": self.client_slug})
admin.py
from django.contrib import admin
from search_admin_autocomplete.admin import SearchAutoCompleteAdmin
from .models import Client, ClientComment
from import_export.admin import ImportExportActionModelAdmin
from import_export import resources
class ClientCommentInline(admin.TabularInline):
model = ClientComment
fk_name = "client"
class ClientResource(resources.ModelResource):
class Meta:
model = Client
class ClientAdmin(ImportExportActionModelAdmin):
inlines = [
ClientCommentInline,
]
resource_class = ClientResource
save_on_top = True
list_display = ['id', 'last_name', 'first_name', 'phone', 'client_created_at',
'client_status', "is_processed", ]
radio_fields = {"client_status": admin.VERTICAL}
list_display_links = list_display
list_filter = ("client_status", "is_processed",)
search_fields = ('last_name', 'first_name', 'phone',)
list_per_page = 50
list_max_show_all = 200
readonly_fields = ('client_created_at', "client_slug", "client_update_at",)
fields = (
('last_name', 'first_name', "middle_name"), ("email", 'phone'), "date_of_birth", ('client_created_at',
"client_update_at"),
('client_status', "is_processed"), "client_slug",
)
admin.site.register(Client, ClientAdmin)
Вот так выглядит админка.
Ответы (1 шт):
1 вариант. у модели добавь метод get_last_comment который будет возвращать последний комментарий и в list_display укажи этот метод
def get_last_comment(self):
last_comment = self.comments.first()
return last_comment.client_comment_body if last_comment else '-'
get_last_comment.short_description = 'Последний комментарий' # это, что бы в админке колонка называлась адекватно
минусы этого варианта в том, что для каждой записи Client django будет обращаться к БД
2 вариант. сделать все тоже самое в ModelAdmin
def get_last_comment(self, client):
return self.last_comments.get(obj.pk, '-)
get_last_comment.short_description = 'Последний комментарий'
но дополнительно нужно будет переопределить еще get_paginator, таким образом, что бы в нем одним запросом получить все последние комментарии по текущей выборке клиентов
def get_paginator(self, request, queryset, per_page, orphans=0, allow_empty_first_page=True):
paginator = super().get_paginator(request, queryset, per_page, orphans=0, allow_empty_first_page=True)
page_objects = paginator.page(int(request.GET.get('p'))+1)
# создать запрос, который будет вытаскивать последний комментарий для всех записей , находящихся в `page_objects` и сохранить их в `self.last_comments`
return paginator
