web-dev-qa-db-fra.com

sélection en ligne un-à-plusieurs avec Django admin

J'ai mis en place une relation plusieurs-à-un standard. Il y a un tas de domaines, mais pour nos besoins ici, le modèle pertinent est:

class Class(models.Model):
    name = models.CharField(max_length=128)

class Student(models.Model):
    class = models.ForeignKey(Class)
    name = models.CharField(max_length=128)
    address = models.CharField(max_length=128)
    # ...etc

J'ai créé un administrateur et cela fonctionne très bien. il a même automatiquement la possibilité de définir la classe lorsque je modifie un étudiant. Cependant, lorsque je vais créer/modifier une classe, tout ce que j'obtiens est une zone de saisie pour le nom.

Existe-t-il un moyen d'ajouter une boîte/un champ où les étudiants peuvent être ajoutés en tant que membres de la classe à partir de la page d'administration de la classe? Je peux créer un formulaire en ligne, mais c'est pour créer de nouveaux étudiants. J'ai déjà créé tous mes étudiants et je cherche juste une méthode rapide pour ajouter plusieurs étudiants existants à différentes classes.

53
MrGlass

Voici la solution "formulaire personnalisé" comme l'a suggéré Luke Sneeringer. Quoi qu'il en soit, je suis surpris par l'absence de solution prête à l'emploi Django à ce problème (plutôt naturel et probablement courant). Suis-je en train de manquer quelque chose?

from Django import forms
from Django.db import models
from Django.contrib import admin

class Foo(models.Model):
    pass

class Bar(models.Model):
    foo = models.ForeignKey(Foo)

class FooForm(forms.ModelForm):
    class Meta:
        model = Foo

    bars = forms.ModelMultipleChoiceField(queryset=Bar.objects.all())

    def __init__(self, *args, **kwargs):
        super(FooForm, self).__init__(*args, **kwargs)
        if self.instance:
            self.fields['bars'].initial = self.instance.bar_set.all()

    def save(self, *args, **kwargs):
        # FIXME: 'commit' argument is not handled
        # TODO: Wrap reassignments into transaction
        # NOTE: Previously assigned Foos are silently reset
        instance = super(FooForm, self).save(commit=False)
        self.fields['bars'].initial.update(foo=None)
        self.cleaned_data['bars'].update(foo=instance)
        return instance

class FooAdmin(admin.ModelAdmin):
    form = FooForm
35
zag

Il y a! Vous voulez InlineModelAdmin(voir la documentation InlineModelAdmin ici)

Exemple de code en bref:

class StudentAdminInline(admin.TabularInline):
    model = Student

class ClassAdmin(admin.ModelAdmin):
    inlines = (StudentAdminInline, )
admin.site.register(Class, ClassAdmin)
37
Luke Sneeringer

Vous pouvez également exécuter les noms des étudiants via un deuxième modèle afin qu'ils soient un ForeignKey. Par exemple, après le message de code d'origine, ajoutez:

class AssignedStudents(models.Model):
    assigned_to = models.ForeignKey(Class, on_delete=models.CASCADE)
    student = models.ForeignKey(Student, on_delete=models.CASCADE)

Ajoutez ensuite l'inline à l'administrateur comme l'a dit Luke Sneeringer. Vous vous retrouvez avec une liste déroulante dans laquelle vous pouvez sélectionner les noms des étudiants. Cependant, il existe toujours la possibilité de créer de nouveaux étudiants.

0
Kris O

Cela aidera probablement: j'ai utilisé l'approche décrite, mais j'ai changé les méthodes save et save_m2m de la manière suivante:

from Django import forms
from Django.db import models
from Django.contrib import admin

class Foo(models.Model):
     pass

class Bar(models.Model):
     foo = models.ForeignKey(Foo)

class FooForm(forms.ModelForm):
    class Meta:
        model = Foo

    bars = forms.ModelMultipleChoiceField(queryset=Bar.objects.all())

    def __init__(self, *args, **kwargs):
        super(FooForm, self).__init__(*args, **kwargs)
        if self.instance:
            self.fields['bars'].initial = self.instance.bar_set.all()

    def save_m2m(self):
        pass

    def save(self, *args, **kwargs):
        self.fields['bars'].initial.update(foo=None)
        foo_instance = Foo()
        foo_instance.pk = self.instance.pk
        # Copy all other fields.
        # ... #
        foo_instance.save()
        self.cleaned_data['bars'].update(foo=instance)
        return instance

class FooAdmin(admin.ModelAdmin):
    form = FooForm
0
Vladimir