Django vs Laravel比較分析: どちらがより効率的にWebサイトを作れる?
文章目的
フルスタック型のWebフレームワークとして人気の
– PHPベースのLaravel
– PythonベースのDjango
どちらが良いのかを比較してみます。
その2つを比較対象に選んだのは、Webフレームワークとしての人気に加え、プログラミング言語として
– PHPはWordPressがあり、WPのシェアが世界のWebサイトにおいていつかは過半数のシェアに届きそうな位まだ伸び続けている為
– PythonはAIでの利用が伸びている為
それぞれ暫く安泰なプログラミング言語と言えるので、暫くはその言語のニーズがあるものとして学んで損がないWebフレームワークと言える為です。
尚、Ruby on Railsは、昔はユニークでしたが、Laravel等他言語の後発のWebフレームワークも同様な機能を実現出来るようになった為、その価値の低下&実際Rubyでなければいけないという理由が希薄な為、将来性としては怪しい感じになっているので、比較検討対象に入れていません。
発表の順番は
2005/07 Django
2005/12 Ruby on Rails
2011/06 Laravel
という歴史になっており、LaravelはRuby on Railsの影響を受けている為、Django以上にコマンドの類似性等の面で親和性が高いです。
その上で、Laravelは後々見てみると分かりますが、後発な分先進の良い所を取込んでるのと、更新がより頻繁な分、より今の時代の機能(UI方面等)を公式として取り込んでいる事が感じられます。
尚、こうしたWebフレームワークの比較記事では、各Webフレームワークの機能をリストして
「どのWeb Frameworkも同じ様な機能を提供していて良いものだから後は君の選択次第」
としてる記事が世の中の大半ですが、この文章では結論としてどっちが良いのかをあえて示す形にしています。
どちらの言語であっても問題無い方が迷った時に選択の判断指針の一助にして頂ければと。
先にDjangoとLaravelのどちらが良いか・選択基準はどうすると良いかの結論
結論から言えば、PHPのPythonに対して言語として劣る部分を考慮しても、Web FrameworkとしてはLaravelの方が機能としては大きく優れており、開発効率も上げてくれます。
Object Oriented Programming要素に欠ける部分も、Laravel自体が文字列(Str)や配列(Arr, Collection)の機能を大幅に拡張するHelper/Wrapperを提供しており、言語的差異によるデメリットは小さくなっている部分があります。
PHPもPythonも両方使えて当然、もしくは両方それぞれ別の人材として採用ができるのならば、Laravelで問題ないでしょう。
その上で、更に詳細に行くとすると、
– GISに凄く強いニーズがあるのならDjangoも候補
– Pythonに兎に角全て寄せたいならDjango
で
– WebはPHP(+JS)、オフラインはPython等と分けて運用できるのなら、純粋にWeb FWとしての機能を評価してLaravel
といった選択が、選択基準としては良いと思われます。
尚、自分はまずこうしたFWの使い勝手の比較を経て、Laravelを選んで
ハローワーク+
は作りました(毎日数万UUに利用される規模のサービス)。
オフライン側はベースはPythonで作ったので、Web App側もPythonに統一した方が技術セット的に良さげに見えますが、別の言語を使ってでも、Web App側はLaravelの方が開発効率良いなー、という結論になりました。
数で見るニーズ
Google Trendsの検索数変化状況(日本)を見ると、Laravelがリードしている事が伺えます。
GithubスターでもLaravelがWebフレームワークでは1位になりました。
但し、米国では、Python系フレームワークの方が検索数は多いです。
そうした第三者的な指標はありますが、以下では、自分で実際に使ってみて、純粋に使い勝手として評価を行います。
なお、言語の仕様自体の評価としては、Pythonの方がPHPより良いと認識しています。
各Webフレームワークの個別の記事
各Webフレームワークの検証時に使ったサンプルコード
機能の検証を行うにおいては、各WebフレームワークをDocker環境で開発を立ち上げるのに役立つテンプレート構築ついでに行っています。
利用してみたい方は、以下のGithubレポジトリをご活用下さい。
Django https://github.com/hikarine3/docker-django-postgresql
Laravel https://github.com/hikarine3/docker-laravel-boilerplate
機能比較
項目 | Django | Laravel | 勝者 | 理由 |
---|---|---|---|---|
総合的な使い勝手 | 普通 | 良い | Laravel | 言語がPythonなので、AIもWebもPytnonで…というのは魅力的だが、単純にWebフレームワークとして見ると、文章等情報の読み易さ・充実度合い・雛形自動生成機能の範囲・リリースのサイクル等で、後発の立場のLaravelの方が遥かに良い。 Webフレームワークに期待するのは、定型処理のスキップ・カプセル化だが、Django本体自身は残念ながらUI方面の貧弱・自分で実装する部分の残し具合等、先行したが故の古臭さから抜け切れていない。 Laravelはリリースのサイクルも定め、開発の進行が定期的・早い |
基盤言語 | Python | PHP | Django | 基盤の言語としてはPythonの方がPHPより簡素でオフラインと共通で使えるので使い易い |
日本での求人数 | 少ない | 多い | Laravel | Laravelが4倍程上 |
世界での求人数 | 多い | 少ない | Django | Djangoが3倍程上 |
Githubstarの数(2020/06) | 6万 | 5万 | Laravel | Laravelの方が数が多い |
Webサーバーの立ち上げ | 普通 | 簡単 | Laravel | PHPに比べPythonの方がインストールで難儀する点はまだ多い。 又LaravelはHomestead、Laradock等開発環境立ち上げ支援が充実している |
サーバー運用 | やや難 | やや簡単 | Laravel | LaravelはFPMというFast CGIによる定番のアプリケーションサーバーがあるが、djangoはgunicornというアプリケーションサーバが別途必要になったり、Djangoの方が複雑 |
大規模開発 | 簡単 | やや難 | Django | アプリケーションの分離レベルがDjangoの方が高い分、設計的により疎結合で開発し易い面はある |
モデルの定義 | やや難 | やや簡単 | Laravel | Djangoはmodle.pyに集約して、migrationsは別途自動生成出来るメリットがある=migrations.pyを見れば現在のモデルの状況が分かるのは利点。 但しフィールド名の名付け方とかいまいち&comment()機能等LaravelにあってもDjangoに無い機能もある。 加えてLaravelはmigration履歴をまとめる機能もversion8から出したので、Djangoの方が現状のテーブル状況把握し易いというメリットもなくなった |
DBへのデータ一括取込 | 普通 | やや難 | Django | DjangoはコマンドのみでのJSON/XMLだが取込み対応 Laravelは自分で書く必要ある。 とはいえ、実際そのまま取り込むのではなく、半角・全角の統一等、どうせ前処理も必要になるのは当然なので、そこまでデメリットでもないが |
ORMの使い易さ | 簡単 | 簡単 | Laravel | Laravelの方が覚え易い文法 |
View(Template)の文法の書き易さ | 簡単 | 簡単 | Laravel | コードを書く機能を全く許さないDjangoの方が設計的には良さげに見えなくもないが、実運用上は書けちゃう選択肢もあるLaravelの方が便利だったりもする。また、Laravelはversion8からはLivewireの組み込みも可能になり、React/Vue/Ajaxを気にせず、バックエンドの記述だけで、動的なページを作れるという選択肢も生まれ、ますます生産性が高まった |
サイト共通のBase Viewの作り易さ | 簡単 | 簡単 | Laravel | 正直どちらも変わらないが、デフォルトで出来るUI的にはLaravelの方が上 |
ページネーションの実装し易さ | やや簡単 | 簡単 | Laravel | Laravelの方がクールなUIのページネーションをより簡単に実現してくれる。またLaravelはv8の途中からはカーソルベースのページネーションが可能になって、どんなにページ数が増えてもサーバー負荷がページめくりで死なないという、運用上はとても嬉しい機能が加わった |
CSSの管理し易さ | やや難 | 簡単 | Laravel | LaravelはデフォルトでSASSコンパイルの仕組みが組み込まれてる。 Djangoは自分で3rd Partyライブラリ等を入れて機能拡張する必要がある |
Single Page Applicationの実装し易さ | 難 | 簡単 | Laravel | サポート機能が組み込まれてる |
認証機能の実装し易さ | やや難 | 簡単 | Laravel | DjangoではView=実質Controllerレベルで認証のサポートがあるが結局諸々実装しないと使えない。 Laravelはコマンド一つでView含めて使えるようになる。 ある意味Laravelの最大のメリット |
SNSログインの実装し易さ | 普通 | 簡単 | Laravel | Laravelのパッケージとして機能が提供されている |
フォーム機能の実装し易さ | 普通 | 普通 | Laravel | Inputのタイプが豊富 |
管理画面の実装し易さ | 簡単 | 普通 | Django | Laravelは3rd Partyの管理画面機能を追加しないといけない。 Djangoは無骨ではあるがデフォルトでモデルから管理画面を自動生成する機能が付いている |
メール機能の実装し易さ | 簡単 | 普通だが機能豊富 | Laravel | Laravelの方がただメールを送るよりもキューに入れておく等多様なパターンにシンプルに対応出来る構造になっている |
文章等情報充実度 | 限られる | 豊富 | Laravel | Laravelの方が相当充実&精錬されてる |
言語の将来性 | Python | PHP | Django | 暫くは勢い的にAI用途に支えられたPythonに軍配。 但しPHPも進化が早い上に、Webサイトの裏側の仕組みとしてのシェアを只管上げ続けているWordPressと言う存在がある為、Web系の言語として一定のポジションを占め続ける事に危惧はまだない |
開発の活発さ | 稀 | 活発 | Laravel | 例えば2021/09にDjangoはBugfixを一つリリースしてるだけですが、Laravelは5つのバージョンを出し、新機能・Bugix含め多数リリースしており、明らかに開発の活発さではLaravelの方が上回っています。逆に言えばDjangoは安定しているとは言えますが、機能的にLaravelに劣っている部分が更に差を広げられているとも言えます。 |
比較項目の詳細分析・記録
Webサーバの立ち上げ
Django
pip install django; PJ=djangopj; django-admin startproject $PJ; cd $PJ; python manage.py runserver;
Laravel
composer global require laravel/installer; PJ=examplepj; laravel new $PJ; cd $PJ; php artisan key:generate; php artisan serve;
Webサーバの運用の定番構成
Django
Gunycorn + nginx + Python + PostgreSQL(or MariaDB)
Laravel
nginx + PHP + MariaDB(or PostgreSQL)
モデルの定義
DjangoはMigrationでは一つのファイルを触り続けるが、LaravelはMigration毎に履歴的に変更用ファイルを作っていく事になる。
但し、Laravel8から、そうした変更履歴をまとめる機能も提供されている
Django
https://docs.djangoproject.com/en/3.0/topics/migrations/
from django.db import models from django.utils import timezone class Country(models.Model): name = models.CharField(max_length=255) key = models.CharField(max_length=2) created_at = models.DateTimeField(default=timezone.now) modified_at = models.DateTimeField(blank=True, null=True) class Meta: verbose_name_plural = "countries" def publish(self): self.modified_at = timezone.now() self.save() def __str(self): return self.name
Laravel
<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateCountriesTable extends Migration { public function up() { Schema::create('countries', function (Blueprint $table) { $table->string('id')->unique()->comment('iso2 country code'); $table->string('name'); $table->timestamps(); }); } public function down() { Schema::dropIfExists('countries'); } }
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Country extends Model { }
DBへのデータ一括取込
Django
https://docs.djangoproject.com/en/3.0/howto/initial-data/
CSVに対応せず、JSON/XMLのみ対応なので注意。
指定形式のJSONファイルを作ったら
manage.py loaddata JSONファイル;
と打つとデータを入れられる。
CSVを取込みたければ、3rd PartyのLibraryを使うか自分で実装する必要がある。
(所定書式のJSON/XMLの用意自体にプログラミングが必要な事も多いと思いますが)。
尚、bulk_createとかを使って、プログラミングを通じて、データ生成した方が楽な事もあるかとは思います。
Laravel
CSVから取り込む機能を実装した例
<?php use Illuminate\Database\Seeder; use Hikarine3\CsvParser; use Carbon\Carbon; class CountrySeeder extends Seeder { public function run() { ini_set('memory_limit','1024M'); $file = __DIR__ .'/data/locations/countries.tsv'; if( !file_exists($file) ) { die($file . " doesn't exist."); } $parser = new CsvParser(); $datas = $parser->parse(['delimiter' => "\t", 'file' => $file]); foreach ($datas as $data) { if(isset($data['id']) && isset($data['name']) ) { DB::table('countries')->insert([ 'id' => strtolower($data['id']), 'name' => $data['name'], 'created_at' => Carbon::now(), 'updated_at' => Carbon::now() ]); } } } }
ORMの使い易さ
Django
CountryのController(DjangoではView)の実装例
from django.shortcuts import render from .models import Country from .models import Prefecture from pprint import pprint from django.core.paginator import Paginator # Create your views here. def index(request): pgsz = 3 country_list = Country.objects.order_by('name') paginator = Paginator(country_list, pgsz) page_number = request.GET.get('page') page_obj = paginator.get_page(page_number) return render(request, 'geo/index.html', { 'page_obj': page_obj })
Laravel
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; class Country extends Controller { public function list() { $pgsz = 10; $countries = DB::table('countries')->orderBy('name', 'asc')->paginate($pgsz); return view('countries.list')->with(['countries' => $countries]); } }
View(Template)
Django
<h2>Country list</h2> {% extends "base.html" %} {% block title %} Country list {% endblock %} {% block content %} <h1>Country list</h1> {% if page_obj %} <ul> {% for country in page_obj %} <li><a href="/countries/{{ country.key }}/">{{ country.name }}</a></li> {% endfor %} </ul> {% else %} <p>No countries are available.</p> {% endif %}
Laravel
@extends('layouts.app') @section('title') Top page @endsection @section('content') <div> <h3>Countries</h3> Only <a href="/geo/us/">United States</a> has state list as data in default status. <ul class="jp_map"> @foreach ($countries as $country) <li><a href="/geo/{{ $country->id }}/">{{ $country->name }}</a></li> @endforeach </ul> {{ $countries->links() }} </div> @endsection
サイト共通のBase View
Django
ベースとなるtemplate
{% load static %} <html> <head> <title>{% block title %}{% endblock %}</title> </head> <body> <img src="{% static "images/sitelogo.png" %}" alt="Logo"> {% block content %}{% endblock %} </body> </html>
Laravel
<!doctype html> <html lang="{{ str_replace('_', '-', app()->getLocale()) }}"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- CSRF Token --> <meta name="csrf-token" content="{{ csrf_token() }}"> <title>{{ config('app.name', 'Laravel') }}</title> <!-- Scripts --> <script src="{{ asset('js/app.js') }}" defer></script> <!-- Fonts --> <link rel="dns-prefetch" href="//fonts.gstatic.com"> <link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet"> <!-- Styles --> <link href="{{ asset('css/app.css') }}" rel="stylesheet"> </head> <body> <div id="app"> <nav class="navbar navbar-expand-md navbar-light bg-white shadow-sm"> <div class="container"> <a class="navbar-brand" href="{{ url('/') }}"> {{ config('app.name', 'Laravel') }} </a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarSupportedContent"> <!-- Left Side Of Navbar --> <ul class="navbar-nav mr-auto"> </ul> <!-- Right Side Of Navbar --> <ul class="navbar-nav ml-auto"> <!-- Authentication Links --> @guest <li class="nav-item"> <a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a> </li> @if (Route::has('register')) <li class="nav-item"> <a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a> </li> @endif @else <li class="nav-item dropdown"> <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre> {{ Auth::user()->name }} <span class="caret"></span> </a> <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown"> <a class="dropdown-item" href="{{ route('logout') }}" onclick="event.preventDefault(); document.getElementById('logout-form').submit();"> {{ __('Logout') }} </a> <form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;"> @csrf </form> </div> </li> @endguest </ul> </div> </div> </nav> <main class="py-4"> @yield('content') </main> </div> </body> </html>
ページネーション
Django
https://docs.djangoproject.com/en/3.0/topics/pagination/
Controller
from django.core.paginator import Paginator from django.shortcuts import render from myapp.models import Contact def listing(request): contact_list = Contact.objects.all() paginator = Paginator(contact_list, 25) # Show 25 contacts per page. page_number = request.GET.get('page') page_obj = paginator.get_page(page_number) return render(request, 'list.html', {'page_obj': page_obj})
View
{% for contact in page_obj %} {# Each "contact" is a Contact model object. #} {{ contact.full_name|upper }}<br> ... {% endfor %} <div class="pagination"> <span class="step-links"> {% if page_obj.has_previous %} <a href="?page=1">« first</a> <a href="?page={{ page_obj.previous_page_number }}">previous</a> {% endif %} <span class="current"> Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}. </span> {% if page_obj.has_next %} <a href="?page={{ page_obj.next_page_number }}">next</a> <a href="?page={{ page_obj.paginator.num_pages }}">last »</a> {% endif %} </span> </div>
Laravel
Controller
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; class Country extends Controller { public function list() { $pgsz = 10; $countries = DB::table('countries')->orderBy('name', 'asc')->paginate($pgsz); return view('countries.list')->with(['countries' => $countries]); } }
View
@extends('layouts.app') @section('title') Top page @endsection @section('content') <div> <h3>Countries</h3> Only <a href="/geo/us/">United States</a> has state list as data in default status. <ul class="jp_map"> @foreach ($countries as $country) <li><a href="/geo/{{ $country->id }}/">{{ $country->name }}</a></li> @endforeach </ul> {{ $countries->links() }} </div> @endsection
認証機能の実装
Django
https://docs.djangoproject.com/ja/3.0/topics/auth/default/
View(Laravelで言うController)は用意されてるが、Templateは公式の例等からとってきて作る必要がある。
Laravel
UIと完全にセットで1コマンドで実装出来る機能が提供されている
SNSログインの実装
Django
social-app-djangoを使って実装する
Laravel
https://laravel.com/docs/7.x/socialite
Form機能の実装
Django
https://docs.djangoproject.com/en/3.0/topics/forms/
Laravel
https://laravel.com/docs/master/blade#forms
管理画面の実装
Django
設定を触ると簡単にテーブルに紐づく形で管理画面を作れる
Laravel
https://voyager.devdojo.com/
の様な3rd Party提供の自動生成管理画面はあるが、Laravel付属という形のではない
メール機能
Django
https://docs.djangoproject.com/en/3.0/topics/email/
Laravel
https://laravel.com/docs/7.x/mail
文献の充実具合
Django
https://docs.djangoproject.com/en/3.0/
Laravel
言語の将来性
Django
Python
今はAI系の処理に支えられて盛り上がっていて、教育への組み込みも考えても、将来有望。
但し、結局解析系では大量データ処理&速度&省資源化が重要な為、Rustの様なC系よりかは簡易だがコンパイル系で速度が速い言語に脅かされる可能性は十分有
Laravel
PHPは未だWebサイトでシェアが増え続けているWordPressが栄え続ける限り絶える事はないというチートさはある。
PHPは特に固い言語・一貫性を好まれる方から、言語としてはいまいちとされる部分があるが、使って作られてるソフト・FWの強さと、Webアプリとしては許容される緩さによる開発効率の利点的に、消滅は遠いと思われる。