Джанго: как можно организовать этот большой беспорядок модели / менеджера / дизайна?


Чтобы подвести итог, прежде чем я перейду к плохим примерам, и т. д.: Я пытаюсь создать приложение, в котором мне не нужно писать код во всех моих моделях, чтобы ограничить выбор текущей учетной записью (я не использую Auth или встроенные функции для учетной записи или входа).

То есть, я не хочу делать что-то вроде этого:

class Ticket(models.Model):
        account = models.ForeignKey(Account)
        client = models.ForeignKey(Client)  # A client will be owned by one account.
        content = models.CharField(max_length=255)

class TicketForm(forms.ModelForm):
        class Meta:
                model = Ticket
                exclude = ('account',)  #First sign of bad design?

        def __init__(self, *args, **kwargs):
                super(OrderForm, self).__init__(*args, **kwargs)
                if self.initial.get('account'):
                        # Here's where it gets ugly IMHO. This seems almost
                        # as bad as hard coding data. It's not DRY either.
                        self.fields['client'].queryset = Client.objects.filter(account=self.initial.get('account'))

Моя идея состоит в том, чтобы создать модель Account(models.Model) со следующим пользовательским менеджером и подклассировать ее, используя многотабличное наследование со всеми моими моделями. Но от этого у меня ужасно болит голова. Будет ли мне по-прежнему нужен внешний ключ account для каждой модели? Могу ли я получить доступ к учетной записи родительского класса для определенного экземпляра модели?

class TicketManager(models.Manager):
    def get_query_set(self):
        return super(TicketManager, self).get_query_set().filter(account=Account.objects.get(id=1))
        # Obviously I don't want to hard code the account like this.
        # I want to do something like this:
        # return super(ProductManager, self).get_query_set().filter(account=self.account)
        # Self being the current model that's using this manager
        # (obviously this is wrong because you're not inside a model
        # instance , but this is where the confusion comes in for me.
        # How would I do this?).

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

Вот где я получил идею сделать это: Django Namespace project

3 3

3 ответа:

Есть две тесно связанные проблемы, когда речь заходит о Django.

Один-это разрешения на уровне строк , где пользователям/учетным записям требуется специальное разрешение для просмотра конкретной строки (объекта) в таблице, в отличие от обычного фреймворка Django auth, который имеет разрешения на уровне таблиц .

Проект, на который вы ссылаетесь, является одним из нескольких проектов, пытающихся реализовать разрешения на строки. django-granular-permissions это еще и третий (мой любимый и самый активный / поддерживаемый) является django-авторитетом.

Предстоящий выпуск Django 1.2 будет иметь крючки, облегчающие реализацию разрешений на уровне строк, иавтор django-authority будет работать над интеграцией своего проекта .

Вторая связанная проблема-это так называемаямультитенантная база данных , которая является вариацией разрешений на строки. В этой схеме у вас может быть несколько пользователей из одной компании, например, которые все имеют доступ к данным для этого компания, но не другие компании (арендаторы).

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

Что касается более конкретного ответа на ваш вопрос, я думаю, что вы должны либо используйте django-authority , либо напишите пользовательский менеджер и используйте средство проверки владения записью во время разработки, чтобы проверить, что ваши запросы не обходят пользовательский менеджер.

Основная проблема здесь заключается в том, что даже если вы не используете django.contrib.auth, Информация о текущем вошедшем в систему пользователе доступна только в представлении и никогда в модели, потому что эта информация привязана к запросу. Поэтому вам всегда придется делать что-то вроде этого в представлении:

def some_view(request):
    account = get_account_by_request(request)

Затем вы можете использовать учетную запись для фильтрации моделей. Вы всегда можете сделать этот подход более элегантным, используя промежуточное программное обеспечение или декоратор, но будьте осторожны, чтобы он не стал слишком сложным. Ваш дизайн может сломаться в неожиданные моменты (случилось со мной) из-за слишком большого количества множественных наследств с унаследованными менеджерами и тому подобное. Пусть все будет просто и предсказуемо.

Для того, чтобы получить текущего пользователя в свой код, вы можете использовать threadlocals middleware но в этом нет ничего необычного, просто один большой Хак, так что будьте осторожны:)

Лично я считаю, что плохо вкладывать такую логику в модели, потому что они просто данные и не должны знать о таких вещах.