Я изучаю ExtJs в связке с Django. Недавно реализовал построение элемента tree из файла json, содержащего данные из базы данных sqlite3.
Код данной реализации поместил на github: https://github.com/ArturV19/for_stack_overflow_extjs_lazy_load
Теперь у меня задача реализовать "ленивую" загрузку, то есть что бы сначала с сервера загружался только список жанров, а уже при нажатии на определённый жанр подгружались авторы, пишущие в данном жанре.
Код модели книги в models.py:
class BookModel(models.Model):
title = models.CharField(max_length=250)
author = models.CharField(max_length=200)
genre = models.CharField(max_length=190)
def __str__(self):
return self.author + ': ' + self.title + ' (' + self.genre + ')'
Код html-страницы templates/Index_LazyDownload.html:
{% load staticfiles %}
<html>
<head>
<meta charset="utf-8">
<title>Combobox</title>
<link rel="stylesheet" type="text/css" href="{% static 'extjs-4.1.1/resources/css/ext-all-debug.css' %}">
<script type="text/javascript" src="{% static 'extjs-4.1.1/ext-all-debug.js' %}"></script>
<script type="text/javascript" src="{% static 'lazyload_tree.js' %}"></script>
</head>
<body>
</body>
</html>
Код приложения ExtJs:
Ext.onReady(function () {
var store = Ext.create('Ext.data.TreeStore', {
proxy:{
type: 'ajax',
url: '../lazy_download_get_genres/'
}
});
var tree1 = Ext.create('Ext.tree.Panel', {
title: 'Жанры',
width: 400,
height: 400,
store: store,
rootVisible: false,
});
var formPanel = Ext.create('Ext.Panel', {
title: 'Форма ввода',
width: 250,
autoHeight: true,
bodyPadding: 10,
defaults: {
labelWidth: 100
},
items: [{
xtype: 'textfield',
id: 'searchField1',
fieldLabel: 'Поиск по названию:',
name: 'searchField',
},
{
xtype: 'button',
text : 'Начать поиск',
margin:'15 0 0 25',
listeners: {
click: function() {
panel.setLoading('Loading...')
store.load(
{
type: 'ajax',
url: '../get_json_search_books/'+Ext.getCmp('searchField1').getValue()
,
callback: function(records, operation, success) {
// the operation object
// contains all of the details of the load operation
panel.setLoading(false)
}});
}
}
}
],
renderTo: Ext.getBody()
});
var panel = Ext.create('Ext.panel.Panel', {
title: 'Книжная полка с попыткой "ленивой" загрузки:',
width: 1072,
padding:10,
height: 400,
autoload: true,
layout: {
type: 'hbox',
align: 'stretch'
},
items: [tree1, formPanel],
renderTo: Ext.getBody()
});
});
Далее идут ещё фрагменты кода python
TreeApp/urls.py:
from django.urls import path
from TreeApp import views
urlpatterns = [
path('', views.tree_index),
path('get_json_for_tree/', views.get_json_for_tree),
path('get_json_search_books/<str:text>', views.get_json_search_books),
path('lazy_load/', views.lazy_download_page),
path('lazy_download_get_genres/', views.lazy_download_get_genres),
]
TreeApp/view.py:
from django.http import HttpResponse
from django.shortcuts import render
from TreeApp.methods_for_lazy_load import json_of_genres_lazy_load
from TreeApp.work_with_database import get_string_json_genres_and_authors,
get_string_json_genres_and_authors_search
# Возвращает html-страницу
def tree_index(request):
return render(request, "Index.html")
# Возвращает JSON-объект для генерации дерева
def get_json_for_tree(request):
return HttpResponse(get_string_json_genres_and_authors())
# Возвращает JSON-объект для генерации дерева
# из книг, соответствующих условию
def get_json_search_books(request, text):
return HttpResponse(get_string_json_genres_and_authors_search(text))
############################################################################
# Далее идут методы для 'ленивой' загрузки
############################################################################
def lazy_download_page(request):
return render(request, "Index_LazyDownload.html")
def lazy_download_get_genres(request):
return HttpResponse(json_of_genres_lazy_load())
work_with_database.py:
from TreeApp.models import BookModel
# Получение авторов, пишущих в данном жанре
def get_authors_in_this_genre(genre):
book_list_in_genre = BookModel.objects.filter(genre=genre)
output = set()
for book in book_list_in_genre:
dict_author = {book.author}
output.update(dict_author)
return output
# Набор жанров
def get_set_of_unique_genres():
book_list = BookModel.objects.all()
output = set()
for book in book_list:
dict_genres = {book.genre}
output.update(dict_genres)
return output
def get_string_books_of_author_in_this_genre(genre, author):
target_books = BookModel.objects.filter(author=author, genre=genre)
string_books = '['
for book in target_books:
string_books += '{"text" : "' + book.title + '" , ' + '"leaf" : "true"},'
# Удаления запятой в конце
string_books = string_books[:-1] + '],'
return string_books
def get_string_books_of_author_in_this_genre_search(genre, author, books):
target_books = books.filter(author=author, genre=genre)
string_books = '['
for book in target_books:
string_books += '{"text" : "' + book.title + '" , ' + '"leaf" : "true"},'
# Удаления запятой в конце
string_books = string_books[:-1] + '],'
return string_books
# Получение json-строки для двухуровнего дерева,
# где сначала идёт список жанров
def get_string_json_genres_and_authors():
genres_list = get_set_of_unique_genres()
# Итоговая строка
string_request = '['
for genre in genres_list:
# Строка, относящаяся к отдельному жанру
string_genre = '{"text" : "' + genre + '", "children" : '
list_authors_in_genre = get_authors_in_this_genre(genre)
# Строка, относящаяся к отдельному автору в жанре
string_authors = '['
for author in list_authors_in_genre:
string_authors += '{"text" : "'+author+'" , "children" : '
string_authors += get_string_books_of_author_in_this_genre(genre, author)+' "leaf" : "false"},'
string_authors = string_authors[:-1]
string_genre += string_authors
string_genre += '], "leaf" : "false"},'
string_request += string_genre
string_request = string_request[:-1]+']'
return string_request
def get_string_json_genres_and_authors_search(search_string):
books = BookModel.objects.filter(title__contains=search_string)
# Список жанров
genres_output = set()
for book in books:
dict_genres = {book.genre}
genres_output.update(dict_genres)
# Итоговая строка JSON
string_request = '['
for genre in genres_output:
# Строка, относящаяся к отдельному жанру
string_genre = '{"text" : "' + genre + '", "children" : '
book_list_in_genre = books.filter(genre=genre)
# Список авторов
output_authors = set()
for book in book_list_in_genre:
dict_author = {book.author}
output_authors.update(dict_author)
# Строка, относящаяся к отдельному автору в жанре
string_authors = '['
for author in output_authors:
string_authors += '{"text" : "'+author+'" , "children" : '
string_authors += get_string_books_of_author_in_this_genre_search(genre, author, books)+' "leaf" : "false"},'
string_authors = string_authors[:-1]
string_genre += string_authors
string_genre += '], "leaf" : "false"},'
string_request += string_genre
string_request = string_request[:-1]+']'
return string_request
methods_for_lazy_load.py:
from TreeApp.work_with_database import get_set_of_unique_genres
def json_of_genres_lazy_load():
genres_list = get_set_of_unique_genres()
# Итоговая строка
string_request = '['
for genre in genres_list:
# Строка, относящаяся к отдельному жанру
string_genre = '{"text" : "' + genre + '"'
string_genre += ', "leaf" : "false"},'
string_request += string_genre
string_request = string_request[:-1]+']'
return string_request
Вывод в данный момент выглядит так:
Очень надеюсь на вашу помощь, из официальной документации нужной информации получить не смог.
Важно, нужна реализация именно в версии ExtJs 4.1.1
Ответ на данный вопрос был найден. Код решения выложен на GitHub по адресу: https://github.com/ArturV19/for_stack_overflow_extjs_lazy_load. Теперь, постараюсь подробно объяснить, как я организовал "ленивую" загрузку.
Во-первых, код самого приложения ExtJs (https://github.com/ArturV19/for_stack_overflow_extjs_lazy_load/blob/master/static/lazyload_tree.js) выглядит так:
Ext.onReady(function () {
let store = Ext.create('Ext.data.TreeStore', {
proxy:{
type: 'ajax',
url: '../lazy_download_get_data/'
}
});
let tree1 = Ext.create('Ext.tree.Panel', {
title: 'Жанры',
width: 400,
height: 400,
store: store,
rootVisible: false,
listeners: {
//Перед открытием нажатого узла дерева:
beforeitemexpand: function (node) {
//console.log('expand, ' + node.getDepth() + ' ');
//console.log('expand, ' + node.getDepth() + ' ' + node.parentNode.data.text);
//Устанавливаем значения дополнительных параметров:
store.getProxy().setExtraParam("node" , node.data.text);
store.getProxy().setExtraParam("level", node.getDepth());
store.getProxy().setExtraParam("parent_text", node.parentNode.data.text);
}
}
});
Ext.create('Ext.panel.Panel', {
title: 'Книжная полка с "ленивой" загрузкой',
width: 1072,
padding:10,
height: 400,
autoload: false,
layout: {
type: 'hbox',
align: 'stretch'
},
items: [tree1],
renderTo: Ext.getBody()
});
});
Вот так выглядит страница при загрузке:
Тут нужно остановиться на элементе tree.Panel подробнее. Дело в том, что при нажатии на любой элемент дерева будет происходить загрузка из хранилища store. Store, в свою очередь, выполняет запрос на сервер (в данном случае - ajax-запрос по адресу '../lazy_download_get_data/') и передаёт полученные с сервера данные "дереву". Поэтому, если не менять никаких параметров, то при нажатии на какой-либо жанр снова будет открываться список жанров, как было в моём вопросе до этого:
Теперь же, с каждым нажатием на элемент дерева, на сервер передаются такие данные как:
Кстати, следует отметить, что для формирования дерева tree JSON объект, получаемый с сервера, должен быть валидным, а передаваемые внутри JSON объекты должны содержать необходимые поля, например "text", "leaf" и другие. Более подробно советую почитать тут: https://metanit.com/web/extjs/7.6.php. Валидность кода я проверял с помощью сервиса https://jsonformatter.curiousconcept.com/.
Во-вторых, рассмотрим более подробно python-код, отвечающий за формирование текста, который станет JSON-объектом (https://github.com/ArturV19/for_stack_overflow_extjs_lazy_load/blob/master/TreeApp/methods_for_lazy_load.py):
from TreeApp.work_with_database import get_set_of_unique_genres, get_authors_in_this_genre, \
get_string_books_of_author_in_this_genre
# Получение JSON-файла с данными для формирования дерева
#
# node - текст записи, которая должна "раскрыться" при нажатии
# node==root - родительский элемент дерева, в который при открытии страницы загружается список жанров
#
# level - "глубина", на которой находится элемент дерева, который нужно раскрыть
#
# parent_text - текст, определяющий "родителя". В данном случае parent_text нужен, чтобы узнать, в котором именно
# жанре искать книги данного автора
def json_of_data_lazy_load(node, level, parent_text):
string_request = None
# Если только открываем страницу, формируем строку для JSON-файла со списком жанров
if str(node) == 'root':
# Список всех жанров
genres_list = get_set_of_unique_genres()
# Итоговая строка
string_request = '['
for genre in genres_list:
# Текст, относящийся к отдельному жанру
string_genre = '{"text" : "' + genre + '"'
string_genre += ',"leaf" : "false"},'
string_request += string_genre
# Удаления запятой в конце и добавление ']'
string_request = string_request[:-1] + ']'
# Если нажимаем на жанр, формируем JSON-строку с авторами, которые пишут в данном жанре
elif str(level) == '1':
# Список всех авторов, у которых есть хотя бы одна книга в данном жанре
authors_list = get_authors_in_this_genre(str(node))
# Итоговая строка
string_request = '['
for author in authors_list:
# Строка, относящаяся к отдельному автору
string_author = '{"text" : "' + author + '"'
string_author += ', "leaf" : "false"},'
string_request += string_author
string_request = string_request[:-1] + ']'
# Если нажимаем на имя автора, формируем JSON-строку с книгами, которые автор написал в данном жанре
elif str(level) == '2':
print('level 2')
string_request = get_string_books_of_author_in_this_genre(str(parent_text), str(node))
string_request = string_request[:-1]
print(string_request)
return string_request
При загрузке страницы формируется строка:
[{"text" : "Научная фантастика","leaf" : "false"},{"text" : "Ужасы","leaf" : "false"},{"text" : "Антиутопия","leaf" : "false"},{"text" : "Учебная литература","leaf" : "false"},{"text" : "Юмор","leaf" : "false"},{"text" : "Фантастика","leaf" : "false"},{"text" : "Тёмное фэнтези","leaf" : "false"}]
При нажатии на на жанр, например "Научная фантастика", будут переданы соответствующие параметры, и сформирован ответ:
[{"text" : "Рэй Брэ'дбери", "leaf" : "false"},{"text" : "Питер Уоттс", "leaf" : "false"}]
А при нажатии на автора, например, "Рэй Брэ'дбери", будет ответ:
[{"text" : "Марсианские хроники" , "leaf" : "true"},{"text" : "И грянул гром" , "leaf" : "true"}]
Думаю, для понимания кода этой информации будет достаточно. Кстати, так как пример учебный, то параметры для входа в admin-панель соответствующие: Username: admin, Password: pass. Надеюсь, информация будет полезна для людей, которые, как и я, изучают ExtJs.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Необходимо, чтобы синий(С) квадратик перемещался за зеленым(З) по оси ХНо если во время движения С квдарата переместить З квадрат, начинается...
Хотите улучшить этот вопрос? Добавьте больше подробностей и уточните проблему, отредактировав это сообщение
Я только начинаю работать с препроцессором LessНо компилировать его через Koola не хочется