СУХОЙ способ добавления созданных/модифицированных и временных
Что-то вроде
- created_by
- created_date
- MODIFIED_BY
- MODIFIED_DATE
Будет очень распространенным шаблоном для большого количества таблиц.
1) Вы можете автоматически установить созданную дату (но не другие) в model.py с помощью
created_date = models.DateTimeField(auto_now_add=True, editable=False)
2) Вы могли бы создать/измененные даты (но не через /user, так как не иметь контекста запроса) в model.py с
def save(self):
if self.id:
self.modified_date = datetime.now()
else:
self.created_date = datetime.now()
super(MyModel,self).save()
3) Вы можете установить созданную/модифицированную дату и в admin.py - но это не касается обновлений не admin
def save_model(self, request, obj, form, change):
if change:
obj.modified_by = request.user
obj.modified_date = datetime.now()
else:
obj.created_by = request.user
obj.created_date = datetime.now()
obj.save()
4) И последнее место будет в view.py, который может выполнять все 4, но не распространяется на обновления администратора.
Таким образом, реалистично должна быть разбросана логика, минимально повторяемая в 3 и 4 (или метод на модели, вызванной из обоих, которая будет пропущена)
Чем лучше? (Я работаю с python/django в течение нескольких дней, поэтому легко можно было бы пропустить что-то очевидное)
- Можете ли вы сделать что-то вроде @login_required, например. @audit_changes
- Можете ли вы получить доступ к запросу и текущему пользователю в модели и централизовать там логику?
Ответы
Ответ 1
Даты создания/модификации теперь могут обрабатываться Django, поэтому их можно реализовать так:
class BaseModel(models.Model):
created_date = models.DateTimeField(auto_now_add=True)
modified_date = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
Добавив это в базовый класс абстрактной модели, его можно легко добавить ко всем моделям приложения.
Сохранение пользователя сложнее, так как request.user
недоступен. Как упоминалось в SeanOC, это разделение проблем между веб-запросом и уровнем модели. Либо вы проходите это поле все время, либо сохраняете request.user
в threadlocal. Django CMS делает это для своей системы разрешений.
class CurrentUserMiddleware(object):
def process_request(self, request):
set_current_user(getattr(request, 'user', None))
И отслеживание пользователя происходит в другом месте:
from threading import local
_thread_locals = local()
def set_current_user(user):
_thread_locals.user=user
def get_current_user():
return getattr(_thread_locals, 'user', None)
Для не-веб-сред (например, команд управления) вам нужно вызвать set_current_user
в начале script.
Ответ 2
Для временных моделей, которые вы, вероятно, захотите посмотреть django-model-utils или django-extensions. Каждый из них включает абстрактные базовые классы, которые автоматически обрабатывают созданную и последнюю измененную временную метку. Вы можете использовать эти инструменты напрямую или посмотреть, как они решили проблему, и придумать собственное решение.
Что касается других вопросов:
Можете ли вы сделать что-то вроде @login_required, например. @audit_changes
Потенциально да, но вы должны быть очень осторожны, чтобы сохранить потоки в безопасности. То, что вы потенциально могли бы сделать, это ваш декоратор @audit_changes, установите флаг для включения аудита в threadlocal. Затем, либо в методе сохранения ваших моделей, либо в обработчике сигналов, вы можете проверить свой флаг аудита и записать свою аудиторскую информацию, если флаг установлен.
Можете ли вы получить доступ к запросу и текущему пользователю в модели и централизовать там логику?
Да, но вы будете делать компромисс. По мере того как вы немного коснулись, существует очень четкое и преднамеренное разделение проблем между Django ORM и битами обработки запроса/аутентификации. Существует два способа получить информацию из запроса (текущего пользователя) в ORM (ваша модель (ы)). Вы можете вручную управлять обновлением информации о создателе/модификаторе на своих объектах или вы можете настроить механизм автоматического управления этой работой по техническому обслуживанию. Если вы возьмете ручной подход (передавая информацию через вызовы методов из запроса в представлении ORM), это будет больше кода для поддержки/тестирования, но вы сохраните разделение проблем. Благодаря ручному подходу вы окажетесь в гораздо лучшей форме, если вам придется работать с вашими объектами за пределами цикла запроса/ответа (например, cron-скрипты, отложенные задачи, интерактивная оболочка). Если вы согласны с нарушением этого разделения проблем, тогда вы можете настроить что-то, где вы установите поток локальный с текущим пользователем в промежуточном программном обеспечении, а затем посмотрите на этот поток локально в методе сохранения вашей модели. Обратившись к ручному подходу, у вас будет меньше кода для работы, но вам будет гораздо труднее, если вы когда-нибудь захотите работать с вашими объектами за пределами цикла запроса/ответа. Кроме того, вы должны быть очень осторожны, чтобы сохранить все поточно-безопасные с более автоматизированным подходом.
Ответ 3
Можете ли вы импортировать объект модели пользователя и вызвать get_current()?
Кроме того, я думаю, вы можете вызвать views в admin.py.