Как сделать пользовательскую форму доступной для чтения при обращении к ней пользователя без права изменения на 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