Страница администратора 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? ) Я попробовал все три решения. К сожалению, безуспешно:

Моя попытка первого решения:

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))

Надеюсь, это поможет.

Вернуться на верх