53π
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
54π
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)
- [Django]-Getting 'DatabaseOperations' object has no attribute 'geo_db_type' error when doing a syncdb
- [Django]-Django: reverse accessors for foreign keys clashing
- [Django]-Convert Django Model object to dict with all of the fields intact
4π
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
- [Django]-CORS error while consuming calling REST API with React
- [Django]-Django models: mutual references between two classes and impossibility to use forward declaration in python
- [Django]-Django REST framework post array of objects
1π
In 2021, there is no straight solution to this problem.
What I did is using ManyToMany
with symmetrical=False
. Read more one Django official doc about symmetrical. Only in that case, you can select multiple entities in django-admin
parent_questions = models.ManyToManyField('self', related_name='parent_questions',
blank=True, symmetrical=False)
- [Django]-Django dump data for a single model?
- [Django]-Django won't refresh staticfiles
- [Django]-Django modelform NOT required field
1π
Something that works for me in Django 3.0.9
In models.py
from django import forms
from django.db import models
class Student(models.Model):
name = models.CharField(max_length=100)
course = models.ForeignKey(Course, on_delete=models.CASCADE, null=True, blank=True)
def __str__(self):
return self.name
class CourseForm(forms.ModelForm):
students = forms.ModelMultipleChoiceField(
queryset=Student.objects.all(), required=False
)
name = forms.CharField(max_length=100)
class Meta:
model = Student
fields = ["students", "name"]
def __init__(self, *args, **kwargs):
super(CourseForm, self).__init__(*args, **kwargs)
if self.instance:
self.fields["students"].initial = self.instance.student_set.all()
def save_m2m(self):
pass
def save(self, *args, **kwargs):
self.fields["students"].initial.update(course=None)
course_instance = Course()
course_instance.pk = self.instance.pk
course_instance.name = self.instance.name
course_instance.save()
self.cleaned_data["students"].update(course=course_instance)
return course_instance
In admin.py
from django.contrib import admin
from .models import Student, Course
class StudentAdmin(admin.ModelAdmin):
pass
class CourseAdmin(admin.ModelAdmin):
form = CourseForm
admin.site.register(Student, StudentAdmin)
admin.site.register(Course, CourseAdmin)
- [Django]-How to get the ID of a just created record in Django?
- [Django]-Manage.py runserver
- [Django]-Malformed Packet: Django admin nested form can't submit, connection was reset
0π
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.
- [Django]-Django form: what is the best way to modify posted data before validating?
- [Django]-Combining Django F, Value and a dict to annotate a queryset
- [Django]-Django Multiple Authentication Backend for one project
-3π
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]-Pylint "unresolved import" error in Visual Studio Code
- [Django]-Navigation in django
- [Django]-Getting a count of objects in a queryset in Django