понедельник, 27 ноября 2017 г.

django-oscar tinymce 4 filebrowser

Задача: в дашборде django-oscar загружать изображения

1. Установка django-filebrowser-no-grappelli

- Открываем проект, загружаем виртуальное окружение и устанавливаем django-filebrowser-no-grappelli

$ pip install django-filebrowser-no-grappelli

- в settings.py добавляем

INSTALLED_APPS = (
    ...
    'filebrowser',

)


и

FILEBROWSER_DIRECTORY = './'

последнее, это путь относительно MEDIA_ROOT, куда будет иметь доступ приложение filebrowser (я поставил корень, но можно настроить и на вложенную папку)

- в urls.py добавляем

urlpatterns = [
    url(r'^admin/filebrowser/', include(site.urls)),

    ...
]

Теперь в админке можно просматривать, добавлять, удалять файлы из media


2. Прикручиваем filebrowser к django-oscar dashboard

По умолчанию в django-oscar dashboard стоит визуальный редактор для текстовых полей tinymce 4, добавляем для него настройки:

- в templates проекта создаем dashboard/layout.html со следующим содержимым:

{% extends "oscar/dashboard/layout.html" %}

{% block onbodyload %}
    options = {
        'languageCode': '{{ LANGUAGE_CODE }}',
        'tinyConfig': {
            valid_children : '+body[li],',
            entity_encoding: 'raw',
            statusbar: false,
            relative_urls : false,
            convert_urls : false,
            menubar: false,
            plugins: [
                        'advlist autolink lists link image charmap print preview anchor',
                        'searchreplace visualblocks code fullscreen',
                        'insertdatetime media table contextmenu paste code image'
            ],
            style_formats: [
                {title: 'Heading', block: 'h2'},
                {title: 'Subheading', block: 'h3'}
            ],
            toolbar: 'undo redo | insert | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | image | code',

            file_browser_callback: function(input_id, input_value, type, win){
                var cmsURL = '/admin/filebrowser/browse/?pop=4';
                cmsURL = cmsURL + '&type=' + type;
  
                tinymce.activeEditor.windowManager.open({
                    file: cmsURL,
                    width: 800,  // Your dimensions may differ - toy around with them!
                    height: 600,
                    resizable: 'yes',
                    scrollbars: 'yes',
                    inline: 'yes',  // This parameter only has an effect if you use the inlinepopups plugin!
                    close_previous: 'no'
                }, {
                    window: win,
                    input: input_id,
                });
                return false;
            },
        }
    };
    oscar.dashboard.init(options);

    {{ block.super }}
{% endblock %}



На этом все, можно переходить в дашборд и добавлять изображение для текстовых полей.

пятница, 22 сентября 2017 г.

При нажатии на кнопку показать текст и прокрутить страницу

<div>
  <button id="feedback_button" class="btn btn-big">
    Задать вопрос
   </button>
</div>
<div id="feedback_form" style="display: none">
  <br>
  <h3 >Форма обратной связи</h3>
  <div>Какая-то форма</div>
</div>


  <script type="text/javascript">
    $('#feedback_button').click(function() {
      //отображаю скрытый #feedback_form
      $("#feedback_form").show( "slow" );
      //прокручиваю страницу до #feedback_form
      $('html, body').animate({
          scrollTop: $("#feedback_form").offset().top
      }, 2000);
    });
  </script>

среда, 9 августа 2017 г.

Русская дата в Django

Столкнулся с локализацией даты в Django, а точнее в Django Rest Framework хотел видеть в json ответе названия месяца на русском.

В моем Django приложении есть models.py:

class Place(models.Model):
    date_end = models.DateTimeField('date end')
    date_start = models.DateTimeField('date start')

    street = models.CharField(max_length=200)
    building = models.CharField(max_length=20)

    
    def __str__(self):
        return "{} {}, {:%d %b %Y %H:%M} - {:%d %b %Y %H:%M}".format(self.street, self.building,
            self.date_start, self.date_end
            )




В настройках Django - settings.py установлена русская кодировка:
LANGUAGE_CODE = 'ru-ru'
USE_I18N = True
USE_L10N = True


Казалось бы, кодировка установлена, но все равно и DRF и админке в дате выдает название месяца на английском:
"31 Jul 2017 09:00"

Нашел рецепт, который нужно поместить в settings.py:
from django.conf.locale.ru import formats as ru_formats
ru_formats.DATETIME_FORMAT = "d b Y H:M"

не сработало.

Стал смотреть в сторону локализации в python. И все оказалось просто, надо добавить в settings.py Django проекта:
import locale
locale.setlocale(locale.LC_ALL, "ru_RU.utf8")

и теперь дата, и в админке и в Django Rest Framework, преобразовывается в русскую:
"31 июл 2017 09:00"

Вроде бы вопрос решен, но только если проект исключительно для русской аудитории, а если проект интернациональный, то я пока решения не нашел.

пятница, 28 апреля 2017 г.

Select2 ajax запрос к Django rest framework

При использование select  нужно будет подгружать для option много данных. Выход есть - использовать альтернативу -  select2.
Далее, привожу пример AJAX подгрузки данных для select2 из django rest framework

{% block javascript %}
  <script>
    $(document).ready(function() {
      $("#select-person").select2({
        language: "ru",
        placeholder: "Введите ФИО человека",
        ajax: {
          url: "/api/company/people/", //drf url для people
          dataType: 'json',
          delay: 300,
          data: function (params) {
            return {
              name: params.term, //name - api поле для поиска, params.term - данные из поля поиска
              //page: params.page
            };
          },
          processResults: function (data, params) {
            params.page = params.page || 1;
            //console.log(data.results); //drf возвращает данные в results
            //console.log(data.count); //drf возвращает количество записей в count
            return {
              results: data.results, //берем из json ответа results
              pagination: {
                more: (params.page * 30) < data.count
              }
            };
          },
          cache: true
        },
        //debug: true,
        escapeMarkup: function (markup) { return markup; }, // let our custom formatter work
        minimumInputLength: 1, //минимальное количество символов для запроса
        templateResult: function(results) {
          return results.name; //берем для select option поле модели - name
        },
        templateSelection: function(results) {
          return results.name || results.text;
        }
      });
    });
  </script>
{% endblock %}
 
{% block body %}
  <select class="form-control" id="select-person">
    <!--
    <option value="" selected="selected"></option>
    -->
  </select>
{% endblock %}


Для django также имеется django-select2 и django-easy-select2

Django ajax обновление данных на странице

Имеем веб страницу index.html, на которой необходимо обновлять данные (без перезагрузки всей страницы), т.е. сделать AJAX запрос. На странице имеется поле ввода номера телефона и подгрузка другой страницы - {% include 'phone-filter.html' %}


        <form>
          <div>
            <label>Номер:</label>
            <input type="text" id="phone" placeholder="Введите номер телефона">
          </div>
        </form>

        <div class="phone-filter">
          <!-- Подгружаю номера телефонов из фильтра -->
          {% include 'phone-filter.html' %}
        </div>


Код страницы phone-filter.html, это то что будет обновлятся на странице index.html

{% if phonenumbers%}
  <ul>
  {% for phonenumber in phonenumbers %}
    <li>
      <a href="{{ phonenumber.get_absolute_url }}">{{ phonenumber }}</a>
    </li>
  {% endfor %}
  </ul>
{% else %}
  По вашему запросу ничего не найдено
{% endif %}



Добавляю jQuery скрипт на страницу index.html, который обрабатывает ввод данных поля id="phone"

<script>
    $("#phone").keyup(function(){
      console.log(this.value);
      $('.phone-filter').html('').load(
        "{% url 'phone-filter' %}?phone=" + this.value
      );
    });

</script>

т.е. при вводе данных в поле phone будет грузиться страница phone-filter.html (тот самый AJAX запрос, который и нужно реализовать)

В urls.py добавляю

    url(r'^phone-filter/', views.phone_filter, name='phone-filter'),


В views.py создаю функцию ответа за запрос

def phone_filter(request):
    if request.method == 'GET':
        queryset = PhoneNumber.objects.filter(is_deleted = False)
        phone = request.GET.get('phone')

   
        if phone:
            queryset = queryset.filter(phone__contains=phone)

   
        response = render(
            request,
            'phone-filter.html',
            {'phonenumbers':queryset}
        )
    

        return response


Это всё, что нужно сделать для динамической AJAX подгрузки данных из базы, без обновления всей страницы.


четверг, 30 марта 2017 г.

Ember js 404 Page Not Found

Если страница не найдена, то Ember js выдает сообщение в консоль:
UnrecognizedURLError
и при этом показывается пустая страница, как будто сайт не работает.
Я погуглил в интернете на эту тему и нашел решение в статье -

Все достаточно просто создаем роут not-found
$ ember g route not-found --path=/*path

В router должна появится строка

Router.map(function() {
  ...
  this.route('not-found', { path: '/*path' })
});


Т.е. все что не подходит под имеющиеся роуты будет отправлено на not-found.
В route not-found надо прописать:

export default Ember.Route.extend({
  redirect: function () {
    var url = this.router.location.formatURL('/not-found');
    if (window.location.pathname !== url) {
      this.transitionTo('/not-found');
    }
  }
});


В template not-found надо прописать:

<h1>404 Страница не найдена</h1>
<p>
  Возможно, ссылка была переделана. Обратитесь к администратору сайта.

</p>


понедельник, 20 февраля 2017 г.

Django Active Directory user backend

В статье Django аутентификация через NGINX kerberos (Active Directory) я описал как использовать прозрачную (sso) аутентификацию.
Django RemoteUserBackend работает так как надо, но мне понадобилось для создаваемого пользователя добавлять из Active Directory дополнительную информацию, а именно ФИО и email. Схема получилась такая:
1) Пользователь первый раз заходит на сайт example.com
2) RemoteUserBackend создает учетную запись пользователя (user.username) в Django
3) ... и дополнительно загружает информацию с использованием класса ADRemoteUserBackend

Ниже привожу код LDAPRemoteUserBackend.py с комментариями, что и куда добавлять.


"""
ADRemoteUserBackend добавляет фамилию, имя и email из Active Directory. Используется
как расширение RemoteUserBackend.
У меня данный LDAPRemoteUserBackend.py находится в папке back, там же где и settings.py
Для аутентификации надо убрать RemoteUserBackend и добавить в settings.py:
 
AUTHENTICATION_BACKENDS = [
    #'django.contrib.auth.backends.RemoteUserBackend',
    'back.LDAPRemoteUserBackend.ADRemoteUserBackend',
    'django.contrib.auth.backends.ModelBackend',
]
 
"""

from django.contrib.auth.backends import RemoteUserBackend
from django.conf import settings as s
from ldap3 import Server, Connection, SIMPLE, SYNC, ASYNC, SUBTREE, ALL


"""
Если созданному пользователю надо добавить права superuser (например, пользователь admin), то
 
python manage.py shell
 
from django.contrib.auth.models import User
user = User.objects.get(username=admin)
user.is_staff = True
user.is_superuser = True
user.save()
"""

# Можно прописать следующие Константы в settings.py, а можно и здесь (чтобы не засорять settings.py)
# DNS имя сервера Active Directory
s.AD_SERVER = "srv.example.com"
# Пользователь (логин) в Active Directory - нужно указать логин в AD в формате 'EXAMPLE\aduser' или 'aduser@example.com'
s.AD_USER = 'EXAMPLE\userad'
s.AD_PASSWORD = 'FADSfjkb,.^%'
s.AD_SEARCH_TREE = 'dc=exampe,dc=com'


class ADRemoteUserBackend(RemoteUserBackend):
    def clean_username(self,username):
        clean_username=username.split('@')[0]
        return clean_username

    def configure_user(self,user):
        #Если нужно пускать пользователей в админку, то раскоментировать следующую строку
        #user.is_staff=True

        #connect to ldap server
        server = Server(s.AD_SERVER)
        conn = Connection(server, user=s.AD_USER, password=s.AD_PASSWORD)
        conn.bind()
        AD_FILTER = '(&(objectCategory=Person)(sAMAccountName='+user.username+'))'
        # get user Common-Name, email, first name, last name, отчество, full Name - attributes ['cn', 'mail', 'givenName', 'sn', 'initials', 'displayName']
        conn.search(s.AD_SEARCH_TREE, AD_FILTER, SUBTREE, attributes =['cn', 'mail', 'givenName', 'sn', 'initials', 'displayName'])

        for entry in conn.entries:
            if entry.mail:
                user.email = entry.mail
            try:
                displayName = entry.displayName.value
            except:
                displayName = entry.cn.value
            if displayName:
                displayName = displayName.split(" ")
            else:
                displayName = ""
            if (displayName[1] and displayName[2]):
                user.first_name = displayName[1] + " " + displayName[2]
            elif (displayName[1]):
                user.first_name = displayName[1]
            if displayName[0]:
                user.last_name = displayName[0]
        #close LDAP Connection
        conn.unbind()
        user.save()

django-oscar tinymce 4 filebrowser

Задача: в дашборде django-oscar загружать изображения 1. Установка django-filebrowser-no-grappelli - Открываем проект, загружаем виртуа...