Джанго: Как бы организовать эту большую мешанину модели/менеджера/дизайна?

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

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

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 Проект пространства имен


person orokusaki    schedule 25.01.2010    source источник


Ответы (3)


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

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

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

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

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

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

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

person Van Gale    schedule 25.01.2010
comment
Отлично, вы прекрасно понимаете, что я делаю. Спасибо, что нашли время, чтобы разместить всю эту информацию. Я разрабатываю SAAS, который будет иметь несколько учетных записей (надеюсь), и у каждой будет несколько пользователей. Пользователи не такая большая проблема, потому что я буду писать некоторые базовые декораторы типа @accountowneronly на уровне представления, чтобы администраторы учетных записей должны были быть владельцем учетной записи или моим собственным вариантом суперпользователя для доступа к определенным представлениям. Огромной проблемой для меня является создание эффективной и расширяемой архитектуры, чтобы я мог кодировать почти так, как если бы я разрабатывал это только для одной учетной записи. - person orokusaki; 26.01.2010
comment
А, мило. Ну, просто в качестве дополнительного примечания (вероятно) идеальным решением для нас в долгосрочной перспективе является поддержка схем Postgresql, которые добавляют эту защиту на уровне базы данных. См. code.djangoproject.com/ticket/1051 и code.djangoproject.com/ticket/6148 (это средний приоритет, поэтому его не будет в django 1.2, см. code.djangoproject.com/wiki/Version1.2Features) - person Van Gale; 26.01.2010
comment
Спасибо Ван. Вы были очень полезны. До последних нескольких дней я не осознавал, насколько кошмарны несколько арендаторов. - person orokusaki; 26.01.2010

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

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

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

person stefanw    schedule 25.01.2010
comment
Это то, что я делал в прошлом, но мне интересно, есть ли какое-то промежуточное программное обеспечение, которое я могу разработать, или что-то, что может инициализировать учетную запись из модели и запроса. Я мог бы создать метод менеджера клиентов, который принимает запрос. - person orokusaki; 25.01.2010
comment
Вы можете сделать что-то вроде Account.special_objects(request).filter..., но имейте в виду: вы смешиваете слой представления со слоем модели. Вы должны держать вещи СУХИМИ, но вы также должны держать их несвязанными. - person stefanw; 25.01.2010
comment
Да, вот почему это такой мозговой пердеж для меня. Я просто не хочу микроуправлять доступом к объектам для каждой учетной записи, и мне очень трудно понять это. Микроуправление может привести (особенно со мной) к ошибке кодирования, которая позволяет одному администратору учетной записи получить доступ к объектам другого владельца учетной записи. - person orokusaki; 25.01.2010

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

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

person Dmitry Shevchenko    schedule 25.01.2010
comment
Да, если честно, я избегаю всего, что связано с какой-либо библиотекой потоков, если только это не требуется для серьезной многопоточности или многопроцессорности. У меня ОКР, поэтому я терпеть не могу хаки и уловки (поэтому все занимает у меня в 3 раза больше времени :) Однако спасибо за ваш ответ. - person orokusaki; 25.01.2010