Создание зависимых строк в администраторе Django

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

class ThingAdmin(admin.ModelAdmin):
    model = Thing

    if obj.date < today: #Something like that
        inlines = [MyInline,]

Проблема в том, что у меня нет доступа к экземпляру obj на этом уровне. Я попробовал переопределить get_formset(), но ничего не получил.

Просьба сообщить?

Ответ 1

Благодаря комментариям для изменения в 1.4. Моя реализация здесь также не была потоковой безопасностью, поэтому ее действительно нужно было удалить.

Так как get_formsets передается объект и вызывает get_inline_instances, мы можем изменить обе функции, чтобы действовать на объект.

Это должно работать:

class ThingAdmin(admin.ModelAdmin):
    model = Thing

    inlines = [inline]
    other_set_of_inlines = [other_inline]

    def get_inline_instances(self, request, obj=None):
        #                                    ^^^ this is new
        inline_instances = []

        if obj.date > datetime.date(2012, 1, 1):
            inlines = self.inlines
        else:
            inlines = self.other_set_of_inlines

        for inline_class in inlines:
            inline = inline_class(self.model, self.admin_site)
            if request:
                if not (inline.has_add_permission(request) or
                        inline.has_change_permission(request) or
                        inline.has_delete_permission(request)):
                    continue
                if not inline.has_add_permission(request):
                    inline.max_num = 0
            inline_instances.append(inline)
        return inline_instances

    def get_formsets(self, request, obj=None):
        for inline in self.get_inline_instances(request, obj):
            #                                           ^^^^^ this is new
            yield inline.get_formset(request, obj)

Ответ 2

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

class MyAdmin(admin.ModelAdmin):

    def add_view(self, request, form_url='', extra_context=None):
        self.inlines = [InlineA, InlineC]
        return super(MyAdmin, self).add_view(request, form_url, extra_context)

    def change_view(self, request, object_id, form_url='', extra_context=None):
        self.inlines = [InlineB, InlineC, InlineD]
        return super(MyAdmin, self).change_view(request, object_id, form_url, extra_context)

Это работает в Django 1.4.x.

Ответ 3

В последней версии Django вам необходимо переопределить ModelAdmin.get_formsets. например.

class MyAdmin(admin.ModelAdmin):

    def get_formsets(self, request, obj=None):
        if obj:
            for _ in super(MyAdmin, self).get_formsets(request, obj):
                yield _
        else:
            for inline in self.get_specific_inlines(request):
                yield inline.get_formset(request, obj)

Ответ 4

У меня была ситуация, когда мне нужно было показать Inline на основе сайта администратора, на котором вы были для данной истории.

Мне удалось получить динамические строки, работающие для Django 1.3, используя следующий код:

В бликах /admin.py

class HighlightInline(generic.GenericTabularInline):
    model = Highlight
    extra = 1
    max_num = 4
    fields = ('order', 'highlight')
    template = 'admin/highlights/inline.html'

class HighlightAdmin(admin.ModelAdmin):
    def regulate_highlight_inlines(self):
        highlights_enabled = Setting.objects.get_or_default('highlights_enabled', default='')
        highlight_inline_instance = HighlightInline(self.model, self.admin_site)
        highlight_found = any(isinstance(x, HighlightInline) for x in self.inline_instances)
        if highlights_enabled.strip().lower() == 'true':
            if not highlight_found:
                self.inline_instances.insert(0, highlight_inline_instance)
        else:
            if highlight_found:
                self.inline_instances.pop(0)
        print self.inline_instances

    def change_view(self, request, object_id, form_url='', extra_context=None):
        self.regulate_highlight_inlines()
        return super(HighlightAdmin, self).change_view(request, object_id)

    def add_view(self, request, form_url='', extra_context=None):
        self.regulate_highlight_inlines()   
        return super(HighlightAdmin, self).add_view(request, form_url, extra_context)

В рассказе /admin.py

class StoryAdmin(HighlightAdmin):

Следует отметить, что я не просто манипулирую встроенными классами (HighlightInline), а скорее меняю встроенные экземпляры (HighlightInline (self.model, self.admin_site)). Это связано с тем, что django уже построил список встроенных экземпляров на основе списка встроенных классов во время начальной конструкции класса admin.

Ответ 5

Я думаю, что лучший ответ в документации django: https://docs.djangoproject.com/en/1.9/ref/contrib/admin/

Поиск "get_inline_instances" Приведенный пример очень хорош и подробно описаны нюансы вызова.

Ответ 6

Я думаю, что самый простой способ взломать это - вызвать ваш пользовательский funciton в get_fields или get_fieldsets и так далее, просто установите self.inlines в пользовательскую функцию.

class XXXAdmin(admin.ModelAdmin):
    def set_inlines(self, request, obj):
        """ hack inlines models according current request.user or obj """
        self.inlines = []
        if request.user.is_superuser or request.user is obj.recorder:
            self.inlines = [AbcInline, ]

    def get_fields(self, request, obj=None):
        self.set_inlines(request, obj)  # NOTICE this line
        super(XXXAdmin, self).get_fields(request, obj)

Ответ 7

Самый простой способ сделать это сейчас - переопределить и вызвать супервызов для get_inline_instances.

class ThingAdmin(models.ModelAdmin):
    inlines = [MyInline,]

    def get_inline_instances(self, request, obj=None):
        unfiltered = super(ThingAdmin, self).get_inline_instances(request, obj)
        #filter out the Inlines you don't want
        keep_myinline = obj and obj.date < today
        return [x for x in unfiltered if not isinstance(x,MyInline) or keep_myinline]

Это ставит MyInline, когда вы этого хотите, а не когда нет. Если вы знаете единственную встроенную строку в своем классе, это MyInline, вы можете сделать ее еще проще:

class ThingAdmin(models.ModelAdmin):
    inlines = [MyInline,]

    def get_inline_instances(self, request, obj=None):
        if not obj or obj.date >= today:
            return []
        return super(ThingAdmin, self).get_inline_instances(request, obj)

Ответ 8

Начиная с Django 2.2.2 (текущая последняя версия на момент написания этой статьи), я бы использовал решение, предоставленное ранее @aggieNick02, которое переопределяет get_inline_instances показанные ниже.

class ThingAdmin(models.ModelAdmin):
    inlines = [MyInline,]

    def get_inline_instances(self, request, obj=None):
        if not obj or obj.date >= today: return []
        return super(ThingAdmin, self).get_inline_instances(request, obj)

Я публикую этот новый ответ, потому что по состоянию на 17 апреля 2019 года в этом коммите похоже, что рекомендуемый в будущем способ сделать это - переопределить метод get_inlines. Таким образом, в более поздних версиях решение этой проблемы может выглядеть как приведенный ниже код, который позволяет указывать различные наборы строк и использовать их в зависимости от условия.

class ThingAdmin(admin.ModelAdmin):
    model = Thing

    inlines = [inline]
    other_set_of_inlines = [other_inline]

    def get_inlines(self, request, obj):
        if obj.date > datetime.date(2012, 1, 1):
            return self.inlines
        else:
            return self.other_set_of_inlines