Какой самый простой способ уведомить клиентов об изменениях в модельной таблице, которую они просматривают?
Я использовал Django Rest Framework для настройки API, который обслуживает клиентов по шаблонной таблице товаров и позволяет им менять покупателей на лету.
В настоящее время я использую повторяющийся запрос jQuery AJAX с setTimeout в течение 2 секунд. Это отправляет тонну запросов и данных, даже если нет никаких изменений, а размер веб-страницы продолжает расти.
Мне пришлось отключить кэширование, так как у некоторых пользователей может быть IE11.
Я начал искать способ выдвигать обновлений для клиентов и начал исследовать каналы Django и Server-Sent-Events.
Каналы Джанго
- Создание демо-чата
- Очень быстро
- Веб-сокеты поддерживаются всеми моими целевыми браузерами
- Похоже на излишество за то, чего я пытаюсь достичь.
- Много настроек
- Требуется Redis или другое хранилище данных
- Мне действительно не нужна двусторонняя связь
- Мое приложение размещено на Pythonanywhere , что не позволяет ASGI и
похоже, не планирует делать это ( 1 , 2 ).
События, отправленные сервером
- Очень мало информации о том, как настроить это для Django
- Нет встроенной поддержки в IE11 или Edge , но доступно полифилов
- Нашел и протестировал рабочий пример из stackoverflow , а не
хотя точно уверен, что он делает. Веб-страница обновляется каждые 5 секунд, не знаю, где это контролируется.
- Очень мало настроено, кажется почти волшебным
- Похоже, было бы идеально использовать это с post_save
сигнал в Django, но я не могу понять, как это настроить.
Ток, на основе AJAX:
models.py
...
...
class Buyer(models.Model):
name = models.CharField(unique=True, max_length = 20)
class Item(models.Model):
name = models.CharField(unique=True, max_length = 50)
active = models.BooleanField(default=True)
bought_by = models.ForeignKey(Buyer, null=True, blank=True, to_field="name",)
views.py
...
...
class ItemViewSet(viewsets.ModelViewSet):
queryset = models.Item.objects.select_related("bought_by")
serializer_class= serializers.ItemSerializer
filterset_fields = ("bought_by")
renderer_classes = [renderers.JSONRenderer, renderers.BrowsableAPIRenderer, renderers.TemplateHTMLRenderer]
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
if request.accepted_renderer.format == "html":
items = list()
for item in queryset:
items.append({"serializer": self.get_serializer(item), "item": item})
return Response(
{
"items_info": items,
"style": {"template_pack": "rest_framework/inline/"},
},
template_name="myapp/items_list.html",
)
else:
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
...
...
(метод списка был изменен на , чтобы клиенты могли редактировать каждый элемент )
handler.js
...
...
$.ajaxSetup({
cache: false
});
var tableUpdater = null;
var updateRequest = null;
// helper that can be called to cancel active timer/ajax in the
// case of interaction with buttons/selects on the page or
// in the case of a new request
function stopUpdate() {
if (tableUpdater || updateRequest) {
clearTimeout(tableUpdater);
updateRequest.abort();
}
}
// Update data table
function tableUpdate() {
stopUpdate();
updateRequest = $.ajax({
type: "GET",
url: "myapp/items/?format=html",
success: function(data) {
$("#activeRequests").html(data);
// schedule another AJAX request
tableUpdater = setTimeout(tableUpdate, 2000);
}
});
}
...
...