web-dev-qa-db-fra.com

Django admin - inline inlines (ou, édition de trois modèles à la fois)

J'ai un ensemble de modèles qui ressemblent à ceci:

class Page(models.Model):
    title = models.CharField(max_length=255)

class LinkSection(models.Model):
    page = models.ForeignKey(Page)
    title = models.CharField(max_length=255)

class Link(models.Model):
    linksection = models.ForeignKey(LinkSection)
    text = models.CharField(max_length=255)
    url = models.URLField()

et un admin.py qui ressemble à ceci:

class LinkInline(admin.TabularInline):
    model = Link
class LinkSectionInline(admin.TabularInline):
    model = LinkSection
    inlines = [ LinkInline, ]
class PageAdmin(admin.ModelAdmin):
    inlines = [ LinkSectionInline, ]

Mon objectif est d'obtenir une interface d'administration qui me permet de tout éditer sur une seule page. Le résultat final de cette structure de modèle est que les choses sont générées dans une vue + modèle qui ressemble plus ou moins à:

<h1>{{page.title}}</h1>
{% for ls in page.linksection_set.objects.all %}
<div>
    <h2>{{ls.title}}</h2>
    <ul>
         {% for l in ls.link_set.objects.all %}
        <li><a href="{{l.url}}">{{l.title}}</a></li>
         {% endfor %}
    </ul>
</div>
{% endfor %}

Je sais que l'astuce "inline-in-an-inline" échoue dans l'administrateur Django, comme je m'y attendais. Est-ce que quelqu'un connaît un moyen d'autoriser ce type d'édition de modèle à trois niveaux? Merci d'avance.

51
The_OP

Vous devez créer un formulaire et modèle personnalisé pour LinkSectionInline.

Quelque chose comme ceci devrait fonctionner pour le formulaire:

LinkFormset = forms.modelformset_factory(Link)
class LinkSectionForm(forms.ModelForm):
    def __init__(self, **kwargs):
        super(LinkSectionForm, self).__init__(**kwargs)
        self.link_formset = LinkFormset(instance=self.instance, 
                                        data=self.data or None,
                                        prefix=self.prefix)

    def is_valid(self):
        return (super(LinkSectionForm, self).is_valid() and 
                    self.link_formset.is_valid())

    def save(self, commit=True):
        # Supporting commit=False is another can of worms.  No use dealing
        # it before it's needed. (YAGNI)
        assert commit == True 
        res = super(LinkSectionForm, self).save(commit=commit)
        self.link_formset.save()
        return res

(Cela vient de mon esprit et n’est pas testé, mais il devrait vous inciter à aller dans la bonne direction.)

Votre modèle doit simplement rendre le formulaire et le formulaire.link_formset de manière appropriée. 

20
Matthew Marshall

Django-nested-inlines est construit pour cela. L'utilisation est simple.

from Django.contrib import admin
from nested_inlines.admin import NestedModelAdmin, NestedStackedInline, NestedTabularInline
from models import A, B, C

class MyNestedInline(NestedTabularInline):
    model = C

class MyInline(NestedStackedInline):
    model = B
    inlines = [MyNestedInline,]

class MyAdmin(NestedModelAdmin):
    pass

admin.site.register(A, MyAdmin)
4
Ian Price

Ma recommandation serait en fait de changer votre modèle. Pourquoi ne pas avoir un ForeignKey dans Link à LinkSection? Ou, si ce n'est pas OneToMany, peut-être un champ ManyToMany? L'interface d'administration générera cela gratuitement. Bien sûr, je ne le recommande pas si les liens n'ont logiquement rien à voir avec les sections de lien, mais peut-être qu'ils ont? Si ce n'est pas le cas, veuillez expliquer quelle est l'organisation envisagée. (Par exemple, 3 liens par section sont-ils fixes ou arbitraires?)

1
David Berger

Vous pouvez créer une nouvelle classe, similaire à TabularInline ou StackedInline, capable d’utiliser les champs intégrés elle-même.

Vous pouvez également créer de nouveaux modèles d'administrateur, spécifiquement pour votre modèle. Mais bien entendu, cela annule les fonctionnalités astucieuses de l'interface d'administration.

0
pvoosten