GSoC Task 1 -- Documenting an absurd behaviour in the InlineModelAdmin

About the task

So my first task was relatively quite simple. I had to read the code for the InlineModelAdmin, verify that they produce a certain kind of behaviour, and then finally document that fact. Sounds simple, right? But trust me it wasn’t that straight forward. I completed this task in around one to one and a half weeks.

My approach for the task

So the task was pretty straightforward, but the one milestone that I have to clear was to learn about the framework that Django used for their testing and documentation. So I started learning about Sphinx and Unit Testing. It took me around four to five days to get a decent knowledge about Sphinx so that I could move forward. After which I check whether the concerned raised by Artem Skoretskiy in ticket 28831 were valid and pushed a PR to document that fact.

Technical description of the problem

The user was trying to use ModelAdmin.get_fieldsets and InlineModelAdmin.get_fieldsets to retrieve the model instances, but the problem was that both of returned different objects. One returned the parent model object, and another returned the child model object, which the user requested to change.

For example cosider the following example:


# in models.py

class Customer(models.Model):
    num = models.IntegerField()

class Account(models.Model):
    ACCOUNT_TYPES = (
        (1, 'A'),
        (2, 'B'),
    )
    customer = models.ForeignKey(Customer)
    account_type = models.IntegerField(choices=ACCOUNT_TYPES)

    a = models.CharField(max_length=255, blank=True) # should be edited when type = "A"
    b = models.CharField(max_length=255, blank=True) # should be edited when type = "B"


# in admins.py
@admin.register(models.Customer)
class AccountInline(admin.TabularInline):
    model = models.Account

    def get_fieldsets(self, request, obj=None):
        print(repr(obj))
        return ((None, {'fields': ('account_type',)}),)

class CustomerAdmin(admin.ModelAdmin):
    inlines = (AccountInline,)

    @admin.register(models.Account)
    class AccountAdmin(admin.ModelAdmin):
        def get_fieldsets(self, request, obj=None):
            print(repr(obj))
            if obj and obj.account_type == 1:
                return ((None, {'fields': ('account_type', 'a')}),)
            elif obj and obj.account_type == 2:
                return ((None, {'fields': ('account_type', 'b')}),)
            return ((None, {'fields': ('account_type',)}),)

When you use ModelAdmin.get_fieldsets, you will receive current object. In my example that would be, when you call AccountAdmin.get_fieldsets, you will receive Account. But when you use InlineModelAdmin.get_fieldsets, you will receive parent object. In my example that would be, i.e. when you call AccountInline.get_fieldsets you receive Customer object instead of Account. This may prevent one from retireving the Account object

But changing the behaviour of the get_fieldsets method would have resulted in a failure of the backward compatibility of the framework. So it was decided that it’s worth a documentation note in the docs.

References

Ticket – #28831
Pull Request – #11431