I wanted to implement one functionality in my Django application such that a custom admin action is created whenever an object is created in some other model.
For e.g. I have one model as Semester:
class Semester(models.Model):
name = models.CharField(_("Semester Name"), max_length=30, default=None, null=True, blank=True, help_text="Name of the semester")
def __str__(self):
return self.name
and another model as UnregisteredStudent:
class UnregisteredStudent(models.Model):
email = models.CharField(_("email"), max_length=30, default="")
semester= models.ForeignKey(Semester, on_delete=models.SET_NULL, null=True, blank=True)
def __str__(self):
return str(self.email)
In the UnregisteredStudent table I have emails filled up and am using custom admin actions to add semester field to each object in UnregisteredStudent table.
My custom admin action in UnregisteredStudent looks like:
@admin.action(description="Add Semester as Semester 1")
def semester1(self, request, queryset):
semester = Semester.objects.get(name="Semester 1")
for obj in queryset:
obj.semester= semester
obj.save(update_fields=["semester"])
Now as I have done this for semester 1, as and when new semester objects are created I would need new such admin actions like:
@admin.action(description="Add Semester as Semester 2")
def semester2(self, request, queryset):
semester = Semester.objects.get(name="Semester 2")
for obj in queryset:
obj.semester= semester
obj.save(update_fields=["semester"])
Right now I have to add custom admin actions manually in my admin.py
, but I suppose this might not be the correct approach to the problem as in future when I have more objects adding custom admin action for each object might not be feasible.
An easier way is just to create a generator that generates admin actions, like:
def set_semester_action(semester_name):
@admin.action(description=f'Add Semester as {semester_name}')
def set_semester(modeladmin, request, queryset):
queryset.update(semester=Semester.objects.get(name=semester_name))
set_semester.__name__ = f'set_semester_{semester_name.replace(" ", "_")}'
return set_semester
then we can construct the two (or more) actions:
@admin.register(student)
class StudentAdmin(admin.ModelAdmin):
actions = [
set_semester_action('Semester 1'),
set_semester_action('Semester 2'),
]
# …
This will not only avoid duplicating code, but makes it less likely that for example the description is not equivalent to the Semester
object we use, we thus make it less error-prone.
That being said, you might look at django-mass-edit
[GitHub] or similar packages, that allow to edit a queryset with a form, and thus allows selecting the semester yourself.