Jump to content

abrahadabra

User
  • Posts

    294
  • Joined

  • Last visited

  • Days Won

    9

Everything posted by abrahadabra

  1. а разве там не буферизация вывода?
  2. Вам объяснить, почему брезгуют table-cell? Бывают сложности с размерами. Всё-таки у таблиц работа с размерами отличная от блоков, ячейки часто не слушаются свойств width и height, отдавая предпочтение контенту. А вот оснований не использовать флекс не вижу. На нём раскладка делается легко и красиво. А если нужна поддержка IE8, то под условным комментарием подключаем отдельный файл стилей, в котором флоаты, а на высоту забить.
  3. а как же минус один http-запрос?
  4. Если не возражаете, укажу на главные ошибки. Это главная ошибка. Нужно было сделать именно то, что вы делать не стали. это следующая ошибка. Не надо такого делать через js. это не просто. Но, кажется, вы уже сами это поняли. Если признаёте свою слабость в js, то не надо хвататься сразу за сложные вещи, особенно когда это неправильное применение. И особенно, когда на css это делается очень просто. Ну и подумайте логически, что вы делаете. Вы устанавливаете высоту элементам. Им жёстко задаётся высота, типа как инлайновые стили. А потом по событию resize вы делаете что: читаете высоту каждой колонки и всем устанавливаете максимальную. А какую высоту вы читаете? Конечно же, ту самую, которая установлена предыдущим вызовом функции.
  5. ага, как по этому поводу говорят: писать на php можно на любом языке Да, доводилось видеть чудеса =)
  6. В битриксе вроде ничего так, понять можно. Если не чистоплюйствовать, то годная штука. А вот юми не удалось победить
  7. вообще-то градиент только фоном и делается.
  8. Ошибаетесь. По части готовых решений в случае с питоном и руби всё очень и очень хорошо. Про php просто не знаю, но скорее всего тоже неплохо. Предполагаю, что паритет. Всё-таки есть, да. Да и подвоха-то особого нет, всё на поверхности. Мы же говорим о сайтах, а php с рождения заточен под веб-разработку. Например, php — один из лучших шаблонизаторов. А ещё есть такая штука, как порог вхождения. Вспомните себя, когда только начинали интересоваться темой. Много всего, с какой стороны подойти — неясно. А тут легко и просто предлагают прямо в html писать <? echo "Hello World"; ?>, и оно работает. Неважно как, но работает! Что такое http? А чёрт его знает! MVC? Это три буквы, наверное матерные. А echo работает. Чтобы написать хэлловорлд на том же питоне, нужно как минимум осилить сам питон, осознать, что есть http, понять, как работают сервера, одолеть wsgi, и только тогда получится вывести в браузер одну строчку. А как оформлять? Шаблонизаторы надо осваивать. А php сам шаблонизатор. И инструменты типа MAMP есть, в которых достаточно одну кнопочку нажать, чтобы сервер запустился. Ну а когда въехал во что-то одно, в этом же предпочитают и развиваться. Меня в своё время отец научил критически воспринимать любую инфу. Есть php? Хорошо! Большинство сайтов на нём? Отлично! Миллионы мух не могут ошибаться. Но что ещё есть? какой именно?)) вы там много привели =) про Yii ?да-да, именно это многокода на Yii2.
  9. избыточного в php — туча синтаксического мусора, унаследованного от разработки 1969 года. Кстати, wwt, вы же php, в отличие от меня, знаете? Выше пример решения простой типовой задачи на php. Он точно рабочий, скопирован из действующего кода. Но по мне так это запредельный говнокод. Вот в нём что не так? Почему так?
  10. тогда пишите инлайновые. Этот вариант для такого случая и хорош.
  11. Чуть выше ради сравнения были простыни кода. Не очень понятно наверное получилось, поскольку очень уж длинно. Можно попробовать маленькие короткие задачки. Вот например, в одном из проектов недавно нужен был метод, возвращающий случайный год с 1789 по 1825. Дважды нужен был, потому что код не изоморфный. JS: const START_YEAR = 1789, END_YEAR = 1825;class EventPool extends Material { … get randomYear() { return Math.Round(Math.random * (END_YEAR - START_YEAR)) + START_YEAR; } …}Python: import randomclass EventPool(Material): START_YEAR: 1789 END_YEAR: 1825 … def get_random_year(self): return random.choice(range(START_YEAR, END_YEAR))то же на каком-нибудь php было наверное примерно так: $START_YEAR = 1789;$END_YEAR = 1825;class EventPool extends Material { … public function getRandomYear() { return( rand($START_YEAR, $END_YEAR); ); }}или на ruby: class EventPool < Material @START_YEAR = 1789 @END_YEAR = 1825 … def getRandomYear rand @START_YEAR..@END_YEAR endendJS в этом примере выглядит довольно странно по сравнению с остальными тремя, которые достаточно легко воспринимаются, даже не зная языка. Но у php и ruby на мой взгляд синтаксис избыточен. И всё равно JS победит из-за возможности делать код изоморфным.
  12. abrahadabra

    Front-end & HTTP

    К вёрстке же это однозначно не относится. Но вообще фронтэнд как без http? Насчёт must know — сомневаюсь. Но жирным плюсом будет точно.
  13. jinja вообще на любителя, если уж на то пошло. Но мне, кстати, джанговские шаблоны именно тем и нравятся, что они чистые от логики. Не нравится в них совершенно другое. Медленные они. Но и это до какой-то степени решается, например кэшированием. А вот про собственные тэги и фильтры — удивительно. Их же весьма просто и удобно делать. Если тэги оказываются сложными, то тут большой-большой вопрос, всё ли в порядке с архитектурой приложения.
  14. и не оспоришь. Так и есть. В этом смысле совсем непонятно, когда на php используют сторонние шаблонизаторы. Чаще всего джанговских шаблонов более чем достаточно. Финты с фильтрами? Не проще ли тогда свой фильтр или тэг сделать? Но вообще есть же jinja2. А ещё можно на django делать только бэкэнд, rest-api, а всю красоту на клиенте творить.
  15. Не, давайте обычное. Самое обычное, обычнее некуда. Просто пишу сейчас, впечатляюсь, аж волосы дыбом стают (а они у меня по пояс длинные, их надо суметь так поднять). И на YII2(php) vs Django(python). Про RoR можно додумать, YII с него писался. Только язык Ruby попроще, чем php. Задача: лента новостей. Самая обычная лента новостей. Список и детальная страница каждой отдельной новости. У каждой новости есть заголовок, картинка, дата, основной текст, анонс. Адреса: /news/ для списка, /news/1/ для детальной. Основной текст и анонс могу произвольно оформляться html/css. В админке для этого визуальный редактор. Все поля обязательны, кроме анонса, если анонс не задан, он формируется из первых 20 слов основного текста, но так, чтобы все открытые тэги оказались закрыты. Лента новостей на двух языках: русском и английском. В админке переключаемся по вкладкам, пишем в варианты полей для каждого из языков свой текст. В публичной части адрес предваряется префиксом /ru/ или /en/, и в зависимости от этого выбираем тексты на нужном языке. Казалось бы, очень простая задача, наитипичнейшая. Модель на YII2 (php): <?php/** * Created by PhpStorm. * User: abrahadabra * Date: 22.12.15 * Time: 12:42 */namespace app\models;use Yii;use yii\db\ActiveRecord;use yii\helpers\StringHelper;use yii\web\UploadedFile;/** * This is the model class for table "news". * * @property integer $id * @property string $date * @property string $name * @property string $name_ru * @property string $name_en * @property string $announce * @property string $announce_ru * @property string $announce_en * @property string $text * @property string $text_ru * @property string $text_en * @property string $image * @property string $filename *//** * @var UploadedFile image attribute */class News extends ActiveRecord{ public $image; public $del_img; /** * @inheritdoc */ public static function tableName() { return 'news'; } /** * @inheritdoc */ public function rules() { return [ [['date', 'name_ru', 'name_en', 'text_ru', 'text_en'], 'required'], [['date', 'image'], 'safe'], [['image'], 'image', 'extensions' => 'png,jpg,jpeg', 'skipOnEmpty' => true], [['del_img'], 'boolean'], [['name', 'name_ru', 'name_en'], 'string', 'max' => 256], [['announce', 'announce_ru', 'announce_en'], 'string', 'max' => 2048], [['text', 'text_ru', 'text_en'], 'string', 'max' => 65535], ]; } /** * @return \yii\db\ActiveQuery */ public function getLanguage() { return $this->hasOne(Language::className(), ['id' => 'language_id']); } public function upload() { if ($this->validate()) { $this->image->saveAs('news/' . $this->image->baseName . '.' . $this->image->extension); $this->image = $this->image->baseName . '.' . $this->image->extension; return true; } else { return false; } } public function getImageUrl() { if ($this->filename) { return "/news/".$this->filename; } else { return ""; } } public function getThumbUrl() { if ($this->filename) { return "/news/thumbs/".$this->filename; } else { return ""; } } /** * @inheritdoc */ public function attributeLabels() { return [ 'id' => 'ID', 'date' => Yii::t('app', 'Дата'), 'filename' => Yii::t('app', 'Картинка'), 'image' => Yii::t('app', 'Загрузить картинку'), 'del_img' => Yii::t('app', 'Удалить картинку'), 'name' => Yii::t('app', 'Заголовок'), 'name_ru' => Yii::t('app', 'Заголовок'), 'name_en' => Yii::t('app', 'Заголовок'), 'announce' => Yii::t('app', 'Анонс'), 'announce_ru' => Yii::t('app', 'Анонс'), 'announce_en' => Yii::t('app', 'Анонс'), 'text' => Yii::t('app', 'Текст'), 'text_ru' => Yii::t('app', 'Текст'), 'text_en' => Yii::t('app', 'Текст'), ]; } public function getName() { $lang = Language::getCurrent(); return trim($this->getAttribute("name_".$lang->alias)); } public function getAnnounce($strict=false) { $lang = Language::getCurrent(); $announce = trim($this->getAttribute("announce_".$lang->alias)); if ($strict || strlen($announce) > 0) { return $announce; } else { return StringHelper::truncateWords($this->getText(), 20, '…'); } } public function getText() { $lang = Language::getCurrent(); return trim($this->getAttribute("text_".$lang->alias)); }}Ещё нужна модель языка, но я её приводить не буду, а то и так слишком много кода. Для той же задачи модель на Django (Python): from django.db import modelsfrom django.utils.translation import ugettext_lazy as _from ckeditor.fields import RichTextFieldclass News(models.Model): name = models.CharField(_('title'), max_length=256) date = models.DateField(_('date'), default=timezone.now) image = models.ImageField(_('image'), upload_to='news', null=True, blank=True) announce = RichTextField(_('announce'), config_name="announce", max_length=2048, null=True, blank=True) text = RichTextField(_('text')) def get_absolute_url(self): return reverse('news', args=[str(self.pk)]) def __str__(self): return self.name class Meta: ordering = ['-date'] verbose_name = _('article') verbose_name_plural = _('news')Здесь первое, на что можно обратить внимание — количество кода. Второе — его осмысленность. В случае YII абсолютно невозможно уверенно утверждать о структуре модели, только некоторые косвенные признаки, метод валидации, метод заголовков полей. Указаны поля отдельно для загрузки картинки, удаления картинки и хранения адреса картинки. В случае Django чётко видна структура модели. Перечислены все поля, для каждого поля его тип, подпись, валидация, значения по умолчанию. Для картинки, как и ожидается, одно поле, все типовые действия над которым реализованы в соответствующем классе. В классе модели же методы для вычисления адреса страницы объекта и строкового представления (что необязательно, но в ряде случаев удобно). Там же сортировка по умолчанию. Для Django нужна ещё регистрация модели как переводимой: from modeltranslation.translator import translator, TranslationOptionsfrom news.models import Newsclass NewsTranslationOptions(TranslationOptions): fields = ('name', 'announce', 'text')translator.register(News, NewsTranslationOptions) Теперь это добро надо вывести в админку. Не буду приводить весь код для YII2. Вот только контроллер: <?php/** * Created by PhpStorm. * User: abrahadabra * Date: 22.12.15 * Time: 13:14 */namespace app\modules\dashboard\controllers;use Yii;use app\models\News;use yii\data\ActiveDataProvider;use yii\web\Controller;use yii\web\NotFoundHttpException;use yii\filters\VerbFilter;use yii\web\UploadedFile;use yii\imagine\Image;class NewsController extends Controller{ public $defaultAction = 'index'; public function behaviors() { return [ 'verbs' => [ 'class' => VerbFilter::className(), 'actions' => [ 'delete' => ['post'], ], ], ]; } /** * Lists all News models. * @return mixed */ public function actionIndex() { $dataProvider = new ActiveDataProvider([ 'query' => News::find(), ]); return $this->render('index', [ 'dataProvider' => $dataProvider, ]); } /** * Displays a single News model. * @param integer $id * @return mixed */ public function actionView($id) { return $this->render('view', [ 'model' => $this->findModel($id), ]); } /** * Creates a new News model. * If creation is successful, the browser will be redirected to the 'view' page. * @return mixed */ public function actionCreate() { $model = new News(); if ($model->load(Yii::$app->request->post()) && $model->save()) { $this->uploadImage($model, "news/"); return $this->redirect(['view', 'id' => $model->id]); } else { return $this->render('create', [ 'model' => $model, ]); } } protected function createDirectory($path) { if (!file_exists($path)) { mkdir($path, 0775, true); } } protected function uploadImage($model, $imagePath) { $file = UploadedFile::getInstance($model, 'image'); if($model->del_img) { if(file_exists(Yii::getAlias('@webroot'.$model->getImageUrl()))) { unlink(Yii::getAlias('@webroot'.$model->getImageUrl())); $model->filename = ''; $model->save(); } } if ($file && $file->tempName) { $fileName = $file->baseName . '.' . $file->extension; $this->createDirectory($imagePath); $file->saveAs($imagePath . $fileName); $this->createDirectory($imagePath . "thumbs/"); Image::thumbnail($imagePath . $fileName, 180, 115)->save($imagePath . "thumbs/" . $fileName, ['quality' => 80]); $model->filename = $fileName; $model->save(); } } /** * Updates an existing News model. * If update is successful, the browser will be redirected to the 'view' page. * @param integer $id * @return mixed */ public function actionUpdate($id) { $model = $this->findModel($id); if ($model->load(Yii::$app->request->post()) && $model->save()) { $this->uploadImage($model, "news/"); return $this->redirect(['view', 'id' => $model->id]); } else { return $this->render('update', [ 'model' => $model, ]); } } /** * Deletes an existing News model. * If deletion is successful, the browser will be redirected to the 'index' page. * @param integer $id * @return mixed */ public function actionDelete($id) { $this->findModel($id)->delete(); return $this->redirect(['index']); } /** * Finds the News model based on its primary key value. * If the model is not found, a 404 HTTP exception will be thrown. * @param integer $id * @return News the loaded model * @throws NotFoundHttpException if the model cannot be found */ protected function findModel($id) { if (($model = News::findOne($id)) !== null) { return $model; } else { throw new NotFoundHttpException('The requested material does not exist.'); } }}Обратим внимание на экшны этого контроллера, на эту тучу кода для типовых-типовых задач. А ещё обратим внимание на метод загрузки картинки. Вручную загружаем, проверяем существование каталога и т.д. и т.п. Сразу поясню, что это способ, рекомендованный официальной документацией. Но контроллера мало. Нужно вывести страницы. Список: <?phpuse yii\helpers\Html;use yii\grid\GridView;/* @var $this yii\web\View *//* @var $dataProvider yii\data\ActiveDataProvider */$this->title = Yii::t('app', 'Новости');$this->params['breadcrumbs'][] = $this->title;?><div class="news-index"> <h1><?= Html::encode($this->title) ?></h1> <p> <?= Html::a('Create News', ['create'], ['class' => 'btn btn-success']) ?> </p> <?= GridView::widget([ 'dataProvider' => $dataProvider, 'columns' => [ ['class' => 'yii\grid\SerialColumn'], 'id', [ 'label' => Yii::t('app', 'Заголовок'), 'format' => 'html', 'content' => function($data){ return $data->getName(); }, ], ['class' => 'yii\grid\ActionColumn'], ], ]); ?></div>Форма добавления/редактирования: <?phpuse yii\helpers\Html;use yii\helpers\Url;use yii\widgets\ActiveForm;use vova07\imperavi\Widget;use yii\jui\DatePicker;/* @var $this yii\web\View *//* @var $model app\models\News *//* @var $form yii\widgets\ActiveForm */$thumbUrl = $model->getThumbUrl();$imageUrl = $model->getImageUrl();?><div class="news-form"> <?php $form = ActiveForm::begin([ 'options' => ['enctype'=>'multipart/form-data'] ]); ?> <div class="row"> <div class="col-md-6"> <?= $form->field($model, 'date')->widget(\yii\jui\DatePicker::classname(), [ 'dateFormat' => 'yyyy-MM-dd', ]) ?> </div> <div class="col-md-6"> <?php if($thumbUrl && $imageUrl): ?> <a href="<?= $imageUrl ?>" class="fancybox"> <?= Html::img($thumbUrl, ['class'=>'img-responsive']) ?> </a> <?= $form->field($model,'del_img')->checkBox() ?> <?php endif ?> <?= $form->field($model, 'image')->fileInput() ?> </div> </div> <div class="row"> <div class="col-md-12"> <ul class="nav nav-tabs" role="tablist"> <li role="presentation" class="active"><a href="#ru" aria-controls="home" role="tab" data-toggle="tab">Русский</a></li> <li role="presentation"><a href="#en" aria-controls="home" role="tab" data-toggle="tab">English</a></li> </ul> <div class="tab-content"> <div role="tabpanel" class="tab-pane active" id="ru"> <div class="row"> <div class="col-md-12"> <?= $form->field($model, 'name_ru')->textInput(['maxlength' => true]) ?> </div> </div> <div class="row"> <div class="col-md-12"> <?= $form->field($model, 'announce_ru')->widget(Widget::className(), [ 'settings' => [ 'lang' => 'ru', 'minHeight' => 100, 'buttons' => [ 'format', 'bold', 'italic', 'deleted', 'lists', 'link', ], 'buttonSource' => true, 'plugins' => [ 'clips', 'fullscreen', ] ] ]) ?> </div> </div> <div class="row"> <div class="col-md-12"> <?= $form->field($model, 'text_ru')->widget(Widget::className(), [ 'settings' => [ 'lang' => 'ru', 'minHeight' => 200, 'buttonSource' => true, 'plugins' => [ 'table', 'clips', 'fullscreen', 'imagemanager', 'filemanager' ], 'formatting' => ['p', 'blockquote', 'h2'], 'imageUpload' => Url::to(['/site/image-upload']), 'imageManagerJson' => Url::to(['/site/images-get']), 'fileManagerJson' => Url::to(['/site/files-get']), 'fileUpload' => Url::to(['/site/file-upload']) ] ]) ?> </div> </div> </div> <div role="tabpanel" class="tab-pane" id="en"> <div class="row"> <div class="col-md-12"> <?= $form->field($model, 'name_en')->textInput(['maxlength' => true]) ?> </div> </div> <div class="row"> <div class="col-md-12"> <?= $form->field($model, 'announce_en')->widget(Widget::className(), [ 'settings' => [ 'lang' => 'ru', 'minHeight' => 100, 'buttons' => [ 'format', 'bold', 'italic', 'deleted', 'lists', 'link', ], 'buttonSource' => true, 'plugins' => [ 'clips', 'fullscreen', ] ] ]) ?> </div> </div> <div class="row"> <div class="col-md-12"> <?= $form->field($model, 'text_en')->widget(Widget::className(), [ 'settings' => [ 'lang' => 'ru', 'minHeight' => 200, 'buttonSource' => true, 'plugins' => [ 'table', 'clips', 'fullscreen', 'imagemanager', 'filemanager' ], 'formatting' => ['p', 'blockquote', 'h2'], 'imageUpload' => Url::to(['/site/image-upload']), 'imageManagerJson' => Url::to(['/site/images-get']), 'fileManagerJson' => Url::to(['/site/files-get']), 'fileUpload' => Url::to(['/site/file-upload']) ] ]) ?> </div> </div> </div> </div> </div> </div> <div class="form-group"> <?= Html::submitButton($model->isNewRecord ? 'Create' : 'Update', ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?> </div> <?php ActiveForm::end(); ?></div>Всё не буду приводить. А нужно ещё несколько вьюх, а затем в базовый шаблон админки поставить ссылку на этот раздел. А для Django приведу весь код: from django.contrib import adminfrom modeltranslation.admin import TranslationAdminfrom .models import Articleclass ArticleAdmin(TranslationAdmin): list_display = ['name', 'date']admin.site.register(Article, ArticleAdmin)Это всё. То есть, конечно, можно делать мудрёные специфичные формы, если в этом есть необходимость, но даже самая мудрёная будет нагляднее и проще, в характерном декларативном стиле. В большинстве же случаев такой необходимости нет, и можно ограничиться крохотным классом. В админке будет список, формы добавления/редактирования, возможность удаления. Также легко в одну строчку можно наладить поиск или фильтр. И всё это будет красиво и удобно выглядеть. Во внешней части. Контроллер на YII2. <?php/** * Created by PhpStorm. * User: abrahadabra * Date: 22.12.15 * Time: 13:36 */namespace app\controllers;use Yii;use yii\data\Pagination;use yii\web\Controller;use app\models\News;use app\models\Page;use app\models\Language;use yii\web\NotFoundHttpException;class NewsController extends Controller{ public function actionIndex() { $query = News::find(); $countQuery = clone $query; $pages = new Pagination([ 'totalCount' => $countQuery->count() ]); $pages->setPageSize(10); $reviews = $query->offset($pages->offset)->limit($pages->limit)->all(); return $this->render('index', [ 'model' => new News(), 'news' => $reviews, 'page' => $this->getPage(), 'pages' => $pages ]); } public function actionView($id) { $model = News::findOne($id); if ($model === null) { throw new NotFoundHttpException; } $page = $this->getPage(); $path = $page->getPath(True); $path[] = $model->getName(); return $this->render('view', [ 'article' => $model, 'page' => $page, 'path' => $path ]); } protected function getPage() { $lang = Language::getCurrent(); return Page::findOne([ 'alias' => 'news', 'language_id' => $lang->id ]); }}View на Django (в архитектуре django view — это вроде аналога контроллера в классическом MVC) from django.views.generic import ListView, DetailViewfrom .models import Newsclass NewsListView(ListView): model = News template_name = 'news/list.html' context_object_name = 'news' paginate_by = 10class NewsDetailView(DetailView): model = News template_name = 'news/detail.html' context_object_name = 'article'Опять же: объём кода и его понятность. Ну и шаблоны. Шаблон списка на YII2: <?php/** * Created by PhpStorm. * User: abrahadabra * Date: 22.12.15 * Time: 13:40 */use yii\helpers\Url;use app\widgets\CustomPagerWidget;/** @var $news \yii\db\ActiveRecord */$this->title = $page->meta_title;$this->registerMetaTag(['name' => 'description', 'content' => $page->meta_description]);$this->registerMetaTag(['name' => 'keywords', 'content' => $page->meta_keywords]);$this->params['breadcrumbs'] = $page->getPath();?><div class="container"><div class="row"><div class="col-md-12"><div class="border-page-header"> <h1 class="pull-left"><?= Yii::t('app', 'Новости') ?></h1> <div class="pull-right"> <?= CustomPagerWidget::widget(['pagination' => $pages]) ?> </div></div></div></div><div class="row"><div class="col-md-12"><ul class="media-list news_list index_content_list"><?php foreach($news as $article): ?> <li class="media news_item index_content_list_item"> <div class="media-left"><?php$thumbUrl = $article->getThumbUrl();$imageUrl = $article->getImageUrl();?><?php if($thumbUrl && $imageUrl): ?> <a href="<?= $imageUrl ?>" class="fancybox"> <img src="<?= $thumbUrl ?>" class="media-object"> </a><?php endif ?> </div> <div class="media-body"> <?php $date = new DateTime($article->date); ?> <div class="news-date"><?= $date->format('j.m.Y') ?></div> <h2 class="media-heading"><?= $article->getName() ?></h2> <div class="news-text"><?= $article->getAnnounce() ?></div> <a href="<?= Url::toRoute(['news/view', 'id' => $article->id]) ?>" class="btn btn-default"><?= Yii::t('app', 'читать далее') ?></a> </div> </li><?php endforeach ?></ul></div><div class="col-md-12"> <hr class="p_media"> <div class="pull-right"> <?= CustomPagerWidget::widget(['pagination' => $pages]) ?> </div></div></div></div> Шаблон списка на Django: {% load i18n pagination thumbnail %}{% block content %} <div class="container"><div class="row"><div class="col-md-12"><div class="border-page-header"> <h1 class="pull-left">{% trans('News') %}</h1> <div class="pull-right"> {% pagination %} </div></div></div></div><div class="row"><div class="col-md-12"><ul class="media-list news_list index_content_list">{% for article in news %} <li class="media news_item index_content_list_item"> <div class="media-left"> {% thumbnail article.image "120x120" crop="center" as im %} <a href="{{ article.get_absolute_url }}"><img src="{{ im.url }}" class="media-object"></a> {% endthumbnail %} </div> <div class="media-body"> <div class="news-date">{{ article.date|date('j.M.Y') }}</div> <h2 class="media-heading">{{ article.name }}</h2> <div class="news-text">{{ article.announce|default:article.text|truncatewords_html:20 }}</div> <a href="{{ article.get_absolute_url }}" class="btn btn-default">{% trans "Read more" %}</a> </div> </li>{% endfor %}</ul></div><div class="col-md-12"> <hr class="p_media"> <div class="pull-right"> {% pagination %} </div></div></div></div>{% endblock %}Здесь всё более или менее похоже, но смотрим на наличие программной логики в шаблонах. Отличия в шаблонах детальных страниц примерно такого же плана, не буду повторяться. Вот такое сравнение языков и фреймворков на простейшей типовой задаче. Python Предлагаю согласиться.
  16. Я понимаю, о чём вы. Буквально недавно был случай. Проект сдан, запущен, заказчик пропал на несколько месяцев, затем заявляется: «мне надо сделать это и то». Как бы без проблем, давай доделаем. И тут выясняется очень интересный момент. За эти несколько месяцев после меня кто-то мой код правил. Вы правы. Не дай бог. Проект был сдан в виде git-репозитория, в котором исходники, и в отдельном каталоге собранное минифицированное-оптимизированное приложение. В исходниках всё как положено: миксины, функции, всё по полочкам разложено. Gulpfile с задачами на все случаи жизни. Шаблоны на jade, ангуляр весь на coffescript (проект начинался задолго до es6). Ну и конечно же, всё это скомпилировано в очень компактную, но нечитаемую сборку. После меня кто-то мой код правил. Ни одного коммита. Правилось всё на боевом сервере. Кто-то ниасилил js, подключил jquery. Кто-то ниасилил sass, правил прямо в минифицированном итоговом css. Кто-то ниасилил ангуляр… Привести это в порядок стало невозможно. Единственный вариант был — откатить к версии из репозитория, и всё писать заново. Естественно, у меня вопрос к заказчику: зачем? И удивительный ответ: у тебя слишком дорого, я нашёл, кто делает дешевле. Естественно, у меня следующий вопрос к заказчику: а почему эти доработки не тому дешёвому? И удивительный ответ: а он пропал и не отвечает. А другие некомпетентны, после их правок пропадало всё то, что сделал дешёвый пацанчик. Заказчик заплатил тому дешёвому, ещё двоим подороже, которые работали с версией из репозитория, убивая все правки дешёвого, а потом мне по слегка повышенному тарифу за то, чтобы всё за кем-то переделать и добавить новые правки. Славная была охота!
  17. Конечно же смотрите на конкретную ситуацию да выбирайте инструмент, наиболее подходящий. Вообще, относительные размеры шрифта хороши в теории, а на практике бывает, что абсолютные лучше. Например, для определённого разрешения базовый шрифт увеличивается, а один из элементов наоборот должен уменьшиться. Или меняются размеры непропорционально: один в два раза, другой в полтора. В таком случае, получается, надо явно указывать размер и того и другого. Преимущество относительных размеров слегка теряется. Не надо забывать, что мы живём в реальном мире, где веб-дизайнера, знающего хоть что-то о вебе, найти затруднительно, а принимать работу заказчик будет, ползая по экрану с линейкой. мсье знает толк
  18. Спасибо Это я так учусь. В вёрстке, мягко говоря, есть куда расти, а пообщаться с теми, кто ею профессионально занимается, так может и понимание лучше станет. SASS. он достаточно мощный; по нему масса материалов, учиться просто; на нём большинство фреймворков; Stylus всё же для эстетов. Его надо очень осмысленно применять, и выбор в пользу Stylus должен быть прочувствованным.
  19. https://github.com/postcss/postcss— стайка весьма облегчающих жизнь инструментов.
  20. Советую rem, но следует всё же понимать, что это разные вещи, и в разных случаях лучше либо одно, либо другое. Чем хорош rem: он всегда считается от одного и того же базового размера, когда em — от контекста. Здесь ясно видны отличия: https://jsfiddle.net/qL4du4sn/
  21. а вот, кстати, зря. Тут что можно сделать: Во-первых, устанавливать цены на свою работу: за современные браузеры базовая цена, за IE10 +10%, за IE8 +50%, и при этом обязательно показывать статистику, в которой доля IE8 менее, чем полпроцента. Во-вторых, progressive enhancement. Есть задачи, которые на флексе решаются в пару строк, а без него приходится городить огород из костылей. Например, прибить подвал к низу страницы. Или отцентрировать элемент по горизонтали и по вертикали. Писать в таких случаях на флексе без оглядки. Ну будет в IE8 подвал не прибит. Это как бы не очень хорошо, но и не критично. Ну будет элемент отцентрирован только по горизонтали тем же кодом. Тоже не должно мешать восприятию информации. Те полпроцента населения, которые по каким-то причинам используют не очень хороший и устаревший браузер, должны иметь возможность получить информацию, но они же и должны быть готовы к тому, что у них не будет в полной мере той красоты, как у тех, кто использует современные браузеры. И как итог, вопрос заказчику, готов ли он доплатить 50% за то, чтобы полпроцента посетителей получили необязательные детали? Если готов, то можно и постараться, вопросов нет А мои заказчики как правило соглашаются с тем, что лучше эти 50% доплатить за адаптивность, глядя в статистику, согласно которой один андроид превосходит все версии windows вместе взятые.
  22. нормальная поддержка. Но чтобы переключиться с раскладки на флексе на раскладку на флоатах, всего одну переменную поменять: $enable-flex: false !default; Кто-кто там ещё против sass'а, gulp'а и прочей нечисти?
  23. о да, бутстрап хорош. Хорошая вещь от посредственной отличается мелочами. Так и мне бутстрап особо полюбился после одного проекта на zurb foundation. Всякие функции-миксины по мелочи. Привыкаешь к ним, код становится красивым и легко читаемым, раскладка сама собой раскладывается, а как только их нет… Раскладка на флексе — красота. Хотя, чтобы всю мощь флекса задействовать, порой приходится дописывать вручную, а то в бутстрапе же наследие старой раскладки на float'ах. Да и варианты использования: можно по быстрому импортировать всё да писать прямо классы, а можно наимпортировать выборочно только что нужно, да оптимизировать строго под проект. В общем, одно удовольствие.
×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue. See more about our Guidelines and Privacy Policy