Добавить jquery в widget Django
14 августа 2018 г. 11:41
Есть два простых способа добавить jQuery в формы или виджеты в админку django
1. Добавление другой версии jQuery
Сначала нужно скачать jQuery. В качестве примера для этой статьи я использовал jQuery.2.2.0. Помещаем её, например, сюда: my_project/static/jquery/jquery-2.2.0.min.js.
from django import forms
from django.conf import settings
from my_app.models import MyModel
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
exclude = ()
class Media:
js = [settings.JQUERY_URL, 'my_app/my_script.js']
import os ... JQUERY_URL = os.path.join(STATIC_URL, 'jquery/jquery-2.2.0.min.js') ...
2. Использование Django jQuery
from django import forms
from django.conf import settings
from my_app.models import MyModel
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
exclude = ()
class Media:
extra = '' if settings.DEBUG else '.min' # to use minified libraries or not
js = [f'admin/js/vendor/jquery/jquery{extra}.js',
'admin/js/vendor/jquery/jquery.init.js'
'my_app/admin_compat.js',
'my_app/my_script.js']
if (window.django !== undefined) var jQuery = django.jQuery, $ = django.jQuery;
В зависимости от того, находится ли наш код в режиме разработки или в продакшене, используем полную или минифицированную версию jquery. Это нужно обязательно сделать, иначе будет либо на локальной машине, либо на продакшене загружаться две версии jquery - это не только избыточный код, но и может произойти, что подключённая библиотека локально может работать, а на продакшене нет и наоборот.
admin/js/vendor/jquery/jquery(/.min).js - версия jquery, которая идёт в коробке с Django.
admin/js/vendor/jquery/jquery.init.js - переименовывает $ в django.jQuery для того, чтобы избежать конфликта имён, если на странице используются разные версии библиотеки jquery (или даже, если другая библиотека использует для сокращения такой же знак $). Вот его код:
var django = django || {};
django.jQuery = jQuery.noConflict(true);
Но так как чаще мы используем знак $ в наших скриптах, например:
$('.test_btn').click(function(){
alert('clicked');
});
то нужно сделать обратное преобразование, как это сделано в admin_compat.js - добавляет $ (в том числе и jQuery) в пространство имён javascript.
Мы можем и не использовать admin_compat.js, но в таком случае нам придётся использовать django jquery так:
(function('.test_btn').click(function(){
alert('clicked');
})(django.jQuery);
Также, если мы хотим использовать $ в наших скриптах, нам не следует просто использовать jquery Django без jquery.init.js и admin_compat.js:
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
exclude = ()
class Media:
extra = '' if settings.DEBUG else '.min'
# плохое использование своих скриптов без jquery.init.js и admin_compat.js
js = [f'admin/js/vendor/jquery/jquery{extra}.js', 'my_app/my_script.js']
Потому что Django может использовать jquery.init.js при рендере других частей страницы, например, рендер inline-объектов, и в этом случае $ переименуется в django.jQuery и для вашего скрипта my_script.js функция $ будет undefined.
Ещё хочу напомнить, что если использовать шаблон админки унаследованный, например, от base_site.html, то нужно всё-равно явно выводить переменную media:
from django.core import management
from django.shortcuts import redirect, render
from django.views.generic import View
from my_app.forms import MyModelForm
class TestView(View):
template_name = 'admin/my_app/test_template.html'
form = MyModelForm
def get(self, request, *args, **kwargs):
return render(request, self.template_name, {'form': self.form()})
def post(self, request, *args, **kwargs):
form = self.form(request.POST)
if form.is_valid():
# some handling...
return redirect('/')
return render(request, self.template_name, {'form': form})
from django.conf.urls import url
from django.contrib.auth.decorators import user_passes_test
from my_app.views import TestView
urlpatterns = [
url(r'^my_test/$', user_passes_test(lambda u: u.is_superuser)(TestView.as_view()), name='my_test'),
]
{% extends "admin/base_site.html" %}
{% load i18n %}
{% block title %}Кастомная страница админки{% endblock %}
{% block extrahead %}{{ block.super }}
<script type="text/javascript" src="{% url 'admin:jsi18n' %}"></script>
{{ form.media }}
{% comment %}Примечание: Можно и сюда загружать свои скрипты и здесь управлять порядком загрузки скприптов{% endcomment %}
{% endblock %}
{% block content %}
<div class="fieldsets">
<fieldset class="module">
<h1>Кастомная страница админки</h1>
<p>Содержание страницы</p>
</fieldset>
</div>
{% endblock %}
Так как {{ media }} выводится только в change_form.html (и ещё delete_confirmation.html и delete_selected_confirmation.html, если быть точным):
{% extends "admin/base_site.html" %}
{% load i18n admin_urls static admin_modify sb_tags %}
{% block extrahead %}{{ block.super }}
<script type="text/javascript" src="{% url 'admin:jsi18n' %}"></script>
{{ media }}
{% endblock %}
{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}" />{% endblock %}
{% block coltype %}colM{% endblock %}
...
Порядок загрузки скриптов в формах и виджетах django
Выше описанные способы работают, когда хочешь загружать в админку свои скрипты. Но если есть такие виджеты, которые требуют jQuery, то нужно загружать jQuery до загрузки виджетов, так как при рендере страницы сначала загружаются скрипты виджетов, а потом уже то, что объявлено в Media у класса Form.
Пока я нашёл единственное решение - раширять виджеты, добавляя в класс Media jquery на первые позиции. Как это сделать, я покажу на примере django-select2.
Добавить jQuery в django-select2
Устарело!
Это актуально для django-select2 <= 7.7.1
Вот так захочешь по-быстрому поставить автокомплит Select2 для Select в админку Django:
from django_select2.forms import Select2Widget
class MyForm(forms.Form):
my_choice = forms.ChoiceField(widget=Select2Widget)
И не получается.. Потом выясняется из документации, что jQuery не идёт в комплекте с виджетом:
- jQuery version 2. This is not included in the package since it is expected that in most scenarios this would already be available.
Казалось бы, ладно, нужно просто добавить отдельно jQuery в форму, например так:
class MyAdminForm(forms.ModelForm):
class Media:
model = MyModel
js = ('path/to/jquery.js', ) # Объявленные скрипты в Media добавятся после скриптов всех используемых виджетов.
Но Select2 не заработает! Так как объявленные стили и скрипты в Media добавятся после стилей и скриптов всех используемых виджетов:
<head> ... <link href="/static/select2/css/select2.css" type="text/css" media="all" rel="stylesheet"> <link href="//cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/css/select2.min.css" type="text/css" media="screen" rel="stylesheet"> <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.min.js"></script> <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/i18n/ru.js"></script> <script type="text/javascript" src="/static/django_select2/django_select2.js"></script> <script type="text/javascript" src="/static/jquery/jquery.js"></script> ... </head>
Чтобы jquery загружалось раньше скриптов select2, я переопределил класс Media, создав свой класс Select2Widget и унаследовав его от базового:
from django.conf import settings
from django_select2.forms import Select2Widget as BaseSelect2Widget
class Select2Widget(BaseSelect2Widget):
"""
Added jquery to widget
"""
def _get_media(self):
media = super()._get_media()
css = ['select2/css/select2.css'] # если нужно какие-то стили переопределить
css.extend(media._css['screen'])
extra = '' if settings.DEBUG else '.min'
js = [f'admin/js/vendor/jquery/jquery{extra}.js', 'admin/js/jquery.init.js', 'my_app/js/admin_compat.js']
js.extend(media._js)
return forms.Media(js=js, css={'screen': css})
media = property(_get_media)
В этом коде, как видите, я использовал jquery, который идёт в комплекте с django. Если вы хотите использовать свою версию jquery, то просто замените строку:
extra = '' if settings.DEBUG else '.min'
js = [f'admin/js/vendor/jquery/jquery{extra}.js', 'admin/js/jquery.init.js', 'my_app/js/admin_compat.js']
на:
js = ['jquery/jquery.js']
Теперь можно также использовать Select2Widget, как описано в документации, импортируя свой Select2Widget:
from my_app.widgets import Select2Widget class MyForm(forms.Form): my_choice = forms.ChoiceField(widget=Select2Widget)
Представляю вашему вниманию книгу, написанную моим близким другом Максимом Макуриным: Секреты эффективного управления ассортиментом.
Книга предназначается для широкого круга читателей и, по мнению автора, будет полезна специалистам отдела закупок и логистики, категорийным и финансовым менеджерам, менеджерам по продажам, аналитикам, руководителям и директорам, в компетенции которых принятие решений по управлению ассортиментом.
Комментарии: 0