Django просмотр списка файлов директории здесь не разрешен

Обновлено: 08.07.2024

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

‒ Apple Developer Documentation

Вместе с authentication и :doc: ` throttling <throttling>`** , разрешения определяют, должен ли запрос быть удовлетворен или в доступе должно быть отказано.

Разрешения используются для предоставления или запрета доступа различных классов пользователей к различным частям API.

Самый простой стиль разрешения - разрешить доступ любому аутентифицированному пользователю и запретить доступ любому неаутентифицированному пользователю. Это соответствует классу IsAuthenticated в REST framework.

Чуть менее строгий стиль разрешения - разрешить полный доступ для аутентифицированных пользователей, но разрешить доступ только для чтения для неаутентифицированных пользователей. Это соответствует классу IsAuthenticatedOrReadOnly в REST framework.

Как определяются разрешения¶

Разрешения в REST-фреймворке всегда определяются как список классов разрешений.

Перед запуском основной части представления проверяется каждое разрешение в списке. Если проверка какого-либо разрешения не прошла, будет вызвано исключение exceptions.PermissionDenied или exceptions.NotAuthenticated , и основное тело представления не будет запущено.

При неудачной проверке разрешений возвращается либо ответ «403 Forbidden», либо ответ «401 Unauthorized», в соответствии со следующими правилами:

Разрешения на уровне объекта¶

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

Разрешения на уровне объектов выполняются общими представлениями REST framework при вызове .get_object() . Как и в случае с разрешениями на уровне представления, исключение exceptions.PermissionDenied будет вызвано, если пользователю не разрешено действовать с данным объектом.

Это либо вызовет исключение PermissionDenied или NotAuthenticated , либо просто вернется, если представление имеет соответствующие разрешения.

Примечание : За исключением DjangoObjectPermissions , предоставленные классы разрешений в rest_framework.permissions не реализуют методы, необходимые для проверки разрешений объектов.

Если вы хотите использовать предоставленные классы разрешений для проверки разрешений объектов, вы должны подклассифицировать их и реализовать метод has_object_permission() , описанный в разделе ** *Custom permissions* (ниже).

Ограничения разрешений на уровне объекта¶

По причинам производительности общие представления не будут автоматически применять разрешения на уровне объекта к каждому экземпляру в наборе запросов при возврате списка объектов.

Часто, когда вы используете разрешения на уровне объекта, вы также хотите filter the queryset соответствующим образом, чтобы гарантировать, что пользователи имеют видимость только тех экземпляров, которые им разрешено просматривать.

Поскольку метод get_object() не вызывается, разрешения объектного уровня из метода has_object_permission() не применяются при создании объектов. Чтобы ограничить создание объектов, вам необходимо реализовать проверку прав либо в классе Serializer, либо переопределить метод perform_create() > вашего класса ViewSet.

Настройка политики разрешений¶

Политика разрешений по умолчанию может быть установлена глобально, с помощью параметра DEFAULT_PERMISSION_CLASSES . Например.

Если этот параметр не указан, то по умолчанию он разрешает неограниченный доступ:

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

Примечание: когда вы устанавливаете новые классы разрешений с помощью атрибута class или декораторов, вы говорите представлению игнорировать список по умолчанию, установленный в файле settings.py.

Если они наследуются от rest_framework.permissions.BasePermission , разрешения могут быть составлены с помощью стандартных побитовых операторов Python. Например, IsAuthenticatedOrReadOnly можно записать:

Примечание: он поддерживает & (и), | (или) и

AllowAny¶

Класс разрешения AllowAny разрешает неограниченный доступ, независимо от того, был ли запрос аутентифицирован или неаутентифицирован.

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

IsAuthenticated¶

Класс разрешения IsAuthenticated будет запрещать разрешение любому неаутентифицированному пользователю, и разрешать в противном случае.

IsAdminUser¶

Класс разрешения IsAdminUser будет запрещать разрешение любому пользователю, если только user.is_staff не является True , в этом случае разрешение будет разрешено.

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

IsAuthenticatedOrReadOnly¶

IsAuthenticatedOrReadOnly позволит аутентифицированным пользователям выполнить любой запрос. Запросы неавторизованных пользователей будут разрешены, только если метод запроса является одним из «безопасных» методов; GET , HEAD или OPTIONS .

Это разрешение подходит, если вы хотите, чтобы ваш API разрешал права на чтение анонимным пользователям и разрешал права на запись только аутентифицированным пользователям.

DjangoModelPermissions¶

Этот класс разрешений связан со стандартом Django django.contrib.auth model permissions . Это разрешение должно применяться только к представлениям, имеющим .queryset свойство или get_queryset() метод. Разрешение будет предоставлено только в том случае, если пользователь аутентифицирован и имеет соответствующие разрешения модели.

Запросы POST требуют, чтобы пользователь имел разрешение add на модель.

Запросы PUT и PATCH требуют, чтобы пользователь имел разрешение change на модель.

Запросы DELETE требуют, чтобы пользователь имел разрешение delete на модель.

Поведение по умолчанию также можно переопределить для поддержки пользовательских разрешений модели. Например, вы можете включить разрешение модели view для запросов GET .

Чтобы использовать пользовательские разрешения модели, переопределите DjangoModelPermissions и установите свойство .perms_map . Подробности см. в исходном коде.

DjangoModelPermissionsOrAnonReadOnly¶

Аналогично DjangoModelPermissions , но также позволяет неаутентифицированным пользователям иметь доступ к API только для чтения.

DjangoObjectPermissions¶

Этот класс разрешений связан со стандартом Django object permissions framework , который позволяет устанавливать разрешения на модели на уровне объекта. Чтобы использовать этот класс разрешений, вам также необходимо добавить бэкенд разрешений, который поддерживает разрешения на уровне объекта, такие как django-guardian .

Как и DjangoModelPermissions , это разрешение должно применяться только к представлениям, имеющим .queryset свойство или .get_queryset() метод. Разрешение будет предоставлено только в том случае, если пользователь аутентифицирован и имеет соответствующие разрешения на объект и соответствующие разрешения на модель.

Запросы POST требуют, чтобы пользователь имел разрешение add на экземпляр модели.

Запросы PUT и PATCH требуют, чтобы пользователь имел разрешение change на экземпляр модели.

Запросы DELETE требуют, чтобы пользователь имел разрешение delete на экземпляр модели.

Обратите внимание, что DjangoObjectPermissions ** не требует пакета django-guardian , и должен одинаково хорошо поддерживать другие бэкенды объектного уровня.

Как и в случае с DjangoModelPermissions , вы можете использовать пользовательские разрешения модели, переопределив DjangoObjectPermissions и установив свойство .perms_map . Подробности см. в исходном коде.

Чтобы реализовать пользовательское разрешение, переопределите BasePermission и реализуйте один или оба из следующих методов:

.has_permission(self, request, view)

.has_object_permission(self, request, view, obj)

Методы должны возвращать True , если запрос должен получить доступ, и False в противном случае.

Если вам нужно проверить, является ли запрос операцией чтения или записи, вы должны проверить метод запроса на соответствие константе SAFE_METHODS``** , которая является кортежем, содержащим ``'GET' , 'OPTIONS' и 'HEAD' . Например:

Примеры¶

Ниже приведен пример класса разрешения, который проверяет IP-адрес входящего запроса по списку блокировки и отклоняет запрос, если IP-адрес был заблокирован.

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

Также обратите внимание, что общие представления будут проверять разрешения на уровне объекта только для представлений, которые получают один экземпляр модели. Если вам требуется фильтрация на уровне объектов для списковых представлений, вам придется фильтровать набор запросов отдельно. Более подробную информацию см. в filtering documentation .

Структура REST предлагает три различных метода настройки ограничений доступа в каждом конкретном случае. Они применяются в разных сценариях и имеют различные эффекты и ограничения.

queryset /** get_queryset() : Ограничивает общую видимость существующих объектов из базы данных. Кверисет ограничивает, какие объекты будут отображаться в списке и какие объекты могут быть изменены или удалены. Метод get_queryset() > может применять различные кверисеты в зависимости от текущего действия.

permission_classes / get_permissions() : Общая проверка прав доступа, основанная на текущем действии, запросе и целевом объекте. Разрешения на уровне объекта могут быть применены только к действиям получения, изменения и удаления. Проверки разрешений для list и create будут применены ко всему типу объекта. (В случае списка: с учетом ограничений в наборе запросов).

serializer_class /** get_serializer() : Ограничения на уровне экземпляра, которые применяются ко всем объектам на входе и выходе. Сериализатор может иметь доступ к контексту запроса. Метод get_serializer() > может применять различные сериализаторы в зависимости от текущего действия.

В следующей таблице перечислены методы ограничения доступа и уровень контроля, который они обеспечивают, над какими действиями.

Также доступны следующие пакеты сторонних производителей.

DRF - Политика доступа¶

Пакет Django REST - Access Policy предоставляет способ определения сложных правил доступа в декларативных классах политик, которые прикрепляются к наборам представлений или представлениям на основе функций. Политики определяются в JSON в формате, аналогичном политикам AWS Identity & Access Management.

Составленные разрешения¶

Пакет Composed Permissions предоставляет простой способ определения сложных и многоглубинных (с логическими операторами) объектов разрешения, используя небольшие и многократно используемые компоненты.

Условие REST¶

Пакет REST Condition - это еще одно расширение для построения сложных разрешений простым и удобным способом. Расширение позволяет комбинировать разрешения с логическими операторами.

DRY Rest Permissions¶

Пакет DRY Rest Permissions предоставляет возможность определять различные разрешения для отдельных действий по умолчанию и пользовательских действий. Этот пакет предназначен для приложений с разрешениями, которые являются производными от отношений, определенных в модели данных приложения. Он также поддерживает проверку разрешений, возвращаемую клиентскому приложению через сериализатор API. Кроме того, он поддерживает добавление разрешений к действиям списка по умолчанию и пользовательским действиям списка для ограничения данных, которые они извлекают для каждого пользователя.

Роли Django Rest Framework¶

Пакет Django Rest Framework Roles облегчает параметризацию API для нескольких типов пользователей.

Django REST Framework API Key¶

Пакет Django REST Framework API Key предоставляет классы разрешений, модели и помощники для добавления авторизации по ключу API в ваш API. Он может быть использован для авторизации внутренних или сторонних бэкендов и сервисов (т.е. машин* ), которые не имеют учетной записи пользователя. API ключи хранятся в безопасном месте с использованием инфраструктуры хэширования паролей Django, и их можно просматривать, редактировать и отзывать в любое время в админке Django.

Ролевые фильтры Django Rest Framework¶

Пакет Django Rest Framework Role Filters обеспечивает простую фильтрацию по нескольким типам ролей.

Django Rest Framework PSQ¶

Пакет Django Rest Framework PSQ - это расширение, которое обеспечивает поддержку того, чтобы основанные на действиях классы_разрешений , сериализатор_классов , и queryset зависели от правил, основанных на разрешениях.

У меня есть приложение django. Вот моя структура каталогов.

вот мой settings.py

Когда я перехожу к localhost:8000/static/updater/ я получаю, что Directory Indexes not allowed here .

Есть идеи? Я думаю, что есть некоторая ошибка в способе обслуживания статических файлов. Кстати, я использую extjs.

Я ожидаю появления index.html в static/updater/ каталоге. Это происходит по умолчанию?

Похоже, что по какой-то причине Django не хочет обслуживать список каталогов /static/updater/. Поскольку static updater - это каталог, а не файл, попробуйте сделать /static/updater/index.html или просто /static/index.html и посмотреть, обслуживаются ли они. Если они не обслуживаются, это может быть проблемой при настройке ваших статических файлов. Если да, то я не уверен, что просто Django отказывается обслуживать индексы каталогов в статических папках или что-то еще.

(Похоже, что это должен быть комментарий, но мне не хватает комментариев для комментариев.)

Ваш STATICFILES_DIRS пуст - отсюда будет работать dev-сервер.

В дополнение к henrikstroem ответьте:

В основном, STATIC_ROOT этого места, где collectstatic команда будет собирать все статические файлы и STATICFILES_DIRS этого место (или места расположения), где эта команда будет выглядеть для статических файлов.

Моя настройка разработки выглядит так:

Я указываю STATIC_ROOT на project_root/run/static и включаю project_root/static в STATICFILES_DIRS . Как вы можете видеть, я храню свои статические активы в project_root/static и они обслуживаются.

Во время разработки (то есть: при использовании команды runserver ) ваши статические активы будут обслуживаться непосредственно из их местоположения, в моем случае project_root/static . При развертывании вы будете собирать свои статические активы в STATIC_ROOT . Это может быть папка в вашем проекте (как мой project_root/run/static или даже каталог в совершенно другой среде, вы можете даже скопировать их в CDN [сеть доставки контента]).

Лично мне нравится развертывание с Apache. Я настрою один virtualenv для обработки частей Django. Статические активы обслуживаются другим virtualenv, который полностью обходит всю динамическую обработку и просто обслуживает статические файлы. Вы можете использовать другой сервер, например nginx, для этой задачи.

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

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

я на своей машине разработки. У меня есть папка media в корневом каталоге моего проекта.

на settings.py Я: MEDIA_ROOT = '' и MEDIA_URL = '/media/' .

но единственный способ получить медиафайлы-это ссылаться на /media/media/ например, <img src="https://askdev.ru/media/media/image.jpg" /> .

я надеюсь (и хочу)
<img src="https://askdev.ru/media/image.jpg" />

может ли кто-нибудь сказать мне, что здесь происходит, и дать мне простой рецепт для настройки обработки медиафайлов?

@Тимми О'Махони - спасибо! эпический пост, и очень четкий. Но это оставляет пару вопросов:

(1) я должен использовать /media/ и /static/ , а не media/ и static/ as MEDIA_URL и STATIC_URL - я что-то пропустила?

(2) Если collectstatic шланги /static/ , где вы помещаете CSS уровня сайта, например, CSS-файлы сайта? Не в /static/ , очевидно.

(3) я положил их в каталог '_' от корня проекта и набор STATICFILES_DIRS чтобы указать на это - и, похоже, именно там сервер разработки получает свои статические файлы, несмотря на

почему вы сделали MEDIA_ROOT настройки пустым? Это должен быть путь к вашему каталогу мультимедиа. Поскольку, как вы говорите, ваш носитель находится в подкаталоге media , вы должны поместить это в MEDIA_ROOT .

Папку Настройки:

ваш корень проекта должен быть чем-то вроде:

The СМИ папка должна содержать такие вещи, как изображения, загрузки и другие материалы, которые могут быть загружены во время обычного использования веб-сайта (т. е. после завершения разработки)

The static папка должна содержать все CSS / JS и другие материалы, которые являются частью разработки сайт

Settings.py:

MEDIA_ROOT - абсолютный путь сервера к статической папке, указанной выше. Это означает, что это должно быть что-то вроде:

MEDIA_URL относительный URL браузера вы должны получить доступ к медиа-файлам, когда вы смотрите на сайте. Это должно быть (обычно)

аналогично, STATIC_ROOT должно быть что-то вроде

и STATIC_URL be

обслуживание файлов:

теперь, когда вы сказали django, где эти папки должны быть, и правильные URL-адреса для доступа к ним, вам нужно правильно обслуживать все запросы к папкам.

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

если вы разрабатываете, вы можете просто получить сервер разработки django, чтобы служить им для вас.

чтобы сделать это, вы добавляете некоторые URL-адреса URLS.py как у тебя:

Extra:

если у вас есть несколько приложений, каждый со своими CSS и JS-файлами, вы не захотите бросать их в одну /статическую/ папку. Было бы полезно поместить их во вложенные папки приложений, к которым они принадлежат:

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

я следовал процедуре Тимми, но я получил ошибку, что нет имени модуля django.views . Когда я использую import django.views в моем virtualenv все работает нормально.e это не проблема с импортом библиотеки.

однако я смог решить эту проблему, выполнив эту процедуру в моем основном файле urls

в своем settings.py Добавьте

в ваших TEMPLATE_CONTEXT_PROCESSORS.В противном случае MEDIA_ROOT не будет работать, когда вы используете его в шаблонах.

Я использую Django 1.10. И моя медиа-папка "uploads" Это конфигурация в my settings.py:

и в шаблоне я поставил имя o моего MEDIA_URL перед de object.имя вместо объекта.url вроде этого:

Это перевод постов из одного интересного блога. Оригинал ч.1, ч.2 и ч.3. Описанный подход работает для Django 1.3 и выше.

В этой статье под файлом подразумевается все, что не генерируется динамически. Есть два типа файлов, с которыми работают веб-приложения:

  1. Файла прописанные в коде и в шаблонах, назовем их STATIC файлы.
  2. Файлы, который используются в коде, но известны только в процессе работы кода, назовем их MEDIA файлы. Прим.: например файлы загруженные пользователем. Имя файлы мы не знаем и в коде оно не прописано.

Это уловная классификация и основана на определениях из документации Django.

Настройка

Рассмотрим один из вариантов настройки:

Что такое _PATH должно быть понятно. В проекте находится папка files , которая содержит папку media для MEDIA файлов и папка static для STATIC файлов. Очень важно разделить их таким образом. Потом можете получить много проблем не разделив их изначально. Это позволит вам легко использовать различные версии файлов, расположив их по разным папкам.

Но почему files в папке проекта? Потому что это удобно при разработке. На "продакшине" рекомендуется вынести files из проекта и изменить MEDIA_ROOT и STATIC_ROOT .

STATIC файлы располагаются в <_PATH>/static . Это используемые js и css файлы. Приложение django.contrib.staticfiles соберет все STATIC файлы из STATICFILES_DIRS и из соответствующих папок приложений и перенесет в STATIC_ROOT .

  • В STATIC добавляются файлы из проекта(смотрите STATICFILES_DIRS и FileSystemFinder ), а так же AppDirectoriesFinder добавит файлы из папки static в приложениях.
  • MEDIA и STATIC файла лучше разделить(смотрите STATIC_ROOT и MEDIA_ROOT ).
  • STATIC_ROOT и MEDIA_ROOT лучше вынести из проекта на "продакшине".

И не забудьте добавить "слеш" в конце STATIC_URL и MEDIA_URL !

Как настроить выдачу файлов для dev-сервера

Как упоминалось выше, лучше отдавать файлы через специальный сервер, например Nginx или Apache. Но при разработке это лишние проблемы. Можно легко настроить выдачу файлов средствами Django. Предполагается что при разработке DEBUG всегда True , а на сервере - False . Добавим в urls.py:

Как использовать STATIC файлы в шаблонах

Как использовать MEDIA файлы в шаблоне

Это на много проще. Объект FieldFile , возвращаемый ImageField и FileField , содержит path и url :

Как добавить STATIC файлы

Так как имена STATIC файлов используются в коде, шаблонах, лучше всего хранить их в VCS вместе с кодом. STATIC файлы собираются(находят и копируются или через simlink) с помощью "finder"-ов. Вам не обязательно знать как все это происходит, главное хранить файлы в нужном месте.

При разработке независимого приложения для Django файлы нужно хранить в папке static в вашем приложении:

Файлы, которые относятся конкретно к проекту нужно хранить в папка указанных в STATICFILES_DIRS :

Как загрузить MEDIA файлы

MEDIA файлы в основном используются в моделях с полями FileField и ImageField. Например:

В этом документе описываются API-интерфейсы Django для доступа к файлам, например загруженным пользователем. API нижнего уровня достаточно общие, чтобы вы могли использовать их для других целей. Если вы хотите работать со «статическими файлами» (JS, CSS и т. Д.), См. Управление статическими файлами (например, изображениями, JavaScript, CSS) .

По умолчанию, Django хранит файлы локально, используя MEDIA_ROOT и MEDIA_URL настройку. В приведенных ниже примерах предполагается, что вы используете эти значения по умолчанию.

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

Использование файлов в моделях ¶

Когда вы используете FileField или ImageField , Django предоставляет набор API, которые вы можете использовать для работы с этим файлом.

Рассмотрим следующую модель, использующую ImageField для хранения фотографии:

Любой Car экземпляр будет иметь photo атрибут, который можно использовать для получения сведений о прикрепленной фотографии:

Этот объект - car.photo в примере - является File объектом, что означает, что он имеет все методы и атрибуты, описанные ниже.

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

Например, вы можете изменить имя файла, name установив путь к файлу относительно местоположения хранилища файлов ( MEDIA_ROOT если вы используете значение по умолчанию FileSystemStorage ):

Хотя в экземпляре доступны ImageField атрибуты данных, не относящиеся к изображению, такие как height , width и size , базовые данные изображения нельзя использовать без повторного открытия изображения. Например:

File Объект ¶

Внутри Django использует django.core.files.File экземпляр каждый раз, когда ему нужно представить файл.

В большинстве случаев вы будете использовать то, File что вам предоставил Django (то есть файл, прикрепленный к модели, как указано выше, или, возможно, загруженный файл).

Если вам нужно построить File самостоятельно, самый простой способ - создать его с помощью встроенного file объекта Python :

Теперь вы можете использовать любые задокументированные атрибуты и методы File класса.

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

Закрытие файлов особенно важно при доступе к полям файла в цикле по большому количеству объектов. Если файлы не закрываются вручную после доступа к ним, может возникнуть риск нехватки файловых дескрипторов. Это может привести к следующей ошибке:

Файловое хранилище ¶

За кулисами Django делегирует решения о том, как и где хранить файлы, системе хранения файлов. Это объект, который на самом деле понимает такие вещи, как файловые системы, открытие и чтение файлов и т. Д.

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

См. Ниже подробные сведения о встроенной системе хранения файлов по умолчанию и см. Создание собственной системы хранения для получения информации о создании собственной системы хранения файлов.

Объекты хранения ¶

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

См. API хранилища файлов для API хранилища файлов.

Класс хранилища встроенной файловой системы ¶

Django поставляется с django.core.files.storage.FileSystemStorage классом, который реализует базовое хранилище файлов локальной файловой системы.

Например, следующий код будет хранить загруженные файлы /media/photos вне зависимости от ваших MEDIA_ROOT настроек:

Пользовательские системы хранения работают одинаково: вы можете передать их в качестве storage аргумента в файл FileField .

Использование вызываемого ¶

Вы можете использовать вызываемый объект в качестве storage параметра для FileField или ImageField . Это позволяет вам изменять используемое хранилище во время выполнения, например, выбирая разные хранилища для разных сред.

Ваш вызываемый объект будет оцениваться при загрузке классов ваших моделей и должен возвращать экземпляр Storage .

Читайте также: