Disable form field in Django's CreateView

1.6k views Asked by At

This question may also be formulated as: "How to change attributes of a CreateView generated form?"

I am using "CreateView" to generate a view and related form in Django 1.10. The idea is that a regular user (a teacher) can create an instance of the model only as himself/herself but a privileged user can create an instance and assign to any teacher. I would like to use the same view/form for both.

The model:

class Set(models.Model):
    name = models.CharField(
        max_length=40,
    )
    matter = models.ForeignKey(
        Matter,
        on_delete=models.SET_NULL,
        null=True,
    )
    group = models.ForeignKey(
        Group,
        on_delete=models.SET_NULL,
        null=True,
    )
    teacher = models.ForeignKey(
        Teacher,
        on_delete=models.PROTECT,
    )

Technically, it would consist on populating the form with the current "teacher" logged in and disable the field unless the current user is privileged. I currently do the initial value setting with the following code, but I do not know how to prevent regular users from modifying the "teacher" field.

class SetCreate(LoginRequiredMixin, CreateView):
    model = Set
    fields = ('name', 'matter', 'group', 'teacher')

    def get_initial(self):
        return {'teacher': self.request.user.teacher.id}

    # code to limit 'teacher' field editing

Another option I tried is to create the instance with the right 'teacher', like:

class SetCreate(LoginRequiredMixin, CreateView):
    model = Set
    fields = ('name', 'matter', 'group')

    def form_valid(self, form):
        form.instance.teacher = self.request.user.teacher
        return super(SetCreate, self).form_valid(form)

It works nice but I cannot use it to edit the 'teacher' field by a privileged user.

I know there exists the 'Field.disable' attribute to form fields, but I do not know how to change the attributes of 'CreateView' generated forms, if possible.

Another option would be to limit the options in the drop-down list in the form, but I do not know how to do it either.

Any advice including a different point of view is welcome.

1

There are 1 answers

2
Daniel Roseman On

Looking at the code, the editing views generate a form within get_form_class based on the value of the fields attribute. That is defined as a class attribute, but the code actually references it via self.fields so there is no reason you couldn't override it at instance level. So, you could do this:

class SetCreate(LoginRequiredMixin, CreateView):
    model = Set
    fields = ('name', 'matter', 'group')

    def get_form_class(self, *args, **kwargs):
        if self.request.user.has_the_right_permission():
            self.fields += ('teacher',)
        else:
            self.fields = self.fields
        return super(SetCreate, self).get_form_kwargs(*args, **kwargs)

(The seemingly pointless else block is to ensure we always set an instance variable.)