django admin - select reverse foreign key relationships (not create, I want to add available)
Asked Answered
B

4

19

Lets say I have a model School and another model Student.

class Student(models.Model):
   school = models.ForeignKey(School)
   name = models.CharField(max_length=100)

When a school is clicked in admin, then a new page appears showing school model fields and values.

I also want to select the already available list of students in that page itself.

Inlines is different, they will allow the ability to create and edit new records(student) belonging to that school. But I don't want that, lets assume there are already many student records available. I should be able to select them in admin from that school model page.

Biparous answered 29/8, 2013 at 7:58 Comment(0)
O
15
class SchoolAdminForm(forms.ModelForm):
    students = forms.ModelMultipleChoiceField(
        queryset=Student.objects.all(),
        widget=FilteredSelectMultiple(verbose_name='students', is_stacked=False))

    class Meta:
        model = School
        fields = ['your_school_fields_go_here']

    def __init__(self, *args, **kwargs):
        super(SchoolAdminForm, self).__init__(*args, **kwargs)
        if self.instance:
            # fill initial related values
            self.fields['students'].initial = self.instance.student_set.all()

class SchoolAdmin(admin.ModelAdmin):
   form = SchoolAdminForm

   def save_model(self, request, obj, form, change):
       super().save_model(request, obj, form, change)
       original_students = set(obj.student_set.values_list("id", flat=True))
       current_students = set(map(lambda x: x.id, form.cleaned_data['students']))
       if original_students != current_students:
           add_to_school = current_students - original_students
           Student.objects.filter(id__in=add_to_school).update(school_id=obj.id)
           remove_from_school = original_students - current_students
           Student.objects.filter(id__in=remove_from_school).update(school_id=None)
Output answered 21/2, 2017 at 18:13 Comment(0)
V
2

I did what felix suggested. the only problem is that it gives a error like this:

ValueError: Unsaved model instance <School: disscount_None> cannot be used in an ORM query.

and the reason I found was that you can not change the foreign key in student's object BEFORE saving your current School model. because there is nothing to add to fk attribute yet! so use felix solution but in save_model function use the obj.save() before "for"

   def save_model(self, request, obj, form, change):
       original_students = obj.student_set.all()
       new_students = form.cleaned_data['students']
       remove_qs = original_students.exclude(id__in=new_students.values('id'))
       add_qs = new_students.exclude(id__in=original_students.values('id'))


       obj.save()   

       for item in remove_qs:
               obj.student_set.remove(item)
       for item in add_qs:
               obj.student_set.add(item)
           
Verditer answered 6/11, 2020 at 16:35 Comment(0)
B
0

You can disable the inline 'add' permission. See InlineModelAdmin

Blackcock answered 29/8, 2013 at 10:21 Comment(0)
H
-1

Do you mean that for a given School instance you want to be able to get a list of all the students related to that school?

In which case you use the related_name attribute of the ForeignKey relationship you specified. You haven't defined the related_name where you do:

school = models.ForeignKey(School)

which is fine, it just uses the default related name which is the name of the child class (student) followed by _set

so for your school instance:

school = School.objects.get(pk=1)
students = school.student_set.all()  # or .filter() or .exclude() etc

then you can pass that student queryset into your template.

Horoscopy answered 29/8, 2013 at 8:9 Comment(1)
I know that. I want to do that in admin. So, there must be a simple widget for that.Biparous

© 2022 - 2024 — McMap. All rights reserved.