понедельник, 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()

Как используя python получить информацию из Active Directory

Во многих локальных сетях используется Microsoft Active Directory.
Используя python с библиотекой ldap3 можно получить информацию для выгрузки в вебпроекты или просто посмотреть нужную информацию (например, когда под пользователем последний раз подключались).

Библиотека python ldap3 - http://ldap3.readthedocs.io
Установка:
pip install ldap3

Использование:
- Ниже привожу код, пример использования. В коде есть поясняющие комментарии.


from ldap3 import Server, Connection, SIMPLE, SYNC, ASYNC, SUBTREE, ALL

# домен - example.com
# DNS имя сервера Active Directory
AD_SERVER = 'srv.example.com'
# Пользователь (логин) в Active Directory - нужно указать логин в AD 

# в формате 'EXAMPLE\aduser' или 'aduser@example.com'
AD_USER = 'EXAMPLE\aduser'
AD_PASSWORD = 'FDfashv,.@#'
AD_SEARCH_TREE = 'dc=example,dc=com'

server = Server(AD_SERVER)
conn = Connection(server,user=AD_USER,password=AD_PASSWORD)
conn.bind()
# в ответ должно быть - True

# Поиск в Active Directory
# примеры ldap фильтров можно посмотреть здесь -
# https://social.technet.microsoft.com/wiki/contents/articles/8077.active-directory-ldap-ru-ru.aspx
# Я в нижеследующем фильтре:
# - исключаю всеx отключенных пользователей (!(UserAccountControl:1.2.840.113556.1.4.803:=2))
# - добавляю только тех пользователей у которых заполнено имя и фамилия
# - и вывожу атрибуты - attributes
# Все возможные атрибуты Active Directory можно посмотреть здесь -
# https://msdn.microsoft.com/en-us/library/ms675090%28v=vs.85%29.aspx
conn.search(AD_SEARCH_TREE,'(&(objectCategory=Person)(!(UserAccountControl:1.2.840.113556.1.4.803:=2))(givenName=*)(sn=*))',
    SUBTREE,
    attributes =['cn','proxyAddresses','department','sAMAccountName', 'displayName', 'telephoneNumber', 'ipPhone', 'streetAddress',
    'title','manager','objectGUID','company','lastLogon']
    )
# после этого запроса в ответ должно быть - True

# можно посмотреть на результат
print(conn.entries)
# или вывести только Common-Name - cn
for entry in conn.entries:
    print(entry.cn)

# Найти пользователя с логином admin (sAMAccountName=admin) и показать информацию по нему
conn.search(AD_SEARCH_TREE,'(&(objectCategory=Person)(sAMAccountName=admin))', SUBTREE,
    attributes =['cn','proxyAddresses','department','sAMAccountName', 'displayName', 'telephoneNumber', 'ipPhone', 'streetAddress',
    'title','manager','objectGUID','company','lastLogon']
    )

conn.entries

воскресенье, 19 февраля 2017 г.

Ember file upload to Django

Ни на сайте emberjs.com, ни в еще какой документации я так и не нашел описания - как загрузить файл с использование Ember js. И действительно, средствами Ember js - никак. Поэтому нужно использовать либо js XMLHttpRequest, либо jQuery ajax.

Я разрабатываю - фронтэнд на  Ember js, бакэнд на Django. Мне надо загрузить файлы на бакэнд.
Порядок загрузки файла на фронэнде следующий:

1) Находится файл на странице:
let fileUpload = document.getElementById('file-field').files[0]; 

2) Создается formData и в нее добавляются все необходимые для отправки данные, в том числе и файл:
let formData = new FormData();
formData.append('creator', creator_id);
formData.append('attachment', fileUpload); 

3) Собранная  formData отсылается (POST) на url бакэнда с добавлением дополнительных заголовков.

Сначала пример POST запроса с использованием XMLHttpRequest

//1
let fileUpload = document.getElementById('file-field').files[0];
//2
let formData = new FormData();
formData.append('creator', creator_id);
formData.append('attachment', fileUpload); 
//3
let req = new XMLHttpRequest();
req.open('POST', url);
let jwtoken = this.get('session.data.authenticated.access_token');
req.setRequestHeader('Authorization', 'Bearer ' + jwtoken);
req.send(formData)



А теперь пример отправки файла с использованием jQuery ajax, взятого из рабочего кода:

route.js

  actions: {
    saveFile() {
      let model = this.controller.get('model');
      let creator_id = this.get('session.data.authenticated.user.id');
      let fileUpload = document.getElementById('file-field').files[0];
 
      let host = this.store.adapterFor('application').get('host');
      let url = host + '/api/task/taskfiles/';
 
      let formData = new FormData();
      formData.append('task', model.get('task.id'));
      formData.append('creator', creator_id);
      formData.append('attachment', fileUpload);
 
      let jwtoken = this.get('session.data.authenticated.access_token');
 
      Ember.$.ajax({
        url: url,
        type: 'POST',
        beforeSend: function(request) {
          request.setRequestHeader('Authorization', 'Bearer ' + jwtoken);
        },
        data: formData,
        processData: false,
        contentType: false
      })
        .then((response)=>{
          console.log(response);
          this.store.findRecord('task/taskfile', response.id);
          this.transitionTo('task.taskfiles');
        })
        .catch(response => {
          //console.log(response);
          if (response.responseJSON.attachment) {
            this.controller.set('errorMessage', 'File do not selected');
          }
        });
 
    },

template.hbs

  <h3>Adding a new file</h3>
 
  <p>{{errorMessage}}</p>
  <input type='file' id='file-field' class="form-control" name="file"/>
  <button class="btn btn-success form-control" type="submit" {{action 'saveFile' model}}>{{t "Save"}}</button>

На Ember фронэнтде разобрались как отсылать.
Теперь настройка Django бакэнда, а точнее Django-rest-framework, для приема файла

В serializers.py все стандартно

class TaskFileSerializer(serializers.ModelSerializer):
    class Meta:
        model = TaskFile
        fields = ('id', 'creator', 'task','attachment')


А во views.py надо добавить:

from rest_framework.parsers import MultiPartParser, FormParser
 
TaskFileViewSet(viewsets.ModelViewSet):
    queryset = TaskFile.objects.all()
    serializer_class = TaskFileSerializer
    parser_classes = (MultiPartParser, FormParser)


Вроде и не сложно.

Ember document title

В Ember js для html head title я использую - 
https://github.com/kimroen/ember-cli-document-title

Поддерживает как статические, так и динамические title

Пример:

- В route - tasks добавляю статический title

// front/app/tasks/route.js 
export default Ember.Route.extend({
  title: 'Задачи',
});


- В route - application добавляю динамический title

// front/app/application/route.js
export default Ember.Route.extend({
  title: function(tokens) {
    return tokens.join(' - ') + ' Название сайта';
  },

});

суббота, 18 февраля 2017 г.

Django rest framework SessionAuthentication и JSONWebTokenAuthentication

Столкнулся с такой ситуацией:
- Опубликовал Ember фронтэнд на Django бакэнде. GET запросы проходят, а вот при POST запросе вылетает ошибка:

POST http://localhost:8000/api/tasks/ 403 (Forbidden)
Error: Adapter operation failed

Смотрю в отладчике хрома network responce:

{"detail":"CSRF Failed: CSRF token missing or incorrect."}

Ничего не понимаю. При разработке Ember фронтэнда POST запросы с localhost:4200 на localhost:8000 пролетают без проблем, а тут при POST запросе с localhost:8000 на localhost:8000 вылетает ошибка связанная с CSRF. Смотрю в Django debug, сравниваю POST запросы с localhost:4200 и localhost:8000 - в обоих случаях передается в request header - authorization - access_token, а вот втором случае дополнительно в request попадают Cookie и Session data. Ну и что с того, что дополнительная информация падает? Смотрю настройки settings.py бакэнда для REST_FRAMEWORK - DEFAULT_AUTHENTICATION_CLASSES прописано:

REST_FRAMEWORK = {
    ...
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
    ),
}


и тут до меня доходит, что при POST запросах с localhost:4200 на localhost:8000 используется только  'rest_framework_jwt.authentication.JSONWebTokenAuthentication' , а при POST запросах с localhost:8000 на localhost:8000 сначала попадая на 'rest_framework.authentication.SessionAuthentication' проходит сразу аутентификация, потому что имеются session data, и до аутентификации JSONWebTokenAuthentication дело не доходит. Поэтому я взял и поменял порядок аутентификации - поставил сначала JSONWebTokenAuthentication, а потом SessionAuthentication:

REST_FRAMEWORK = {
    ...
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',    

    ),
}


Теперь все POST запросы проходят! Это тот случай, когда порядок имеет значание!

пятница, 17 февраля 2017 г.

Ember Django deploy v2

Ну, неугомонный я, хочется как попроще. Я уже сделал один вариант публикации Ember фронэнда на Django бакэнде. Теперь еще один вариант.

Структура разработки Django - Ember сайта такая:

SITENAME (это название сайта)
    |
    |-back (это Django проект, здесь внути и будет находится static)
    |
    |-front (это Ember проект)


1) На Django бакэнде добавить:

в settings.py

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static"),
]
STATIC_URL = '/static/'



в urls.py

from .views import index

urlpatterns = [
    ...

    url(r'^$', index, name='index'),
    url(r'^(?P<path>.*)/$', index),
]


во views.py

from django.shortcuts import render

def index(request, path=''):
    response = render(request, 'index.html')
    return response



2) На Ember фронтэнде

Просто запускаем в папке front:

$ ember serve --output-path ../back/static/

Используя ключ --output-path ../back/static/ можно заменить путь вывода скомпиленного фронэнда (по умолчанию папка dist).

После запуска фронтэнда, видим, что в папке back создалась папка static и внутри лежит файл index.html и папка assets (в ней лежат js скрипты и css фронтэнда).
Нам нужен файл index.html, копируем его в back/templates, открываем его и видим строки, которые ссылаются на assets

<link rel="stylesheet" href="/assets/vendor.css">
<link rel="stylesheet" href="/assets/front.css">


<script src="/assets/vendor.js"></script>
<script src="/assets/front.js"></script>


Т.к. django не знает ничего про папку assets, зато знает где находится static (в котором теперь есть assets), то надо просто добавить перед assets - static. Вот что должно получится:

<link rel="stylesheet" href="/static/assets/vendor.css">
<link rel="stylesheet" href="/static/assets/front.css">


<script src="/static/assets/vendor.js"></script>
<script src="/static/assets/front.js"></script>


Теперь и на бакэнде (localhost:8000) можно наблюдать фронтэнд.

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

$ ember build -prod --output-path ../back/static/
Ну и скопировать и отредактировать index.html, как было описано выше.

Можно добавить в файл ember-cli-build.js

  var app = new EmberApp(defaults, {
    fingerprint: {
      prepend: '/static/'
    }
  });


Строка prepend: '/static/' позволит не добавлять префикс static в index.html.
Теперь, после выполнения команды

$ ember build -prod --output-path ../back/static/

в index.html уже будут строки похожие на такие

    <link rel="stylesheet" href="/static/assets/ember-app-cd507f10f1eb6d96c8c44db034f28a4f.css">
    <script src="/static/assets/ember-app-03dac61f48f7ccdae5ae0aa2db976ec8.js"></script>


и, теперь, достаточно будет скопировать index.html в templates без всяких изменений.


На мой взгляд, второй вариант получился проще.

Чтобы на фронтэнде при запуске не вспоминать путь --output-path ../back/static/ я сделал shell скрипт в папке front:

- Создаю файл run.sh
$ vi run.sh

- Добавляю туда следующие строки:

#!/bin/bash
ember serve --output-path ../back/static/


- Делаю файл run.sh исполняемым

$ chmod +x run.sh

Теперь можно запускать фронтэнд  командой
./run.sh

четверг, 16 февраля 2017 г.

Публикуем (deploy) Ember фронтэнд в Django бакэнде

Разработка это, конечно, интересно, но приходит время деплоя (публикации фронтэнда). Перед публикацией на хостинге надо проверить работу на локальном компьютере.
Как и куда поместить данные из фронтэнда в бакэнд я сейчас расскажу.
Если непонятна структура связки Django - Ember, то смотрите статью про Создание среды для разработки веб приложения под Django framework и Ember js

Итак, делаем тестовый деплой:

1. На Django бакэнде

Приложение frontend можно и не создавать, но с ним элегантней смотрится (понятно, что и откуда берется).

- Создаем приложение - frontend:
$ python manage.py startapp frontend

-  В back/back/settings.py в INSTALLED_APPS добавляю приложение 'frontend' и сразу добавляю путь к статике фронэнда:

INSTALLED_APPS = [
    ...

    #local apps
    'frontend.apps.FrontendConfig',
]

 
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "frontend/templates"),
]



- создаем back/frontend/urls.py

# back/frontend/urls.py

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^$', views.index, name='index'),

    url(r'^(?P<path>.*)/$', index),
]


- в back/back/urls.py добавляем ссылку на  'frontend.urls' 

# back/back/urls.py

from django.conf.urls import url, include
...

 
urlpatterns = [
    url(r'^', include('frontend.urls')),
    ...
]


- в back/frontend/views.py создаем функцию index  которая будем выводить index.html на главную страницу нашего Django бакэнда:

# back/frontend/views.py

from django.shortcuts import render


def index(request, path=''):
    response = render(request, 'index.html')
    return response


- создаем папку templates в back/frontend/

- в back/frontend/templates/ создаем для проверки  index.html
- в back/frontend/templates/index.html добавляем строку

<h1>Главная страница Django бакэнда</h1>

- Запускаем django бакэнд
$ python manage.py runserver

и смотрим http://localhost:8000/ должна быть надпись: Главная страница Django бакэнда


2. На Ember фронтэнде

переходим в папку ember фронэнда - у меня называется front

- добавляю в файл front/ember-cli-build.js префикс 'static/'

//front/ember-cli-build.js
 
var EmberApp = require('ember-cli/lib/broccoli/ember-app');

module.exports = function(defaults) {
  var app = new EmberApp(defaults, {
    fingerprint: {
      prepend: 'static/'
    }
  });
  return app.toTree();
};


Смысл в том, что в Django файлы статики ищутся в 'static' ,а в Ember создается для статики папка assets, я пошел по простому пути и добавил префикс 'static/'

- теперь выполняем команду:

$ ember build -prod

все благополучно "собралось" в папку front/dist

- содержимоем папки front/dist копирую в back/frontend/templates/

и запустив в консоли Django бакэнд -
$ python manage.py runserver
смотрю в браузере http://localhost:8000/.

Таким образом, "собрав" исходники на фронэнде и опубликовав их на бакэнде можно посмотреть, предварительно, как это все работает на одном сервере, т.е. теперь не нужно запускать фронэнд.

Например, можно отключить CORS на бакэнде
CORS_ORIGIN_ALLOW_ALL = False
и посмотреть на результат.


Есть еще и второй вариант Ember Django deploy v2

django-oscar tinymce 4 filebrowser

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