maggie
maggie

Reputation: 4395

Django model form with only view permission puts all fields on exclude

Using a custom ModelForm in my default (django admin) change view gives me an empty variable self.fields if the form gets rendered for a user with only view permissions (new in Django 2.1).

This is my code:

# models.py

class Door(ValidateOnSaveMixin, models.Model):
    ...

    motor_type = models.ForeignKey(
        MotorType,
        on_delete=models.SET_NULL,
        default=None,
        blank=True,
        null=True)
    ...
    door_type = models.CharField(
        max_length=3,
        choices=DOOR_TYPES,
        null=True,
        default=None)
    ...
    vehicle_variant = models.ForeignKey(
        VehicleVariant,
        on_delete=models.CASCADE)

    class Meta:
        unique_together = ("vehicle_variant", "location", "motor_type")

    ...



# admin.py

@admin.register(Door)
class DoorAdmin(ImportExportModelAdmin):
    form = DoorAdminForm

    list_display = ('descriptor', 'get_customer_link', 'get_variant', 'location', 'get_motor_type_link',
                    'window_type', 'door_type', 'drum_diameter', 'dist_per_motor_rotation')
    fields = ('vehicle_variant', 'description', 'location', 'motor_type',
              'drum_diameter', 'window_type', 'door_type')

    ...

# forms.py

class DoorAdminForm(ModelForm):

    class Meta:
        model = Door
        fields = '__all__'
        widgets = {
            'motor_type': DoorMotorTypeWidget,
        }

    def __init__(self, *args, **kwargs):
        super(DoorAdminForm, self).__init__(*args, **kwargs)
        # this line is crashing on access with a user who has only the view permission, as self.fields is empty
        self.fields['vehicle_variant'].queryset = VehicleVariant.objects.all().prefetch_related('customer').order_by('customer__name', 'name')

The root cause is related to the exclude attribute of the Meta class in DoorAdminForm. No matter what i write to the fields/exclude attributes, always all of the model's fields get automatically put to the exclude list and prevent self.fields to be populated. And this makes my code crash.

Of course i can check for 'verhicle_variant' in self.fields but i don't understand the reason for that behaviour. I also couldn't find the part in the django source which populates the exclude attribute with all model fields.

Anybody has an idea if this is intended? Whats the root cause for that behaviour (ignoring the fields and exclude attribute of the Meta class)?

Upvotes: 3

Views: 1570

Answers (1)

CoffeeBasedLifeform
CoffeeBasedLifeform

Reputation: 2921

The point where all fields get put into exclude is here: https://github.com/django/django/blob/cf79f92abee2ff5fd4fdcc1a124129a9773805b8/django/contrib/admin/options.py#L674

I have never used django admin with just 'view' permissions. Haven't really found a lot of information about how this should look like either: django-admin comes with add- and change-views amongst others, but no viewonly-view? Almost looks like it doesn't really know what to do with users that do not have 'change' permission. It directs them to the change_view and then notices that they are not allowed to change anything so it displays an empty form?
I suppose you could just declare all the fields as readonly for a viewonly-view?


UPDATE:
The field vehicle_variant won't be in form.fields as it would still be excluded due to not having change permission, but at least you don't crash on the form's init. I still have no idea what a change_view without change permissions should look like other than everything being readonly.

class DoorAdminForm(ModelForm):
    qs = VehicleVariant.objects.prefetch_related('customer').order_by('customer__name', 'name')
    model_field = Door._meta.get_field('vehicle_variant')

    vehicle_variant = model_field.formfield(queryset=qs)

    class Meta:
        model = Door
        fields = '__all__'
        widgets = {
            'motor_type': DoorMotorTypeWidget,
        }

Upvotes: 2

Related Questions