Как я могу изменить Django, чтобы создать разрешение на "просмотр"?

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

Одна вещь, которая, по-видимому, отсутствует для моих целей, - это возможность предоставить пользователям доступ только для чтения к данным. Например, у нас есть роль, где людям разрешено входить в систему и создавать заказы на покупку. Они также должны иметь возможность просматривать, но не редактировать данные других клиентов или продуктов.

Как бы я создал разрешения на "просмотр" в администраторе django, чтобы пользователи могли изменять данные для одних таблиц, имея доступ только для чтения к другим?

Обновление: Django Admin, кажется, дает мне CUD интерфейса CRUD. Как получить часть "Только чтение" со связанными разрешениями и группами?

Обновление 2010-февраль-12: Django 1.2 теперь будет доступен только для чтения. Подробности ниже.


Я ответил на мой собственный вопрос, я думаю. Перемещение контента вниз к реальному ответу ниже.

6 ответов

Решение

Возможность добавлять поля только для чтения в представление администратора теперь включена в Django версии 1.2.

См. Билет № 342 http://code.djangoproject.com/ticket/342

См. Номер набора изменений 11965 http://code.djangoproject.com/changeset/11965

См. Документацию http://docs.djangoproject.com/en/dev/ref/contrib/admin/

Вот как я изменил Django 1.0.2, чтобы добавить разрешения на "просмотр". Извините, различий нет.

[X] 1. Добавлен "просмотр" в список разрешений по умолчанию

#./contrib/auth/management/__init__.py
def _get_all_permissions(opts):
    "Returns (codename, name) for all permissions in the given opts."
    perms = []
    for action in ('add', 'change', 'delete', 'view'):
        perms.append((_get_permission_codename(action, opts), u'Can %s %s' % (action, opts.verbose_name_raw)))
    return perms + list(opts.permissions)

[X] 2. Проверка разрешения "вид" добавлено для всех моделей

run manage.py syncdb

Я подтвердил, что разрешение на просмотр теперь добавлено для всех таблиц в таблице auth_permissions

[X] 3. Добавьте "get_view_permission" в класс модели по умолчанию.

Добавлено get_view_permission в модельный класс. Вы можете найти это в файле./db/models/options.py Это используется классом администратора на следующем шаге.

def get_view_permission(self):
    return 'view_%s' % self.object_name.lower()

[X] 4. Добавьте "has_view_permission" в класс администратора по умолчанию

Просто чтобы быть последовательным, я собираюсь добавить "has_view_permission" в систему. Похоже, это должно быть где-то в contrib / admin / options.py. Убедитесь, что если у пользователя есть разрешение на изменение, то автоматически отображаются права на просмотр.

# /contrib/admin/options.py
# Added has_view_permissions
def has_view_permission(self, request, obj=None):
    """
    Returns True if the given request has permission to change or view
    the given Django model instance.

    If `obj` is None, this should return True if the given request has
    permission to change *any* object of the given type.
    """
    opts = self.opts
    return self.has_change_permission(request, obj) or \
        request.user.has_perm(opts.app_label + '.' + opts.get_view_permission())

# modified get_model_perms to include 'view' too.
# No idea where this may be used, but trying to stay consistent
def get_model_perms(self, request):
        """
        Returns a dict of all perms for this model. This dict has the keys
        ``add``, ``change``, and ``delete`` and ``view`` mapping to the True/False
        for each of those actions.
        """
        return {
            'add': self.has_add_permission(request),
            'change': self.has_change_permission(request),
            'delete': self.has_delete_permission(request),
            'view': self.has_view_permission(request),
        }

# modified response_add function to return the user to the mode list
# if they added a unit and have view rights
... 
    else:
        self.message_user(request, msg)

        # Figure out where to redirect. If the user has change permission,
        # redirect to the change-list page for this object. Otherwise,
        # redirect to the admin index.
        #if self.has_change_permission(request, None):
        if self.has_change_permission(request, None) or self.has_view_permission(request, None):
            post_url = '../'
        else:
            post_url = '../../../'
        return HttpResponseRedirect(post_url)

 # modified the change_view function so it becomes the details 
 # for users with view permission

    #if not self.has_change_permission(request, obj):
    if not (self.has_change_permission(request, obj) or (self.has_view_permission(request, obj) and not request.POST)):
        raise PermissionDenied


  # modified the changelist_view function so it shows the list of items
  # if you have view permissions
def changelist_view(self, request, extra_context=None):
    "The 'change list' admin view for this model."
    from django.contrib.admin.views.main import ChangeList, ERROR_FLAG
    opts = self.model._meta
    app_label = opts.app_label
    #if not self.has_change_permission(request, None):
    if not (self.has_change_permission(request, None) or self.has_view_permission(request, None)):
        raise PermissionDenied

[X] 5. Обновите шаблон по умолчанию для отображения списка моделей, если у пользователя есть разрешение на просмотр.

Я изменил шаблон по умолчанию в contrib/admin/templates/admin/index.html. Это также может быть обработано путем копирования файла в локальный каталог шаблонов. Я внес изменения в оба, поэтому у меня есть копия, если позднее обновление перезаписывает мои изменения.

 {% for model in app.models %}
            <tr>
            {% if model.perms.change %}
                <th scope="row"><a href="{{ model.admin_url }}">{{ model.name }}</a></th>
            {% else %}
                {% if model.perms.view %}
                    <th scope="row"><a href="{{ model.admin_url }}">{{ model.name }}</a></th>
                {% else %}
                    <th scope="row">{{ model.name }}</th>
                {% endif %}
            {% endif %}

[X] 6. Подтвердите, что пользователь может "просматривать", но не "менять" модель

Найденный contrib / admin / templatetags / admin_modify.py, по-видимому, управляет отображением кнопок сохранения / сохранения и продолжения или нет. Изменено поле "Сохранить" по умолчанию всегда True, чтобы проверить контекст и разрешения. Пользователь должен иметь возможность сохранить, если у него есть изменения или добавить разрешения.

 'show_save': (change and context['has_change_permission']) or (context['add'] and context['has_add_permission'])

[X] 7. Удалить кнопку "Сохранить и добавить еще", если пользователь просматривает элемент

Снова изменил contrib / admin / templatetags / admin_modify.py. Я не знаю, что означает "save_as", так что, возможно, я что-то сломал, но, похоже, это работает.

    #'show_save_and_add_another': context['has_add_permission'] and
    #                    not is_popup and (not save_as or context['add']) ,
    'show_save_and_add_another': not is_popup and
        (( change and context['has_change_permission']) or (context['add'] and context['has_add_permission']))
        and
        (not save_as or context['add']),

[X] 8. Измените разрешение просмотра, чтобы сделать форму доступной только для чтения.

Если у пользователя есть разрешение на "просмотр" и "изменение", то ничего не делайте. Изменить вид переопределений.

Если пользователь имеет разрешение "просматривать" без "изменения", измените формы по умолчанию и добавьте атрибуты DISABLED или READONLY к элементам формы. Не все браузеры поддерживают это, но для моих целей я могу требовать, чтобы пользователи использовали правильный. Пример отключен / только для чтения

Обнаружено, что не все браузеры поддерживают режим "только для чтения", поэтому некоторые элементы управления устанавливаются только для чтения, другие отключаются. Это позволяет пользователям при необходимости копировать данные из текстовых элементов управления.

#/django/contrib/admin/templates/admin/change_form.html

{# JavaScript for prepopulated fields #}
{% prepopulated_fields_js %}

</div>
</form></div>
{% if has_view_permission and not has_change_permission %}
    <script type="text/javascript">
    jQuery('input:text').attr('readonly', 'readonly');
    jQuery('textarea').attr('readonly', 'readonly');
    jQuery('input:checkbox').attr('disabled', true);
    jQuery('select').attr('disabled', true);
    jQuery('.add-another').hide();
    </script>
{% endif %}

Этот фрагмент сделает суперпользователя единственным с правами на запись.

class AdminOwn(admin.ModelAdmin):
    def get_readonly_fields(self, request, obj=None):
        if request.user.is_superuser:
            return self.readonly_fields
        #get all fields as readonly
        fields = [f.name for f in self.model._meta.fields]
        return fields

Это прямо в админке. Вы можете установить разрешения для пользователей и групп в администраторе, чтобы добавлять, изменять и удалять определенные модели.

Обновление: Извините, я неправильно понял вопрос, потому что я неправильно истолковал слово view, чтобы придать ему значение Django, а не "только для чтения". Если вы хотите использовать только для чтения с помощью администратора, я думаю, вам нужно будет немного поработать. Посмотрите эту ветку, где Джеймс Беннетт (менеджер релиза Django) говорит:

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

а также

Администратор Django работает с тремя разрешениями: "добавить", "изменить" и "удалить". Нет разрешения "просматривать, но не вносить изменения", следовательно, нет никакого способа применить такое ограничение без значительного пользовательского кодирования.

Дополнительная работа будет включать добавление разрешения "только для чтения" для определенных моделей и изменение базовых шаблонов администратора, чтобы проверить, есть ли у пользователя такое разрешение, и, если да, отключение определенных элементов управления (таких как кнопки сохранения) и предоставление другим пользователям доступа только для чтения., Это предотвратит случайное искажение, но вам также может понадобиться изменить логику на стороне сервера, чтобы проверить то же разрешение, чтобы избежать любых POST, сделанных хитрым способом, чтобы обойти разрешения.

Вы можете создать разрешение "только для чтения" в своей модели и использовать код jasuca с модификацией:

models.py:

class MyModel(models.Model):
    name = models.CharField(max_length=100)
    description = models.CharField(max_length=256, null=True, blank=True)

    class Meta:
        permissions = (
            ('readonly_mymodel','Readonly MyModel'),
        )

admin.py:

class MyModelAdmin(admin.ModelAdmin):
    def get_readonly_fields(self, request, obj=None):
        if not request.user.is_superuser and request.user.has_perm('mymodel.readonly_mymodel'):
            return [f.name for f in self.model._meta.fields]
        return self.readonly_fields

В администраторе приложения вы должны дать пользователю разрешение "изменить" и "только для чтения".

Вы можете создавать группы в модуле авторизации. Затем в admin.py, основанном на входе в группу пользователей, установите атрибут readonly_fields для modeladmin. Добавьте метод def has_add_permission (self, request) для возврата false для группы с разрешением только для чтения. Дайте добавить, изменить разрешения для группы. Они смогут только читать атрибуты модели.

Другие вопросы по тегам