This is a question about GenericTabularInline
, TabularInline
, and admin.py
.
models.py
class Chapters(models.Model):
name = models.CharField(max_length=40, unique=True)
class Projects(models.Model):
name = models.CharField(max_length=40)
class Courses(models.Model):
name = models.CharField(max_length=30)
is_active = models.BooleanField(default=False)
chapters = SortedManyToManyField(Chapters, related_name="courses", blank=True, sort_value_field_name="chapter_number", base_class=CourseChapters)
projects = models.ManyToManyField(Projects, related_name="courses", blank=True)
# Here comes generic relations
class QuestionsManager(models.Manager):
def active(self):
return self.get_queryset().filter(Q(fib__is_active=True) | Q(mcq__is_active=True) | Q(code__is_active=True))
class Questions(models.Model):
chapter = models.ForeignKey(Chapters, on_delete=models.CASCADE)
content_type = models.ForeignKey(ContentType,
limit_choices_to={
"model__in": ["fib", "mcq", "code"]},
on_delete=models.CASCADE
)
object_id = models.PositiveIntegerField()
fib_mcq_code = GenericForeignKey("content_type", "object_id")
objects = QuestionsManager()
class CODE(models.Model):
name = models.CharField(max_length=36)
chapter_question = GenericRelation("questions", related_query_name='code')
is_active = models.BooleanField(default=False)
class MCQ(models.Model):
name = models.CharField(max_length=36)
chapter_question = GenericRelation("questions", related_query_name='mcq')
is_active = models.BooleanField(default=False)
class FIB(models.Model):
name = models.CharField(max_length=36)
chapter_question = GenericRelation("questions", related_query_name='fib')
is_active = models.BooleanField(default=False)
admin.py
class QuestionsChaptersInlineForm(forms.BaseInlineFormSet):
def clean(self):
pass
class QuestionsChaptersInline(admin.TabularInline):
model = Questions
formset = QuestionsChaptersInlineForm
extra = 0
readonly_fields = ['content_type', 'object_id', 'is_active', "modify"]
def has_add_permission(self, request, obj):
return False
def has_delete_permission(self, request, obj):
return False
class ChapterAdminForm(forms.ModelForm):
class Meta:
model = Chapters
fields = '__all__'
def clean(self):
cleaned_data = super().clean()
if cleaned_data.get('is_active'):
if not self.instance.questions_set.active().exists():
self.add_error('is_active', 'At least one question in this chapter must be active.')
return cleaned_data
class ChaptersAdmin(ImportExportModelAdmin, ImportExportActionModelAdmin, admin.ModelAdmin):
form = ChapterAdminForm
inlines = [QuestionsChaptersInline, CoursesChaptersInline]
# ===========================
class ChaptersQuestionsInline(GenericTabularInline):
model = Questions
extra = 1
max_num = 1
class FIBAdmin(ImportExportModelAdmin, ImportExportActionModelAdmin, AdminConfirmMixin, admin.ModelAdmin):
inlines = [ChaptersQuestionsInline]
^
|
# same for CODE and MCQ
The problem is that I need to set is_active = False
for a chapter if there are no active questions associated with it. Conversely, I want to set is_active = True
for the chapter if at least one question is active. Currently, I'm using the clean()
method in ChapterAdminForm
to check question activeness by accessing self.instance
, but this approach is not reliable as it shows me the data of the previously saved instance. To handle this, I've disabled the ability to add and delete inline objects for safety.
Overall, what I need is:
For Chapters:
When I attempt to save a chapter with is_active = True
, it should first check if there are any active questions associated with it. If there are no active questions, I want to display an error message on the tabular inline indicating that at least one question must be active
. The same validation process should also apply if I allow deletion on the tabular inline. (although it is done but I need a best practice)
For the FIB, CODE, and MCQ models: Since I am not adding questions directly from the Chapters model, the only way to define questions for a chapter is through the GenericTabularInline in the FIB, CODE, and MCQ models, which I have already implemented. However, what I want to achieve is to check the current chapter when performing an update or deleting a GenericRelation from the GenericTabularInline. If there are no active questions left in that chapter, I want to add an error message stating, "There are no active questions left in this chapter. Please deactivate the chapter before proceeding."
For the Courses model: I would like to implement a similar validation process for the Chapter and Courses relationship, just like we did for Chapters and the Questions (FIB, MCQ, CODE) models. If there are no active chapters present in a course, I want to perform a similar validation and set the appropriate status (especially for the TubularInline view).