Как динамически отобразить данные модели в Html?
У меня есть модель, и в функции эта модель обновляется. Я хочу отображать данные этой модели динамически. Таким образом, новые значения должны отображаться без обновления страницы. Как я могу это сделать?
models.py
class MyLongProcess(models.Model):
active_uuid = models.UUIDField('Active process', null=True, blank=True)
name = models.CharField('Name', max_length=255)
current_step = models.IntegerField('Current step', default=0)
total = models.IntegerField('Total', default=0)
@property
def percentage_sending(self):
# or it can be computed by filtering elements processed in celery with complete status
return int((current_step / total) * 100)
views.py
def setup_wizard(request):
process = MyLongProcess.objects.create(active_uuid=uuid.uuid4(), name=name, total=100)
functions.myClass(..., process=process)
....
return render(request, 'setup_wizard.html', context)
functions.py
class myClass():
def __init__(self, ..., process):
self.download_all(..., process=process)
@app.task(bind=TRUE)
def download_all(self, ..., process):
....
for s in scans:
....
process.current_step += 1
process.save()
...
setup_wizard.html
<div class="progress-bar" role="progressbar"
style="width: {{ my_model_object.percentage_sending }}%;"
aria-valuenow="{{ my_model_object.percentage_sending }}"
aria-valuemin="0" aria-valuemax="100">{{ my_model_object.percentage_sending }}%
</div>
Все мои функции работают нормально. Когда я просматриваю MyLongProcess
из Django admin и обновляю страницу, значения обновляются. Просто я хочу отобразить его во фронтенде без обновления.
Это не то, для чего был предназначен Django. По сути, он рендерит статический HTML-контент и все. Что вы хотели бы сделать в такой трудоемкой функции, как эта, так это сначала отрисовать некоторый начальный контент (допустим, с прогрессом 0), а затем асинхронно уведомить фронтенд. Это может быть реализовано двумя способами. Первый способ, безусловно, проще в реализации, но он немного сложнее по ресурсам - это опрос. По сути, вы создаете специальную конечную точку в вашем urls.py, которая, при обращении к ней, будет выдавать вам процент выполнения задания, которое вы ищете. Затем вы можете иметь код javascript, который будет setInterval( (js-code), 1000)
обновлять прогресс каждую секунду. Просто чтобы дать вам представление о том, как это может быть реализовано:
document.addEventListener("DOMContentLoaded", function(event) {
var myElement = document.getElementById('my-element');
var task_id = myElement.getAttribute('data-task_id');
setInterval(function() {
var progressReq = new XMLHttpRequest();
progressReq.addEventListener("load", function() {
myElement.setAttribute('aria-valuenow', this.responseText);
});
progressReq.open("GET", "/progress-query/?task_id=" + task_id);
progressReq.send();
}, 1000);
});
HTTP-запрос действительно может выглядеть намного красивее с помощью jQuery, но я хотел просто дать вам представление о том, как это может выглядеть.
Для того чтобы это работало, вам придется изменить html-шаблон и включить в него id следующим образом:
<div ... data-task_id="some-task-id" />
если вы захотите опубликовать здесь значения, связанные с моделью, не забывайте хэшировать их, чтобы случайно не опубликовать необработанные PK базы данных или что-то подобное.
Второй подход, который немного сложнее в реализации, заключается в использовании вебсокета. Это означает, что когда фронтенд запускается, он будет слушать события, которые посылаются ему через вебсокет бэкенда. Таким образом, вы могли бы обновлять прогресс почти в реальном времени, а не каждую секунду. В обоих подходах код javascript должен получить доступ к элементу, который вы хотите обновить - либо через var element = document.getElementById( ... )
(что означает, что для начала у вас должен быть id), либо с помощью jQuery. затем вы можете обновить значение следующим образом: element.setAttribute('aria-valuenow', 10)
. Конечно, с помощью библиотек типа jQuery ваш код может выглядеть гораздо красивее, чем это.
p.s. не забудьте (если вы используете метод опроса) прервать цикл setInterval, как только прогресс достигнет 100 :-)