pythonpython-3.xdjangodjango-admindjango-admin-actions

Django admin page copy ManyToMany related field from one model to another


I am trying to write a scheduling app. Here there are multiple workers and I create a group of workers. The Group is used as a template to create schedule.

When I create a Schedule object I want to take input of a Group object and copy the members from the Group object to the Schedule object. I don't want to give a ForeignKey to the Group object itself because the workers might change for a single day, whereas the group should be left intact as a template.

models.py

class Worker(models.Model):
    name = models.CharField(max_length=30)
    email = models.EmailField()

class Group(models.Model):
    name = models.CharField(max_length=30)
    members = models.ManyToManyField(Worker, blank=True)

class Schedule(models.Model):
    start_time = models.DateTimeField()
    end_time = models.DateTimeField()
    date = models.DateField()
    members = models.ManyToManyField(Worker, blank=True)

I understand this can be done easily with views and html templates. But I am trying to do the same under admin page. I have a form for Schedule and registered it in admin.py

forms.py

class CreateScheduleForm(forms.ModelForm):
    group = ModelChoiceField(queryset=Group.objects.all())

    class Meta:
        model = Schedule
        fields = ( 'date', 'start_time', 'end_time')

admin.py

@admin.register(Schedule)
class ScheduleAdmin(admin.ModelAdmin):
    form = CreateScheduleForm

I don't quite understand how to receive this input and process the Group object instance and copy the members. Any help is much appreciated.

Thanks in advance.


Solution

  • I think there are a number of ways to approach this. One approach could be overriding the save_model method in your ScheduleAdmin:

    @admin.register(Schedule)
    class ScheduleAdmin(admin.ModelAdmin):
        form = CreateScheduleForm
    
        def save_model(self, request, obj, form, change):
            
            sched = form.save()
            group = form.data.get('group')
            for worker in Group.objects.get(pk=group).members.all():
                sched.members.add(worker)
    

    Please be aware that this is only a very quick and dirty example, adding the members from the selected group each time you save the object. If you modify an existing schedule the obj arg in the method gives you that instance and the change arg would be True. You might want to implement some additional checks and remove existing members before you add the members of the group in cases where you modify an existing schedule.

    More details on the admin site, exisiting methods and what you can do can be found in the django docs.