0

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).

shraysalvi
  • 303
  • 1
  • 10

0 Answers0