Debian, Dojo, Django, Python

Делюсь опытом в описанных технологиях. Блог в первую очередь выполняет роль памяток для меня самого.

LESS для Google Material Icon Font

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

У Google для Web-разработки с использованием Angular Material есть даже специальный набор иконок, а так же репозиторий на GitHub с возможностью установки через Bower. Там всё хорошо, но вот CSS для иконок приходится по кускам собирать из официальной документации. Тут я и публикую такой LESS/CSS, собранный собственноручно по результатам чтения официальных доков.

Установка через Bower

bower install material-design-icons

Помимо шрифта в архиве куча иконок в разных форматах, так что будьте осторожны - bower скачает около 30 Мб, а потом будет его некоторое время распаковывать.

У меня все сторонние библиотеки хранятся в каталоге static/libs/, вам же следует изменить пути к шрифтам (переменная @BASE_PATH) на подходящие.

material-icons.less

@BASE_PATH: '/static/libs/material-design-icons/iconfont/MaterialIcons-Regular.';
@font-face {
    font-family: 'Material Icons';
    font-style: normal;
    font-weight: 400;
    src: url("@{BASE_PATH}eot");
    /* For IE6-8 */
    src: local('Material Icons'),
         local('MaterialIcons-Regular'),
         url("@{BASE_PATH}woff2") format('woff2'),
         url("@{BASE_PATH}woff") format('woff'),
         url("@{BASE_PATH}ttf") format('truetype');
}

.material-icons {
    font-family: 'Material Icons';
    font-weight: normal;
    font-style: normal;
    font-size: 24px;
    /* Preferred icon size */
    display: inline-block;
    width: 1em;
    height: 1em;
    line-height: 1;
    text-transform: none;
    letter-spacing: normal;
    word-wrap: normal;
    -webkit-font-smoothing: antialiased; /* Support for all WebKit browsers. */
    text-rendering: optimizeLegibility;  /* Support for Safari and Chrome. */
    -moz-osx-font-smoothing: grayscale;  /* Support for Firefox. */
    font-feature-settings: 'liga';       /* Support for IE. */
}

.material-icons.md-18 { font-size: 18px; }
.material-icons.md-24 { font-size: 24px; }
.material-icons.md-36 { font-size: 36px; }
.material-icons.md-48 { font-size: 48px; }

// Rules for using icons as black on a light background.
.material-icons.md-dark { color: rgba(0, 0, 0, 0.54); }
.material-icons.md-dark.md-inactive { color: rgba(0, 0, 0, 0.26); }

// Rules for using icons as white on a dark background.
.material-icons.md-light { color: rgba(255, 255, 255, 1); }
.material-icons.md-light.md-inactive { color: rgba(255, 255, 255, 0.3); }

Работает данный шрифт через лигатуры. В отличие от FontAwesome, который оперирует классами для тегов <span> и <i>, здесь нужно использовать и класс, и лигатуру:

Пример использования

<md-icon>
    <i class="material-icons md-24">menu</i>
</md-icon>

Полный список лигатур находится в каталоге material-design-icons/iconfont/codepoints. Так же есть отдельный ресурс с описанием и показом всех иконок.

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

Отправить комментарий

Django Rest Framework - обновление поля типа ImageField

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

Убил сегодня полдня на решение этой проблемы. Чтобы не забыть, сразу же публикую всё здесь.

Исходные данные

Дано:

  • Модель, имеющая поле типа ImageField
  • Django REST Framework
  • ngFileUpload на фронте

Задача: сделать возможным загрузку изображений в указанное поле на основе Class-Based View в DRF.

Решение

Фронт-энд:

Вёрстка

<img ng-src="{$ item.logo200x200 $}" ng-model="logo" ngf-select ngf-change="uploadLogo(files)" accept="image/*" />

Да, всего одна строка. Вы можете поместить указанное изображение в любой подходящий контейнер, например, панель из Twitter Bootstrap.

Что делает этот код:

Параметр Описание
ng-src="{$ item.logo200x200 $}" Связываем свойство модели и источник для нашего изображения. Делается через директиву Angular ng-src, как того советует официальная документация. На скобки в виде '{$' и '$}' не обращайте внимания. Т.к. на сервере используется стандартный шаблонизатор Django, приходится для Angular использовать другие скобки.
ng-model="logo" Для выбора файлов будет использоваться отдельная модель - logo
ngf-select Указываем, что данное изображение (можно использовать вообще-то что угодно) является полем ввода для плагина ngFileUpload
ngf-change="uploadLogo(files)" При изменении значения поля выполняем указанную функцию. Загрузка без нажатия кнопки "Загрузить", в общем, достаточно лишь выбрать файл.
accept="image/*" Разрешаем выбирать любые изображения. Фильтр для окна выбора файла.

После того, как будет произведён клик по указанному изображению, откроется обычное окно открытия файла. Когда же файл будет выбран, запустится функция загрузки изображения:

LogoController.js

$scope.uploadLogo = function() {
    if ($scope.logo.length < 1) {
        return;
    }
    Upload.upload({
        url: logoUrl, // /api/item/3/logo/
        file: $scope.logo,
        method: 'PATCH'
    }).success(function(data) {
        $scope.item.logo = data.logo;
    });
};

Я описал лишь одну функцию контроллера. Надеюсь, догадаться, что нужно инжектировать $scope и Upload, не сложно.

Обратите внимание, для загрузки логотипа используется метод PATCH, а файл логотипа помещяется в объект file - потом именно его будем обрабатывать на сервере.

Бэк-энд

Нам понадобятся модель, отдельный сериализатор для логотипов и отдельное представление. Так же размеры всех логотипов следует нормализовать - не более 200px по большей стороне. Для этого можно написать отдельную функцию - resize_logo(), принимающую как аргумент экземпляр нашей модели.

core.helpers.py

from PIL import Image

MAX_THUMBNAIL_SIZE = 200

def resize_logo(instance):
    """
    Resize model logo to needed sizes.
    """
    width = instance.logo.width
    height = instance.logo.height

    filename = instance.logo.path

    max_size = max(width, height)

    if max_size > MAX_THUMBNAIL_SIZE:  # Да, надо изменять размер
        image = Image.open(filename)
        image = image.resize(
            (round(width / max_size * MAX_THUMBNAIL_SIZE),
             round(height / max_size * MAX_THUMBNAIL_SIZE)),
            Image.ANTIALIAS
        )
        image.save(filename)

Пришло время описать саму модель, переопределив её метод save() таким образом, чтобы при сохранении размеры изображения для логотипа нормализовались, как нам нужно:

core.items.models.py

from os import path

from django.db import models

from core.helpers import resize_logo

class ItemModel(models.Model):

    name = models.CharField(
        "Название",
        max_length=255,
        help_text='Максимум 255 знаков',
        null=False,
        blank=False
    )
    logo = models.ImageField(
        "Логотип",
        upload_to=path.join('item', 'logo'), # Отдельный каталог для аватаров
        null=True,
        blank=True,
    )

    def save(self, *args, **kwargs):
        # Сначала модель нужно сохранить, иначе изменять/обновлять будет нечего
        super(ItemModel, self).save(*args, **kwargs)

        # Приводит размеры лого к одному виду - 200px по наибольшей стороне
        if self.logo:
            resize_logo(self)

    class Meta:
        app_label = 'core'
        db_table = 'item'
        verbose_name = 'элемент'
        verbose_name_plural = 'элементы'

Теперь можно описать части, относящиеся к API - сериализатор, представление и часть конфигурации URL.

api.items.serializers.py

from rest_framework import serializers

from core.items.models import ItemModel

# Тут должны быть описаны остальные сериализаторы, сейчас же опускаю для краткости


class ItemLogoSerializer(serializers.ModelSerializer):

    class Meta:
        model = ItemModel

Как видно, сериализатор крайне прост. Опишем наше представление.

api.items.api.py

from rest_framework import permissions
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView

from core.items.models import ItemModel

from .serializers import ItemLogoSerializer


class ItemLogoAPIView(APIView):

    permission_classes = [
        permissions.IsAdminUser,
    ]

    serializer_class = ItemLogoSerializer

    # Обновление модели - методом PATCH, как я уже писал выше
    def patch(self, *args, **kwargs):

        # Находим нужную модель (по-хорошему надо обернуть в try ... except, но
        # сейчас я этого делать не буду, чтобы не загромождать код)
        instance = ItemModel.objects.get(pk=kwargs.get('pk'))

        # Получаем из запроса наш файл (как указали выше, в JS)
        instance.logo = self.request.FILES['file']

        # Сохраняем запись (тут должна быть проверка значений встроенными в DRF
        # методами, но сейчас я этого делать не буду)
        instance.save()

        # Возвращаем ответ - нашу сериализованную модель и статус 200
        return Response(
            ItemLogoSerializer(instance).data,
            status=status.HTTP_200_OK
        )
Обязательно проверяйте, что именно приходит от клиента, иначе будут проблемы. Так же добавьте нужные права в permission_classes.

Теперь - самое простое - конфигурация URL:

api.items.urls.py

from django.conf.urls import url

# Тут должен быть импорт остальных сериализаторов
from .api import ItemLogoAPIView

urlpatterns = [
    # А здесь должны быть остальные URL (создание/получение/обнавление)
    url(r'^(?P\d+)/logo/$', ServiceLogoAPIView.as_view()),
]

Ну что ж, всё выглядит не таким уж сложным. Пришло время закрыть вопросы на Toster'е и StackOverflow.

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

Отправить комментарий

Django - изменить размер изображения перед сохранением

2 комментария

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

Вкратце:

  • Сохраняем объект
  • Проверяем наличие данных в нужном поле типа ImageField
  • Открываем сохранённое изображение и меняем его свойства, как нам нужно

Пример

from PIL import Image
from django import models

from os import path

# Максимальный размер изображения по большей стороне
_MAX_SIZE = 300

class CarManufacter(models.Model):
    """Производитель автомобилей, два поля
       name - название, строка
       logo - логотип, изображение
    """
    name = models.CharField(
        'Наименование',
        max_length=100,
        unique=True
    )
    logo = models.ImageField(
        upload_to=path.join('car', 'manufacter', 'logo'),
        null=True,
        blank=True
    )

    def save(self, *args, **kwargs):
        # Сначала - обычное сохранение
        super(CarManufacter, self).save(*args, **kwargs)

        # Проверяем, указан ли логотип
        if self.logo:
            filepath = self.logo.path
            width = self.logo.width
            height = self.logo.height

            max_size = max(width, height)

            # Может, и не надо ничего менять?
            if max_size > _MAX_SIZE:
                # Надо, Федя, надо
                image = Image.open(filename)
                # resize - безопасная функция, она создаёт новый объект, а не
                # вносит изменения в исходный, поэтому так
                image = image.resize(
                    (round(width / max_size * _MAX_SIZE),  # Сохраняем пропорции
                    round(height / max_size * _MAX_SIZE)),
                    Image.ANTIALIAS
                )
                # И не забыть сохраниться
                image.save(filename)

2 комментария :

Отправить комментарий

Зависимости в Angular

1 комментарий

Внеднение зависимостей

Все знают, что Angular построен на постоянном и вездесущем внедрении зависимостей, т.н. dependency injection. Существуют как минимум три способа сделать это.

1. Implicit annotation

Зависимости просто перечисляются как аргументы нашего модуля.

Пример

(function (A){
    "use strict";
    A.module('app', []).controller('MyCtrl', function($scope, $sce){
        //Тут должен быть код контроллера
    });
}(this.angular));
Не используйте этот способ!

Всё хорошо до момента минификации нашего кода. Это потому, что при таком объявлении функций Angular для определения нужных зависимостей превращает их код в строку, которую затем разбирает. А при минификации, как известно, все идентификаторы заменяются на более короткие, что делает наши скрипты неработоспособными.

2. $inject

Хороший способ внедрения зависимостей - создать статическое свойство функции под названием $inject. В нужный момент Angular возьмёт список зависимостей оттуда.

Пример

(function (A){
    "use strict";

    function MyCtrl($scope, $sce){
        //Код контроллера
    }

    MyCtrl.$inject = [ '$scope', '$sce' ]; //Строки не сжимаются никогда

    A.module('app', []).controller('MyCtrl', MyCtrl);
}(this.angular));

Уже лучше. Такой код успешно переживёт минификацию.

3. Inline array annotation

Весьма хороший метод, не хуже предыдущего, но многие статические анализаторы кода, например, в Eclipse, считают такое поведение ошибкой (Они не правы!):

Пример

(function (A){
    "use strict";

    function MyCtrl($scope, $sce){
        //Код контроллера
    }

    A.module('app', []).controller('MyCtrl', [ '$scope', '$sce', MyCtrl ]);
}(this.angular));

Такой код тоже хорошо переживёт минификацию.

Дополнительно

Недавно узнал, что в Angular даже есть специальная директива для запрета внедрения зависимостей первым способом - ng-strict-di. Указывать её надо рядом с ng-app:

Пример

<!doctype html>
<html ng-app="app" ng-strict-di>
    <head>
        <!-- Код -->
    </head>
    <body>
        <!-- И тут тоже код -->
    </body>
</html>

После этого попытка внедрения зависимостей через Implicit Annotation будет вызывать ошибку.

1 комментарий :

Отправить комментарий