LESS для Google Material Icon Font
У Google для Web-разработки с использованием Angular Material есть даже специальный набор иконок, а так же репозиторий на GitHub с возможностью установки через Bower. Там всё хорошо, но вот CSS для иконок приходится по кускам собирать из официальной документации. Тут я и публикую такой LESS/CSS, собранный собственноручно по результатам чтения официальных доков.
Установка через Bower
bower install material-design-icons
Помимо шрифта в архиве куча иконок в разных форматах, так что будьте осторожны - bower скачает около 30 Мб, а потом будет его некоторое время распаковывать.
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
, не сложно.
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 - изменить размер изображения перед сохранением
Как изменить размер изображения перед сохранением? Никак. Но далее я опишу путь, на который указал пользователь 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)
Зависимости в Angular
Внеднение зависимостей
Все знают, что 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 будет вызывать ошибку.
Комментариев нет :
Отправить комментарий