one-to-many inline select with django admin

前端 未结 5 1643
名媛妹妹
名媛妹妹 2020-12-01 04:16

I have a standard many-to-one relationship set up. There are a bunch of fields, but for our purposes here, the relevant model is:

class Class(models.Model):
         


        
相关标签:
5条回答
  • 2020-12-01 04:38

    You could also run the student names trough a second model so that they are a ForeignKey. For example after the original code post add:

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

    Then add the inline to the admin like Luke Sneeringer said. You end up with a drop down list that you can select the student names from. Although, there is still the option to create new students.

    0 讨论(0)
  • 2020-12-01 04:43

    Here is "custom form" solution as Luke Sneeringer suggested. Anyway, I'm suprised by absence of out-of-the-box Django solution to this (rather natural and probably common) problem. Am I missing something?

    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
    
    0 讨论(0)
  • 2020-12-01 04:44

    If the intention is to have students exist independently from a class, and then be able to add or remove them from a class, then this sets up a different relationship:

    • Many students can be a part of one class.
    • One student can be a part of many classes

    In reality this is describing a Many-To-Many relationship. Simply exchanging

    class Student(models.Model):
        class = models.ForeignKey(Class) ...
    

    for

    class Student(models.Model):
        class = models.ManyToManyField(Class)... 
    

    will give the desired effect in Django admin immediately

    Django Many-to-many

    0 讨论(0)
  • 2020-12-01 04:50

    Probably, this will help: I used the described approach, but changed methods save and save_m2m in the following way:

    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 讨论(0)
  • 2020-12-01 05:00

    There is! You want InlineModelAdmin (see InlineModelAdmin documentation here)

    Sample code in brief:

    class StudentAdminInline(admin.TabularInline):
        model = Student
    
    class ClassAdmin(admin.ModelAdmin):
        inlines = (StudentAdminInline, )
    admin.site.register(Class, ClassAdmin)
    
    0 讨论(0)
提交回复
热议问题