Добавить 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