Как сделать пользовательскую форму доступной для чтения при обращении к ней пользователя без права изменения на Django Admin

У меня есть проблема. У меня есть пользовательские формы для большинства моих моделей - так как я делаю большинство своих валидаций и манипуляций с данными через формы, а не через представления, чтобы опыт или поведение пользователя при выполнении операции через мой сайт или админ-панель Django были одинаковыми.

Для моделей, зарегистрированных на сайте Admin без пользовательских форм (только для модели register) - они ведут себя безупречно в соответствии с пермами пользователей. Если у них есть change_modelname прав, они могут нажать на ссылку на объекте и попасть на DetailView форму, где они могут внести изменения. Если у них есть только view_modelname пермы, они попадут на DetailView, которые сейчас все readonly.

Для моделей, зарегистрированных на сайте Admin с моими пользовательскими формами - для пользователей, имеющих только права просмотра (без прав изменения), при нажатии на объект отображается KeyError, вместо того, чтобы видеть DetailView в режиме readonly.

Я искал, как решить эту проблему, включая попытки найти в исходном коде, как Django делает это с формами, которые они генерируют из наших моделей по умолчанию, но не могу найти ничего полезного для меня.

До сих пор я пробовал проверять наличие прав пользователя в методе admin.ModelAdmin экземпляра get_form - если у пользователя есть change_modelname права, я передаю ему свою пользовательскую форму, иначе ничего не делаю. Сначала это вроде бы работает, пользователь без change_modelname разрешений, но с view_modelname разрешениями может попасть в DetailView с помощью readonly, поскольку им передается форма, которую Django создает из модели, а не моя собственная. Когда после этого я дам им пермы change_modelname, они получат мою пользовательскую форму, как и ожидалось. Но когда я отзываю это разрешение change_modelname (изменяя групповые разрешения пользователя или удаляя его из группы), они снова сталкиваются с KeyError, поскольку Django все еще пытается дать им мою пользовательскую форму по неизвестным мне причинам. Почему они выполняют проверку в первый раз, но не во второй?

В любом случае, я думал, что это решение было скорее janky обходным путем. В идеале, я хотел бы знать, как сделать пользовательскую форму, которая наиболее похожа на Django-форму, которая позволяет ей вести себя так, как ожидается в зависимости от пермов - сделать форму readonly, когда у пользователя только view_modelname пермы, как ведут себя формы, сгенерированные Django. Не стесняйтесь также сообщить мне, что я мог бы сделать лучше с моим janky обходным решением, которое я попытался сделать. Спасибо!

Примечание: Я также пытался придумать, как сделать так, чтобы сайт Django Admin не давал ссылку на DetailView объекта для начала, если у них нет change_modelname perms.

Код:

admin.py

class IotDeviceAdmin(admin.ModelAdmin):
  list_display = ['name', 'desc', 'owner', 'mac_address']
  search_fields = ['name', 'desc', 'owner', 'mac_address']

  def get_form(self, request, obj=None, **kwargs):
    # if updating
    if obj:
      # if user has change perms
      if request.user.has_perm('iot.change_iotdevice'):
        # give custom update form
        self.form = IotUpdateForm
    # if creating
    else:
      # give custom create form
      self.form = IotCreateForm
    return super(IotDeviceAdmin, self).get_form(request, obj, **kwargs)

forms.py

class IotUpdateForm(forms.ModelForm):
  def __init__(self, *args, **kwargs):
    super(IotUpdateForm, self).__init__(*args, **kwargs)
    self.fields['owner'].disabled = True
    self.fields['device_name'].disabled = True
    try:
      updating_device = IotDevice.objects.get(mac_address=self.instance.mac_address)
    except ObjectDoesNotExist as e:
      # log error
    else:
      if updating_device.approved:
        self.fields['mac_address'].disabled = True

  class Meta:
    model = IotDevice
    fields = ['name', 'desc', 'owner', 'mac_address']
  
  def clean_my_fields(self):
    # clean
    return my_fields

models.py

class IotDevice(models.Model):
  owner = models.CharField(max_length=100)
  name = models.CharField("Device Name", unique=True, max_length=100)
  desc = models.CharField("Device Description", max_length=200)
  mac_address = models.CharField("MAC Address", unique=True, max_length=17)
  
  class Meta:
    verbose_name = "IoT Device"

  def __str__(self):
    return self.name
Вернуться на верх