Страница администратора Django - создание ссылки на список объектов, отфильтрованных по внешнему ключу
Я изучаю Django прямо сейчас и все еще борюсь с этим. У меня есть две модели: Database и DatabaseUser, связанные между собой иностранным ключом. На странице администрирования я хочу добавить ссылку на список с базами данных, которая направляет меня к списку с пользователями базы данных конкретной базы данных.
Python: 3.9; Django: 4.2.13
# models.py
class Database(models.Model):
datenbank_name = models.CharField(max_length=300)
datenbank_art = models.CharField(max_length=100)
# ... (omitted some attributes)
def __str__(self):
return self.datenbank_name
class DatabaseUser(models.Model):
datenbank = models.ForeignKey(Database, on_delete=models.CASCADE)
user_name = models.CharField(max_length=300)
# ... (omitted some attributes)
def __str__(self):
return self.user_name
Мои модели администратора выглядят следующим образом:
# admin.py
class PasswordInput(forms.ModelForm):
passwort = forms.CharField(widget=forms.PasswordInput())
class DatabaseAdmin(admin.ModelAdmin):
form = PasswordInput
fieldsets = [
("Allgemein", {"fields": ["datenbank_name", "datenbank_art", "kunde", "host", "port"]}),
("Anmeldeinformationen", {"fields": ["user_name", "passwort"]}),
]
list_display = ["datenbank_name", "kunde", "datenbank_art", "port", "erreichbar"]
admin.site.register(Database, DatabaseAdmin)
class DatabaseUserAdmin(admin.ModelAdmin):
form = PasswordInput
fieldsets = [
("Datenbank", {"fields": ["datenbank"]}),
("Anmeldeinformationen", {"fields": ["user_name", "passwort"]}),
("Datenbank-Rechte", {"fields": ["rechte"]}),
]
list_filter = ["datenbank"]
list_display = ["user_name", "rechte", "datenbank"]
admin.site.register(DatabaseUser, DatabaseUserAdmin)
Теперь я попробовал несколько вещей и нашел 2 очень похожих вопроса на Stack Overflow. Однако я не смог заставить их работать в моем коде. Во втором случае создается ссылка не на список объектов, а на change-view (насколько я понял).
- Как перечислить все объекты, связанные с внешним ключом, в панели администратора Django?
- Ссылка в админке Django на объект с внешним ключом
Из первой темы (Как перечислить все объекты, связанные с внешним ключом, в панели администратора Django? ) Я попробовал все три решения. К сожалению, безуспешно:
Моя попытка первого решения:
def users(self):
return '<a href="/admin/db_manager/databaseuser/?datenbank__id__exact=%d">%s</a>' % (
self.datenbank_id, self.datenbank)
users.allow_tags = True
list_display = ["datenbank_name", "users", "kunde", "datenbank_art", "port", "erreichbar"]
Ошибки:
В моем коде
.self.datenbank_id
иself.datenbank
выделяются PyCharm, говоря "Unresolved attribute reference 'datenbank_id' for class 'DatabaseAdmin'"Более того, когда я пытаюсь открыть сайт администратора, он говорит "users() принимает 1 позиционный аргумент, но было задано 2"
.
Если я добавляю obj в функцию: def users(self, obj):
Ошибка либо "'DatabaseAdmin' object has no attribute 'datenbank_id'" (если используется self.datenbank_id
), либо 'Database' object has no attribute 'datenbank_id' (если используется obj.datenbank_id
)
Просто не могу понять.
Моя попытка второго решения:
# models.py
def get_admin_url(self):
return "/admin/db_manager/databaseuser/%d/" % self.id
# admin.py
def databases(self, obj):
html = ""
for obj in DatabaseUser.objects.filter(datenbank__id_exact=self.id):
html += '<p><a href="%s">%s</a></p>' % (obj.get_admin_url(), obj.title)
return html
databases.allow_tags = True
list_display = ["user_name", "rechte", "datenbank", "databases"]
Ошибки:
- PyCharm выделяет
self.id
объекты вDatabaseUser.objects.filter
иself.id
в функции get_admin_url(). - Аналогичные другие ошибки, как и в первом решении. Например: добавление
obj
в параметры функции
Затем я попробовал решение по второй ссылке ( Link in django admin to foreign key object) и..: Сюрприз. Я получил нечто, что, по крайней мере, загружается:
def users(self, obj:DatabaseUser):
link = reverse("admin:db_manager_databaseuser_change", args=[obj.pk])
return mark_safe(f'<a href="{link}">{escape(obj.__str__())}</a>')
users.allow_tags = True
list_display = ["datenbank_name", "users", "kunde", "datenbank_art", "port", "erreichbar"]
Проблема в том, что вторая тема о немного другой проблеме. В списочном представлении базы данных ссылка отображается, как я и хотел, в отдельной колонке, рядом с каждой базой данных. Однако эта ссылка направляет меня на форму изменений, где я могу внести изменения в атрибуты конкретного пользователя. А не в список, как я хотел.
Я попытался изменить link = reverse("admin:db_manager_databaseuser_change", args=[obj.pk])
на link = reverse("admin:db_manager_databaseuser_changelist", args=[obj.pk])
. Но это приводит к ошибке:
Reverse for 'db_manager_databaseuser_changelist' with arguments '(2,)' not found. 1 pattern(s) tried: ['admin/db_manager/databaseuser/\\Z']
Я думаю, что одна из главных проблем для меня здесь в том, что я начал работать с Django около 1 недели назад и до сих пор не могу понять, как все связано. Сейчас я немного в отчаянии. Я буду очень признателен за любую помощь, чтобы все исправить.
Приветствия.
Насколько я понял, вы хотите отобразить все Database
объекты, с колонкой, где ссылка будет перенаправлять на все DatabaseUsers
объекты, ссылающиеся на выбранный Database
. В админке Django представление, в котором вы можете увидеть таблицу всех экземпляров модели, называется changelist. Таким образом, переведя грамматику django admin, вы хотите получить ссылку в Database
changelist на DatabaseUser
changelist, отфильтрованную для соответствующих Database
.
Теперь вам нужна ссылка, поэтому давайте посмотрим, как строятся URL-адреса. URL списка изменений для любой модели yourmodel
в приложении yourapp
- это /admin/yourapp/yourmodel/. Фильтрация происходит и в URL, в синтаксисе Django: /admin/yourapp/yourmodel/?my_field__gte=5 выдает все yourmodel
экземпляры со значением myfield
больше или равным 5.
Вы уже почти пришли к первому решению. Как указывают ваши ошибки, ни Database
, ни DatabaseAdmin
не имеют атрибута datenbank_id
. Однако в вашей модели Database
есть (неявно созданный) атрибут id
, поэтому попробуйте:
from django.utils.safestring import mark_safe
def users(self, obj):
return mark_safe(
'<a href="/admin/db_manager/databaseuser/?datenbank__id__exact=%d">
See list of users
</a>' % (obj.id))
Опечатка mark_safe
нужна для того, чтобы результат интерпретировался как HTML, а не как текст (попробуйте без нее, чтобы увидеть разницу).
Насколько я понимаю, ваше (исправленное) второе решение даст вам список ссылок на каждый релевантный DatabaseUser
, вместо уникальной ссылки на список всех релевантных DatabaseUsers
. Не думаю, что это то, что вам нужно.
Третье решение: reverse("admin:db_manager_databaseuser_changelist")
даст вам правильную ссылку на список изменений DatabaseUser
, но я не думаю, что вы можете предоставить фильтр таким образом (args - это что-то другое). Вы все же можете использовать его, чтобы переписать предыдущее решение:
from django.utils.safestring import mark_safe
from django.urls import reverse
def users(self, obj):
link = reverse("admin:db_manager_databaseuser_changelist")
return mark_safe(
'<a href="' + link + '?datenbank__id__exact=%d">
See list of users
</a>' % (obj.id))
Надеюсь, это поможет.