Search code examples
djangoformsdjango-adminadmininlines

How to Auto Invite all users of selected Group using Django admin inlines form?


I am working on an application where currently, I have
1) Staff Model is connected to User model via OneToOne Relationship and can have more than one Group.
2) Meeting model can also assigned to many Group.
3) RSVPinline is a part with MeetingAdmin as a inline form.

Here i was trying to automatically ADD all 'Staff' associated in Selected Groups in django admin form while creating Meetings.

I have tried save_model to add current user in meeting's 'creator' field.

models.py

class Group(models.Model):
    name = models.CharField(max_length=200)

class Staff(models.Model):
    fullname = models.CharField(max_length = 250,verbose_name = "First Name")
    group = models.ManyToManyField(Group, blank=False,verbose_name = "Meeting Group")  # protect error left to add
    is_active = models.BooleanField(default=True)
    user = models.OneToOneField(User, on_delete=models.CASCADE,null=True, blank=True,verbose_name = "Associated as User")  # protect error left to add
    left_date = models.DateField(null=True, blank=True,verbose_name = "Resigned Date")

class Meeting(models.Model):
    title = models.CharField(_('Title'), blank=True, max_length=200)
    start = models.DateTimeField(_('Start'))
    group = models.ManyToManyField(Group, blank=False,verbose_name = "Meeting Group")  # protect error left to add
    location = models.ForeignKey(Location, blank=False,verbose_name = "Location",on_delete=models.CASCADE)  # protect error left to add

class RSVP(models.Model):
    meeting = models.ForeignKey(Meeting, on_delete=models.CASCADE)
    responder =  models.ForeignKey(User, editable=True, on_delete=models.CASCADE, null=True, blank=True,verbose_name = "Attendees", related_name='guest')
    response = models.CharField(max_length = 20, choices= MEETING_RSVP, default='No response', verbose_name = "Status",null=True, blank=True)

admin.py

class RSVPInline(admin.TabularInline):
    model = RSVP
    extra = 0


class MeetingAdmin(admin.ModelAdmin):
    form = MeetingForm
    list_display = ('title', 'location', 'start','creator' )
    inlines = [RSVPInline, TaskInline]

    #Currently using save_model to automatically add current user as a creator
    def save_model(self, request, obj, form, change):
        obj.creator = request.user
        super().save_model(request, obj, form, change)

My pseudo code is:

grouplist = Get group's list from submitted MeetingForm
stafflist = Staff.objects.filter(department__in =grouplist).values_list('id', flat=True).distinct()
Add to RSVPInline:
    values = list(for staff in stafflist:
              'responder' = staff
              'meeting' = 'meeting from  MeetingForm' 
              'response' = has a default value in model

    bulk_create() RSVPInline with values

Solution

  • You can extend save_related() ModelAdmin method to perform additional actions after form object (Meeting) and its Inlines (RSVPs, if present in submitted form) are saved:

    class MeetingAdmin(admin.ModelAdmin):
        ...
    
        def save_related(self, request, form, formsets, change):
            # call original method - saves Meeting and inlines
            super(MeetingAdmin, self).save_related(request, form, formsets, change)
            # get this form Meeting
            obj = form.instance
            # get Staff members of this meeting groups
            # and we can exclude ones already having
            # RSVP for this meeting
            stafflist = Staff.objects.filter(
                group__in=obj.group.all()
            ).exclude(
                user__guest__meeting=obj
            )
            rsvps = list(
                RSVP(responder=staff.user, meeting=obj)
                for staff in stafflist
            )
            # calls bulk_create() under the hood
            obj.rsvp_set.add(*rsvps, bulk=False)
    

    ** Few possibly useful notes:

    • group field may be better to be called groups as it represents ManyToMany relation and returns multiple objects
    • related_name represents relation from the related object back to this one so it may be more logical to use something like invites instead of guest