Добавить jquery в widget Django

Есть два простых способа добавить jQuery в формы или виджеты в админку django

1. Добавление другой версии jQuery

Сначала нужно скачать jQuery. В качестве примера для этой статьи я использовал jQuery.2.2.0. Помещаем её, например, сюда: my_project/static/jquery/jquery-2.2.0.min.js.

my_app/forms.py
# -*- coding: utf-8 -*-
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']
settings.py
import os

...
JQUERY_URL = os.path.join(STATIC_URL, 'jquery/jquery-2.2.0.min.js')
...

2. Использование Django jQuery

my_app/forms.py
# -*- coding: utf-8 -*-
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 = ['admin/js/vendor/jquery/jquery{}.js'.format('' if settings.DEBUG else '.min'),
               'admin/js/vendor/jquery/jquery.init.js'
               'my_app/admin_compat.js',
               'my_app/my_script.js']
my_app/admin_compat.js
if (window.django != undefined) var jQuery = django.jQuery, $ = django.jQuery;

admin/js/vendor/jquery/jquery.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:
         # плохое использование своих скриптов без jquery.init.js и admin_compat.js
         js = ['admin/js/vendor/jquery/jquery{}.js'.format('' if settings.DEBUG else '.min'),
               'my_app/my_script.js']

Потому что Django может использовать jquery.init.js при рендере других частей страницы, например, рендер inline-объектов, и в этом случае $ переименуется в django.jQuery и для вашего скрипта my_script.js функция $ будет undefined.

Ещё хочу напомнить, что если использовать шаблон админки, унаследованный, например, от base_site.html, то нужно всё-равно явно выводить переменную media:

my_app/views.py
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.core import management
from django.shortcuts import redirect, render
from django.views.generic import View

from my_app.forms import MyModelForm


class RunManagementCommandView(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})
my_app/urls.py
# -*- coding: utf-8 -*-
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'),
]
my_app/templates/admin/my_app/test_template.html
{% 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, если быть точным):

lib/python2.7/site-packages/django/contrib/admin/templates/admin/change_form.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

Вот так захочешь по-быстрому поставить автокомплит Select2 для Select в админку Django:

from django_select2.forms import Select2Widget

class MyForm(forms.Form):
    my_choice = forms.ChoiceField(widget=Select2Widget)

И не получается.. Потом выясняется из документации, что jQuery не идёт в комплекте с виджетом:

External Dependencies
- 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 добавятся после стилей и скриптов всех используемых виджетов:

my_app/my_model/1/change/ (просмотр кода в браузере загруженной страницы)
<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 и унаследовав его от базового:

my_app/widgets.py
from django_select2.forms import Select2Widget as BaseSelect2Widget

class Select2Widget(BaseSelect2Widget):
    """
    Added jquery to widget
    """
    def _get_media(self):
        media = super(Select2Widget, self)._get_media()
       
        extra = '' if settings.DEBUG else '.min'
        js = ['vendor/jquery/jquery%s.js' % extra, 'jquery.init.js']
        js = ['admin/js/%s' % url for url in js]
        js.append('my_app/admin_compat.js')

        # insert scripts before Select2Widget scripts
        for i, url in enumerate(js):
            media._js.insert(i, url)

        return media

    media = property(_get_media)

В этом коде, как видите, я использовал jquery, который идёт в комплекте с django. Если вы хотите использовать свою версию jquery, то просто замените строки:

extra = '' if settings.DEBUG else '.min'
js = ['vendor/jquery/jquery%s.js' % extra, 'jquery.init.js']
js = ['admin/js/%s' % url for url in js]
js.append('my_app/admin_compat.js')

# insert scripts before Select2Widget scripts
for i, url in enumerate(js):
    media._js.insert(i, url)

на:

media._js.insert(0, 'jquery/jquery.js')

Теперь можно также использовать Select2Widget, как описано в документации, импортируя свой Select2Widget:

my_app/forms.py
from my_app.widgets import Select2Widget

class MyForm(forms.Form):
   my_choice = forms.ChoiceField(widget=Select2Widget)

Оцените статью

5 из 5 (всего 1 оценка)

Поля, отмеченные звёздочкой ( * ) , являются обязательными.

Спасибо за ваш отзыв!

После нажатия кнопки "Отправить" ваше сообщение будет доставлено мне на почту.

Автор статьи

Права на использование материала, расположенного на этой странице https://vivazzi.pro/it/jquery-to-widget-django/:

Разрешается копировать материал с указанием её автора и ссылки на оригинал без использования параметра rel="nofollow" в теге <a>. Использование:

Автор статьи: Мальцев Артём
Ссылка на статью: <a href="https://vivazzi.pro/it/jquery-to-widget-django/">https://vivazzi.pro/it/jquery-to-widget-django/</a>

Подробнее: Правила использования сайта

Комментариев: 0

Вы можете оставить комментарий как незарегистрированный пользователь.

Но зарегистрировавшись, вы сможете:

  • получать оповещения об ответах
  • просматривать свои комментарии
  • иметь возможность использовать все функции разработанных сервисов

Для комментрирования от своего имени, войдите или зарегистрируйтесь обычным способом или через социальные сети:

Отправить

На данный момент нет специального поиска, поэтому я предлагаю воспользоваться обычной поисковой системой, например, Google, добавив "vivazzi" после своего запроса.

Попробуйте