Объясните работу функции, пожалуйста!
Есть задача добавить возможность просмотров поста(с возможностью накрутки) в учебных целях. В интернете нашел такой код:
models.py:
class PageHit(models.Model):
url = models.CharField(max_length=1000, unique=True)
count = models.PositiveIntegerField(default=0)
views.py:
from django.db.models import F
from functools import wraps
from django.db import transaction
def counted(f):
@wraps(f)
def decorator(request, *args, **kwargs):
with transaction.atomic():
counter, created = PageHit.objects.get_or_create(url=request.path)
counter.count = F('count') + 1
counter.save()
return f(request, *args, **kwargs)
return decorator
Этим декоратором оборачивается view
для показа поста.
Код работает исправно, и при нажатии на ссылку для перехода на пост количество просмотров действительно увеличивается, но я не понимаю как работает эта функция. Пожалуйста, объясните.
Каждый раз, перед тем как вызывать данный view
, обернутый этим декоратором, в PageHit
с данным url
инкрементируется count
:
def counted(f):
@wraps(f)
def decorator(request, *args, **kwargs):
# делаем транзакцию атомической (если что-то зафейлится, изменения ролбэкнутся)
with transaction.atomic():
# получаем объект PageHit с данным url (если его еще не существует, создаем)
counter, created = PageHit.objects.get_or_create(url=request.path)
# инкрементируем его counter (counter.count += 1 тоже бы сработало)
counter.count = F('count') + 1
# сохраняем
counter.save()
# наконец вызываем сам view который декорировали
return f(request, *args, **kwargs)
return decorator
Советую почитать, как работают декораторы. Вкратце на ваши вопросы:
f
- это сама функция, которую мы оборачиваем, в данном случае нашview
(поэтому в конце мы ее вызываем).decorator
здесь довольно сбивающее с толку название, т.к. это не декоратор.counted
в данном примере является декоратором, а то что здесь названоdecorator
обычно называютwrapper
, это функция которая подменяет (оборачивает) декарируемую функцию:@counted def view(...): ...
эквивалентно:
def view(...): ... view = counted(view)
F
- вспомагательный метод django ORM, для более "низкоуровневой" коммуникации с ДБ через SQL. Как и писал выше, можно использовать более простой метод с+=
.wraps
используется для того, чтобы сохранить оригинальноее имя (view.__name__
) и оригинальную документацию (view.__doc__
) декарируемой функции. Иначе ее имя заменится (в данном случае) наdecorator
, а документация пропадет, т.к. уdecorator
нет док-строки.get_or_create
возвращает кортеж из двух значений(object, created)
- созданный или найденный объект и булеан был объект создан или найден уже существующий в ДБ по URL. Можно использовать_
для неиспользованных переменных:
counter, _ = PageHit.objects.get_or_create(url=request.path)
но created
более наглядно (IMHO).