Как показать разные строки в зависимости от текущего значения поля объекта - PullRequest
8 голосов
/ 10 ноября 2011

Учитывая модель с именем MainModel и RelatedModel, где последняя имеет поле ForeignKey до MainModel:

class MainModel(models.Model):
    name = models.CharField(max_length=50)
    type = models.BooleanField()

class RelatedModel1(models.Model):
    main = models.ForeingKey(MainModel):
    name = models.CharField(max_length=50)

class RelatedModel2(models.Model):
    main = models.ForeingKey(MainModel):
    name = models.CharField(max_length=50)

и соответствующие классы ModelAdmin:

class RelatedModel1InlineAdmin(admin.TabularInline):
    model = RelatedModel1

class RelatedModel2InlineAdmin(admin.TabularInline):
    model = RelatedModel2

class MainModel(admin.ModelAdmin):
    inlines = [RelatedModel1, RelatedModel2]

И это поведение по умолчанию, вы получаете две строки, по одной для каждой связанной модели. Вопрос в том, как полностью скрыть все строки при создании экземпляра MainModel (ModelAdmin s add_view) и показать строки для RelatedModel1, когда поле type поля MainModel instance True, и показывают строки для RelatedModel2, когда False.

Я собирался создать дескриптор для атрибута ModelAdmin.inline_instances, но я понял, что мне нужен доступ к редактируемому экземпляру объекта, но он передается как параметры.

Любая помощь?

Спасибо!

Ответы [ 5 ]

4 голосов
/ 29 января 2013

@ Yuji 'Tomita' Tomitayou идея была хорошая, у меня было то же самое, но однажды пытаясь, я понял, что вы также должны удалить конкретный ключ из self.inlines, потому что в change_view и add_view метод self.get_inline_instances(request) вызывается раньше get_formsets(). Поэтому я переместил обработку строк в метод get_form().

Вот как мне это удалось:

class SampleAdmin(ModelAdmin):
    inlines = []

    def get_inlines(self):
        return [SampleInline, SampleInline2]

    def get_form(self, request, obj=None, **kwargs):
        # due to django admin form fields caching you must 
        # redefine inlines on every `get_form()` call
        if (obj): self.inlines = self.get_inlines()
        for inline in self.inlines:
            # Here change condition based on your needs and manipulate
            # self.inlines as you like (remove, change, etc). 
            # I used inline.__name__ to detect if this is correct inline 
            # for my obj
            if obj.CONDITION:
                self.inlines.remove(inline)
        return super(SampleAdmin, self).get_form(request, obj, **kwargs)
3 голосов
/ 17 октября 2017

Я понимаю, что этот вопрос немного стар, и кодовая база немного изменилась; сейчас есть чистый момент, чтобы переопределить вещи: get_inline_instances. Вы можете сделать это:

class MainModelAdmin(models.ModelAdmin):
    inlines = [RelatedModel1InlineAdmin,RelatedModel2InlineAdmin]

    def get_inline_instances(self, request, obj=None):
        #Return no inlines when obj is being created
        if not obj:
            return []
        unfiltered = super(MainModelAdmin, self).get_inline_instances(request, obj)
        #filter out the Inlines you don't want
        if obj.type:
            return [x for x in unfiltered if isinstance(x,RelatedModel1InlineAdmin)]
        else:
            return [x for x in unfiltered if isinstance(x,RelatedModel2InlineAdmin)]
1 голос
/ 04 ноября 2017

Это сработало для меня, когда я искал ответ на ту же проблему в этом старом посте.Продолжая ответ на Darklow, я думаю, что вы можете просто полностью переопределить get_inline_instances и добавить дополнительную проверку, основанную на вашем типе.

  1. Добавить метод проверки логического типа в вашей модели

    class MainModel(models.Model):
    
        name = models.CharField(max_length=50)
    
        type = models.BooleanField()
    
        def is_type1(self):
    
           return type=="some value"
    
        def is_type2(self):
            return type=="some value"
    
  2. Добавить встроенную базу экземпляров при проверке типа - просто скопируйте и вставьте метод get_inline_insances из родительского класса в класс admin.ModelAdmin и добавьте блок if для проверки типа модели, как показано ниже

    class MyModelAdmin(admin.ModelAdmin):
    
        inlines = [RelatedModel1, RelatedModel2]
    
        def get_inline_instances(self, request, obj=None):
            inline_instances = []
            if not obj:
                return []
            for inline_class in self.inlines:
                inline = inline_class(self.model, self.admin_site)
                if request:
                    if not (inline.has_add_permission(request) or
                                inline.has_change_permission(request, obj) or
                                inline.has_delete_permission(request, obj)):
                        continue
                    if not inline.has_add_permission(request):
                        inline.max_num = 0
                if obj.is_type1() and isinstance(inline,RelatedModel1InlineAdmin):
                    inline_instances.append(inline)
                if obj.is_type2() and isinstance(inline,RelatedModel2InlineAdmin):
                    inline_instances.append(inline)
    
            return inline_instances
    
1 голос
/ 10 ноября 2011

Из заглядывания на contrib.admin.options.py Похоже, вы могли бы переопределить ModelAdmin.get_formsets. Обратите внимание, что сайт администратора заполняется self.inline_instances на __init__, поэтому вы, вероятно, хотите следовать, а не создавать экземпляры своих строк снова и снова. Я не уверен, насколько это дорого:)

def get_formsets(self, request, obj=None):
    if not obj:
        return [] # no inlines

    elif obj.type == True:
        return [MyInline1(self.model, self.admin_site).get_formset(request, obj)]

    elif obj.type == False:
        return [MyInline2(self.model, self.admin_site).get_formset(request, obj)]

    # again, not sure how expensive MyInline(self.model, self.admin_site) is. 
    # the admin does this once. You could instantiate them and store them on the 
    # admin class somewhere to reference instead.

Оригинальный администратор get_formsets использует генераторы - вы могли бы также более близко подражать оригиналу:

def get_formsets(self, request, obj=None):
    for inline in self.inline_instances:
        yield inline.get_formset(request, obj)
0 голосов
/ 07 апреля 2014

Вот фрагмент кода, который я написал, когда столкнулся с той же проблемой. Я думаю, это немного грубый стиль, но он очень проворен и должен подходить для всех случаев.

class MyModelAdmin(admin.ModelAdmin):
    def __init__(self, *args, **kwargs):
        super(MyModelAdmin, self).__init__(*args, **kwargs)
        self.inline_instances_hash = {}
        for inline_class in self.inlines:
            for inline_instance in self.inline_instances:
                if isinstance(inline_instance, inline_class):
                    break
            self.inline_instances_hash[inline_class] = inline_instance

    def get_inline_instance(self, inline_class):
        return self.inline_instances_hash[inline_class]

    def get_form(self, request, obj=None, **kwargs):
        if obj:
            self.inline_instances = []
            if self.CONDITION:
                self.inline_instances.append(self.get_inline_instance(
                    THE_INLINE_CLASS_I_WANT))
            #...
        else:
            self.inline_instances = self.inline_instances_hash.values()
...