Skip to content

Fixed sortable tabular inline visualization bug for view-only users #268

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

Riccardo-Maffei
Copy link

Code:

import nested_admin

from django.db import models
from django.contrib import admin


class Menu(models.Model):
    name = models.TextField(max_length=64)


class Dish(models.Model):
    class Meta:
        ordering = ['position']
        verbose_name_plural = 'Dishes'

    name = models.TextField(max_length=64)

    menu = models.ForeignKey(Menu, models.CASCADE)

    position = models.PositiveIntegerField(
        default=0,
        blank=False,
        null=False,
    )


class DishNestedTabularInline(nested_admin.SortableHiddenMixin, nested_admin.NestedTabularInline):
    model = Dish
    extra = 0


class MenuAdmin(nested_admin.NestedModelAdmin):
    inlines = [DishNestedTabularInline]


admin.site.register(Dish)
admin.site.register(Menu, MenuAdmin)

The admin page for the Menu model has the following appearance:

image

For an user without change permissions, however, the form will look rather different:

image

A quick inspection of the HTML reveals the problem. The field responsible for ordering the inlines, which is normally represented as an input element, is being rendered as a dictionary instead.

image

image

These two elements are added to the HTML by the following line:

{% if field.field.is_hidden %} {{ field.field }} {% endif %}

where field is an instance of either AdminField or AdminReadonlyField. These two classes are also the root of the problem, their init mehtod, specifically:

...
class AdminField:
    def __init__(self, form, field, is_first):
        self.field = form[field]  # A django.forms.BoundField instance
        self.is_first = is_first  # Whether this field is first on the line
        self.is_checkbox = isinstance(self.field.field.widget, forms.CheckboxInput)
        self.is_readonly = False

...

class AdminReadonlyField:
    def __init__(self, form, field, is_first, model_admin=None):
        ...
        self.field = {
            "name": class_name,
            "label": label,
            "help_text": help_text,
            "field": field,
            "is_hidden": is_hidden,
        }
        self.form = form

...

The difference is therefore located here. For view-only users, self.field corresponds to a simple dictionary, which does not contain any HTML. For users with more permissions, on the other hand, self.field will correspond to a BoundField, which will eventually return the expected HTML element.

In my implementation I tried to do the exact same thing that the init method does, aka accessing the form dictionary to retrieve a BoundField instance. This can be done quite easily by accessing field.form instead of field.field and then using a filter to retrieve the required Object.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant