https://wayhome25.github.io/django/2017/05/05/django-url-reverse/
django official : https://docs.djangoproject.com/en/2.2/ref/urlresolvers/



  • 장고는 urls.py 변경을 통해 ‘각 뷰에 대한’ url이 변경되는 유연한 시스템을 갖고 있다.
  • url이 변경 되더라도 url name명만 일치하면  url reverse가 변경된 url을 추적한다. (누락의 위험이 적다)

URL Reverse를 수행하는 4가지 함수

reverse()

  • 리턴형식 : string
from django.core.urlresolvers import reverse

reverse('blog:post_list') # '/blog/'
reverse('blog:post_detail', args=[10]) # '/blog/10/' args 인자로 리스트 지정 필요
reverse('blog:post_detail', kwargs={'id':10}) # '/blog/10/'
reverse('/hello/') # NoReverseMatch 오류 발생

resolve_url()

  • 리턴형식 : string
  • 내부적으로 reverse() 사용
  • reverse() 보다 사용이 간단하다.
from django.shortcuts import resolve_url

resolve_url('blog:post_list') # '/blog/'
resolve_url('blog:post_detail', 10) # '/blog/10/'
resolve_url('blog:post_detail', id=10) # '/blog/10/'
resolve_url('/hello/') # '/hello/' 문자열 그대로 리턴


본글 : https://naruport.com/blog/2019/7/25/django-custom-form/
같이보면 좋은 자료 : https://developer.mozilla.org/ko/docs/Learn/Server-side/Django/Forms



煉獄!Djangoのフォームを俺好みに改造する

65, 2019-07-25
Djangoロゴ

目次

はじめに

以下のようなフォームがある。

改造前のフォーム

# forms from django import forms class ProfileForm(forms.Form): name = forms.CharField() age = forms.IntegerField()

# templates <form action="{% url 'index' %}" method="POST"> {% csrf_token %} {{ profile_form }} <input type="submit" value="送信"> </form>

# view

from django.http import HttpResponseRedirect from django.shortcuts import render from .forms import NameForm def get_name(request): # if this is a POST request we need to process the form data if request.method == 'POST': # create a form instance and populate it with data from the request: form = NameForm(request.POST) # check whether it's valid: if form.is_valid(): # process the data in form.cleaned_data as required # ... # redirect to a new URL: return HttpResponseRedirect('/thanks/') # if a GET (or any other method) we'll create a blank form else: form = NameForm() return render(request, 'name.html', {'profile_form ': form})



これを改造していく。

Formの多様なフォーマット
 BooleanFieldCharFieldChoiceFieldTypedChoiceFieldDateFieldDateTimeFieldDecimalFieldDurationFieldEmailFieldFileFieldFilePathFieldFloatFieldImageFieldIntegerFieldGenericIPAddressFieldMultipleChoiceFieldTypedMultipleChoiceFieldNullBooleanFieldRegexFieldSlugFieldTimeFieldURLFieldUUIDFieldComboFieldMultiValueFieldSplitDateTimeFieldModelMultipleChoiceFieldModelChoiceField .
(
https://developer.mozilla.org/ko/docs/Learn/Server-side/Django/Forms)

フィールドを参照する

テンプレート内でフォームのフィールド名を参照すると対応するフィールドが取り出せる。

from django import forms


class ProfileForm(forms.Form):
    name = forms.CharField()
    age = forms.IntegerField()
{% with profile_form as f %}
<form action="{% url 'index' %}" method="POST">
    {% csrf_token %}
    <div>{{ f.name }}</div>
    <div>{{ f.age }}</div>
    <input type="submit" value="送信">
</form>
{% endwith %}

しかしインプット要素のみの取り出しなので味気がない出力になる。

ラベルを参照する

ラベル名を参照するにはフィールドのlabelを参照する。ちなみにlabel名の参照ではHTMLlabelは生成されないので、別途タグで囲む必要がある。

{% with profile_form as f %}
<form action="{% url 'index' %}" method="POST">
    {% csrf_token %}
    <div><label>{{ f.name.label }}</label>: {{ f.name }}</div>
    <div><label>{{ f.age.label }}</label>: {{ f.age }}</div>
    <input type="submit" value="送信">
</form>
{% endwith %}

ラベル名を変更するにはフォームのフィールドのlabelを指定する。

from django import forms


class ProfileForm(forms.Form):
    name = forms.CharField(label='名前')
    age = forms.IntegerField(label='年齢')


대부분의 필드에 공통적인 인자들은 아래와 같다. ( 이들은 적절한 기본값을 가지고 있다 ):

  • requiredTrue 로 설정되면, 필드를 빈칸으로 두거나 None 값을 줄 수 없게된다. 보통필드는 required는 True로 기본 설정되므로, 폼에서 빈 칸을 허용하기 위해서는required=False 로 설정해야 한다. 
  • label: HTML에서 필드를 렌더링할때 사용하는 레이블이다. label 이 지정되지 않으면,  Django는 필드 이름에서 첫번째 문자를 대문자로, 밑줄을 공백으로 변형한 레이블을 새로 생성할 것이다. (예를 들면, renewal_date  --> Renewal date).
  • label_suffix: 기본적으로, 콜론(:)이 레이블 다음에 표시된다. (예를 들면, Renewal date:). 이 인자는 다른 문자(들)를 포함한 접미사를 지정할 수 있도록 해준다.
  • initial: 폼이 나타날 때 해당 필드의 초기 값.
  • widget: 사용할 디스플레이 위젯.
  • help_text (위의 예에서 봤듯이): 필드 사용법을 보여주는 추가적인 문구.
  • error_messages: 해당 필드의 에러 메시지 목록. 필요하면 문구를 수정할 수 있다.
  • validators: 해당 필드가 유효한 값을 가질 때 호출되는 함수의 목록.
  • localize: 폼 데이타 입력의 현지화(localisation)를 허용함 (자세한 정보는 해당 링크 참조).
  • disabled: 이 옵션이 True 일때 해당 필드를 볼 수는 있지만 편집이 안됨. 기본 값은 False.




出力結果。

ラベルを付けたフォーム

ヘルプの参照

フォームのフィールドにhelp_textを設定した場合、テンプレートではフィールドのhelp_textから設定文字列を参照できる。

from django import forms


class ProfileForm(forms.Form):
    name = forms.CharField(label='名前', help_text='英字のみ')
    age = forms.IntegerField(label='年齢', help_text='0以上120以下')
{% with profile_form as f %}
<form action="{% url 'index' %}" method="POST">
    {% csrf_token %}
    <div>
        <label>{{ f.name.label }}</label>:
        {{ f.name }}
        <p>{{ f.name.help_text }}</p>
    </div>
    <div>
        <label>{{ f.age.label }}</label>:
        {{ f.age }}
        <p>{{ f.age.help_text }}</p>
    </div>
    <input type="submit" value="送信">
</form>
{% endwith %}

出力結果。

ヘルプを付けたフォーム

エラーの参照

フォームは検証に失敗するとエラー文字列をフォームに格納する。

{% with profile_form as f %}
<form action="{% url 'index' %}" method="POST">
    {% csrf_token %}

    {# フィールドに属さない全体的なエラーの参照 #}
    {% if f.errors %}
        <p>{{ f.non_field_errors }}</p>
    {% endif %}

    <div>
        <label>{{ f.name.label }}</label>:
        {{ f.name }}
        <p>{{ f.name.help_text }}</p>
        {# フィールドに属しているエラーの参照 #}
        <p>{{ f.name.errors }}</p>
    </div>
    <div>
        <label>{{ f.age.label }}</label>:
        {{ f.age }}
        <p>{{ f.age.help_text }}</p>
        {# フィールドに属しているエラーの参照 #}
        <p>{{ f.age.errors }}</p>
    </div>
    <input type="submit" value="送信">
</form>
{% endwith %}

バリデーションの追加

バリデーションを追加するにはフォームにclean_で始まるメソッドを定義する(参照: http://python.keicode.com/django/form-validation.php)。
デフォルトのバリデーターが呼ばれた後に実行されるらしい。

from django import forms class ProfileForm(forms.Form): name = forms.CharField(max_length=20, label='名前', help_text='英字のみ') age = forms.IntegerField(label='年齢', help_text='0以上120以下') def clean_name(self): # viewのform.is_validが実行されるとdef clean_xxxx系の関数から有効性検査を行う name = self.cleaned_data['name'] isallalpha = True for c in name: if not c.isalpha(): isallalpha = False break if not isallalpha: raise forms.ValidationError('英字以外が含まれています。') return name def clean_age(self): """ ちなみにフィールドの属性 min_value, max_valueで入力範囲を制限できる。 それらを設定したらこのバリデーションは不要? """ age = self.cleaned_data['age'] if age < 0 or age > 120: raise forms.ValidationError('範囲外です。') return age

フォームに不正な値を入力してみた出力結果。

エラー表示を付けたフォーム

ちなみにテンプレートでerrorsを参照したい場合、views.pyでちょっとした工夫が必要になる。is_validを呼んだ後のフォームを再度出力用のテンプレートに渡すとerrorsを参照できるようになる。

from django.shortcuts import render
from django.http import HttpResponse
from . import forms


def index(req):
    if req.method == 'GET':
        return render(req, 'myapp/profile-form.html', {
            'profile_form': forms.ProfileForm(),
        })
    elif req.method == 'POST':
        profile_form = forms.ProfileForm(req.POST)
        if not profile_form.is_valid():
            # ここでerrorsを生かしたままテンプレートに渡す。
            return render(req, 'myapp/profile-form.html', {
                'profile_form': profile_form,
            })

        return HttpResponse('ok')

プレースホルダーの設定

インプット要素のプレースホルダーはフォームのwidgetで設定できる。widgetの種類は https://github.com/django/django/blob/master/django/forms/widgets.py を参照。

class ProfileForm(forms.Form):
    name = forms.CharField(max_length=20, label='名前', help_text='英字のみ', widget=forms.TextInput(attrs={
        'placeholder': 'Name',
    }))
    age = forms.IntegerField(min_value=0, max_value=120, label='年齢', help_text='0以上120以下', widget=forms.NumberInput(attrs={
        'placeholder': '0',
    }))

出力結果。

プレースフォルダーを付けたフォーム

だいぶフォームらしくなりました(・∀・)ノ


해당 어플리케이션 직하에/ templatetags 디렉토리 생성

예를 들어 example어플리케이션이 타겟이라면, application 디렉토리 아래에 templatetags디렉토리를 생성

example/
    __init__.py
    models.py
    templatetags/
        __init__.py
    views.py

template filter 스크립트 작성

위에서 생성한 templatetags 아래에 스크립트를 생성 후 저장
파일명은 무엇이든 상관 없으나, 이후 template에서 load할 때,파일명으로 불러오게 되므로 주의

(app_root)/templatetags/dict_filter.py

from django.template.defaulttags import register
...
@register.filter
def get_dict(dictionary, key):
    return dictionary.get(key)

customizing tag를 호출하기

커스터마이징 태그를 호출하기 위해서는 templatetags 아래있는 작성한 스크립트 명을 호출해야한다 : {% load dict_filter %}
이를 사용할 때는 함수명을 호출하여 사용한다 : {{ mydict|get_dict:item.NAME }}

test.html

{% load dict_filter %}
...
{{ mydict|get_dict:item.NAME }}
...

출처


https://ssungkang.tistory.com/entry/Django-render-%EC%99%80-redirect-%EC%9D%98-%EC%B0%A8%EC%9D%B4


[Django] render 와 redirect 의 차이(Django shortcut functions)

사용자 ssung.k 2019.05.21 17:49

HttpResponse

HttpResponse(data, content_type) 

 HttpResponse 는 HttpRequest와 짝을 이루며, response를 반환하는 가장 기본적인 함수입니다. express로 치자면 send 같은 놈이 되겠네요.  Json이나 html을 반환할 수도 있습니다.


HttpResponseRedirect

HttpResponseRedirect(url)

별다른 Response를 반환하는게 아니라, 지정된 url페이지로 redirect할시 사용됩니다.



render

render(request, template_name, context=None, content_type=None, status=None, using=None)

render 는 httpResponse객체를 반환하는 함수로, template을 context과 엮어 httpResponse로 쉽게 반환하게 해주는 함수입니다.

 다음과 같은 파라미터들을 가집니다. 이 중에서 request 와 template_name 은 필수적으로 필요합니다. request 는 위와 동일하게 써주게 되고, template_name 은 불러오고 싶은 템플릿을 기재해줍니다. 쉽게 생각해서 화면에 html 파일을 띄운다고 생각하면 됩니다. 이 때 context 로 원하는 인자를, 즉 view 에서 사용하던 파이썬 변수를 html 템플릿으로 넘길 수 있습니다. context 는 딕셔너리형으로 사용하며 key 값이 탬플릿에서 사용할 변수이름, value 값이 파이썬 변수가 됩니다.

# views.py

from django.shortcuts import render

def my_view(request):
    name = "minsung"
    return render(request, 'myapp/index.html', {
        'name': name,
    }

redirect

redirect(to, permanent=False, *args, **kwargs)

redirect 는 다음과 같은 파라미터를 가집니다. to 에는 어느 URL 로 이동할지를 정하게 됩니다. 이 때 상대 URL, 절대 URL 모두 가능하며 urls.py 에 name 을 정의하고 이를 많이 사용합니다. 단지 URL로 이동하는 것이기 때문에 render 처럼 context 값을 넘기지는 못합니다.

# views.py

from django.shortcuts import redirect

def my_view(request):
    ...
    return redirect('view-name')             # view_name 사용
    # return redirect('/some/url/')                  # 상대 경로 
      # return redirect('https://example.com/')# 절대 경로 

render 와 redirect 구분

두 함수를 헷갈려 혼동하는 경우가 많습니다. 특히 장고가 익숙하지 않을 때는 둘다 return 뒤에 위치하여 함수를 종료할 시 사용되니 그럴만 합니다. 생각 외로 둘의 차이는 명확합니다. render 는 템플릿을 불러오고, redirect 는 URL로 이동합니다. URL 로 이동한다는 건 그 URL 에 맞는 views 가 다시 실행될테고 여기서 render 를 할지 다시 redirect 할지 결정할 것 입니다. 이 점에 유의해서 사용하신다면 상황에 맞게 사용하실 수 있을 겁니다.


https://qiita.com/t-iguchi/items/54424f619829a6d135fc


Python + Djangoで独自のユーザ認証を実装する

この記事は最終更新日から1年以上が経過しています。

Djangoの認証機能を利用して簡単に認証機能を実装してみます

環境:
OS: Windows10 Home 64bit
Python 3.6.5
Django 2.0.6

Python + Djangoのプロジェクト作成でプロジェクトを作成します

1.migrate

python manage.py migrateを実行して、認証系のテーブルを作成します。

cmd.prompt
(venv) C:\data\python\work\myproject>python manage.py migrate

2.管理ユーザを作成する

管理ユーザを作成します。このユーザで認証を行うことになります。

cmd.prompt
python manage.py createsuperuser
Username (leave blank to use 'papa'): django
Email address: django@localhost
Password:
Password (again):
Superuser created successfully.

3.アプリケーションの構成

完成後のアプリケーションの構成は以下のとおりです
JQuery, Bootstrap4を使いますので、ダウンロードして配置しておきます

C:\data\python\work\myproject
├─accounts・・・・(1)
│  │  admin.py
│  │  apps.py
│  │  models.py
│  │  tests.py
│  │  urls.py・・・・(2)
│  │  views.py・・・・(3)
│  │  __init__.py
│  │  
│  ├─migrations
・・・
│  │          
│  └─__pycache__
・・・
├─myproject
│  │  settings.py・・・(8)
│  │  urls.py・・・・・(9)
│  │  wsgi.py
│  │  __init__.py
│  │  
│  └─__pycache__
・・・
├─static
│  ├─css
│  │      bootstrap.min.css
│  │      bootstrap.min.css.map
│  │      style.css・・・・(4)
│  └─js
│         bootstrap.bundle.min.js
│         jquery-3.3.1.min.js
└─templates
    ├─accounts
    │      top.html・・・・(5)
    ├─commons
    │      base.html・・・・(6)
    └─registration
           login.html・・・・(7)

(1)accounts

Djangoの認証機能を作成するには、accountsアプリケーションを作成します

cmd.prompt
(venv) C:\data\python\work\myproject>python manage.py startapp accounts

(2)accounts\urls.py

accounts\urls.pyを作成します。

accounts\urls.py
from django.urls import path
from . import views

app_name = 'accounts'
urlpatterns = [
    path('', views.index, name='index'),
]

(3)accounts\views.py

@login_required をつけると、このviewにアクセスするときに認証が必要になります

accounts\views.py
from django.shortcuts import render
from django.contrib.auth.decorators import login_required

@login_required
def index(request):
    return render(request, 'accounts/top.html')

(4)static\css\style.css

画面全体のスタイルを定義します。

static\css\style.css
@charset "UTF-8";

.mysystem-header{
  background: #3333FF !important;
  color: white !important;
}
.mysystem-border{
  background: #3333FF !important;
  color: white !important;
}

(5)accounts\top.html

認証後に表示するtop画面を作成します

accounts\top.html
{% extends "commons/base.html" %}

{% block title %}メニュー{% endblock %}

{% block styles %}
nav.menu ul li {
    list-style-type:none;
    font-size: 1.1em;
}
{% endblock %}

{% block headertitle %}メニュー画面{% endblock %}

{% block content %}
<nav class="menu">
  <ul>
      <li>
        <a target="_blank" href="/hello">Hello World!</a>
      </li>
  </ul>
</nav>
{% endblock %}

{% block scripts %}
<script>
</script>
{% endblock %}

(6)commons\base.html

共通テンプレートです

commons\base.html
{% load staticfiles %}
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" type="text/css" href="{% static 'css/bootstrap.min.css' %}">
    <link rel="stylesheet" type="text/css" href="{% static 'css/style.css' %}">
    <title>
      {% block title %}{% endblock %}
    </title>
    <style>
        body {
          padding : 10px;
        }
        div.container {
          min-width:600px;
        }
        nav ul#menu {
            margin:0px;
            padding:0px;
        }
        nav ul#menu li {
            float:right;
            list-style-type:none;
            text-decoration: underline;
            padding-left: 10px;
        }
        div.header {
          padding: 10px;
        }
        div.header * {
          color: white !important;
        }
        div.header .title {
          font-size: 1.2em;
        }
        {% block styles %}{% endblock %}
    </style>
</head>
<body>
<div class="container">
  <div class="inner">
    <div class="row header mysystem-header">
      <div class="col-sm title">
        {% block headertitle %}{% endblock %}
      </div>
      <div class="col-sm">
        <nav class="pull-right">
            <ul id="menu">
              {% if user.is_authenticated %}
                <li><a href="{% url 'logout' %}" class="logout">Logout</a></li>
              {% else %}
              {% endif %}
            </ul>
        </nav>
      </div>
    </div>
    <br/>
    <div class="content">
        {% block content %}{% endblock %}
    </div>
  </div>
</div>
<script src="{% static "js/jquery-3.3.1.min.js" %}"></script>
<script src="{% static "js/bootstrap.bundle.min.js" %}"></script>
{% block scripts %}{% endblock %}
</body>
</html>

(7)registration\login.html

ログイン画面です
:point_up_2:templateファイルは、Djangoの「registration」フォルダに入れておく必要があります

registration\login.html
{% load staticfiles %}
{% load bootstrap4 %}
<!DOCTYPE html>
<html lang="ja-JP" >
<head>
    <title>ログイン</title>
    <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1.0, maximum-scale=1.0">
    <link rel="stylesheet" type="text/css" href="/static/admin/css/responsive.css" />
    <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
    <link rel="stylesheet" href="{% static 'css/style.css' %}">
    <style>
    .login #container {
        width: 30em !important;
        min-width: 300px;
        margin: 100px auto;
        border-style: solid;
        border-color: #3333FF;
    }
    div.form-group {
      padding-top: 20px;
    }
    div#header {
      padding: 10px;
    }
    div#content-main {
      padding: 20px;
    }
    </style>
</head>

<body class=" login" data-admin-utc-offset="32400">
<div id="container">
  <div class="inner">
    <div id="header" class="mysystem-header">
              ○○管理システム
    </div>
    <div id="content" class="colM">
      <div id="content-main">
        <form method="post" action="{% url 'login' %}">
        {% csrf_token %}
        {% bootstrap_form form layout='horizontal' %}
        <div class="form-group row px-3">
          <input type="submit" class="btn btn-primary btn-block" value="ログイン" />
        </div>
        <input type="hidden" name="next" value="{{ next }}" />
        </form>
      </div>  <!-- content-main -->
      <br class="clear" />
    </div>  <!-- content -->
  </div>  <!-- inner -->
</div>  <!-- END Container -->
</body>
</html>

:point_up_2:bootstrap_form でformを描画しています。
{% bootstrap_form form layout='horizontal' %}

(8)settings.py

settings.pyを編集します

settings.py

INSTALLED_APPS = [ 'accounts.apps.AccountsConfig', #追加 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'bootstrap4', #追加 ] ・・・・ # Authentication LOGIN_URL = '/' #ここで定義したURLに入ると、template/registration/login.htmlに移動 LOGIN_REDIRECT_URL = '/dashboard/' #任意の行に追加 LOGOUT_REDIRECT_URL = '/accounts/login/' #任意の行に追加 LOGOUT_REDIRECT_URL='/' #任意の行に追加

bootstrap4は、pipでインストールできます
ログアウトでは、ルートを表示するようにしておきます。

(9)myproject\urls.py

・accountsにアクセスすると、Djagoの認証用のサイトにアクセスします
・ルートにアクセスすると、「accounts」アプリのサイトにアクセスします

myproject\urls.py
・・・
urlpatterns = [
    path('accounts/', include('django.contrib.auth.urls')), #  追加
    path('', include('accounts.urls')), #"追加"
    path('admin/', admin.site.urls),
]

ルートのURLをaccountsにすることで、topページが表示されるようにしておきます。

動作確認してみます。

cmd.prompt
(venv) C:\data\python\work\myproject>manage.py runserver

http://localhost:8000/にアクセスします

image.png

「2.管理ユーザを作成する」で作成したスーパーユーザでログインしてみます

image.png

ログインできました。Djangoは、認証の実装も簡単ですね。


https://narito.ninja/blog/detail/86/



概要

Djangoフレームワークは多言語に対応するための機能も提供しており、割と簡単に扱うことができます。基本的な使い方と流れを説明していきます。


結論から言ってDjangoのi18nがカスタマイズi18nよりいいこと

1.Djangoに最適化されているので実装が速い
2.★Session管理を勝ってにしてくれる。ページが変わってもクライアントで選択した言語を維持してくれる

多言語対応の準備

多言語対応する上での準備です。

settings.py

まず、LANGUAGE_CODEjaとしておきます。これはおなじみの設定ですね。

LANGUAGE_CODE = 'ja'

特に多言語対応をしない場合ならば、このLANGUAGE_CODEに指定した言語で翻訳されていきます。しかし多言語対応、サイト上で複数の言語を扱う場合は追加の設定が必要です。
→LANGUAGE_CODEはDefault言語になる

MIDDLEWAREに、django.middleware.locale.LocaleMiddlewareを読み込ませる必要があります。

    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.locale.LocaleMiddleware',  # これ
    'django.middleware.common.CommonMiddleware',

これにより、Djangoはこのサイト上で複数の言語を扱うのだなと判断します。アクセスしてきたURLやセッション、使っているブラウザの言語といった情報をもとに、サイト上の文章を翻訳しようとします。

どうしてもユーザーの言語がわからない場合にだけ、LANGUAGE_CODEが使われるようになります。言い換えると、Localeミドルウェアを使わない場合は必ずLANGUAGE_CODEの言語で翻訳されるということになります。

ミドルウェアの場所にも注意です。SessionMidlewareCommonMiddlewareの間に書きます。キャッシュ関連ミドルウェアを使う場合は、CommonMiddlewareの前にLocaleMiddlewareが来るようにしてください(つまりキャッシュの後に読み込ませる)。

また、次の記述も追加しておきましょう。

LOCALE_PATHS = (
    os.path.join(BASE_DIR, 'locale'),
)

localeディレクトリの作成

プロジェクト直下と、アプリケーションディレクトリ内にlocaleというディレクトリを作成しておきましょう。

今回はappというアプリケーションです。次の画像のようになります。 2つのlocaleディレクトリがあることを確認しましょう。プロジェクト直下とアプリケーション直下にlocaleディレクトリを置く

Windowsの場合

翻訳ファイルを作成するためのツールが必要です。こちらにアクセスし、ダウンロードしてください。

多言語化したい文章を指定する

それでは、多言語化したい文章をマークしていきましょう。

テンプレート内の文章

テンプレート内で文章を多言語化する簡単な方法は、テンプレートタグtransを使う方法です。

{% load i18n %}
<h1>{% trans "Hello" %}</h1>

{% load i18n %}とするのを忘れないようにしましょう。{% trans %}をはじめとする、翻訳関連のテンプレートタグ・フィルタを読み込むための記述です。これがどういう動作をするかは、後々わかるようになります。

Pythonファイル内の文章

フォームのlabelやモデルのverbose_nameなど、そういった部分も多言語化できますし、テンプレートへ渡す文字列を多言語化したいかもしれません。

例えば、モデルの例です。

from django.db import models
from django.utils.translation import gettext_lazy as _


class Post(models.Model):
    title = models.CharField(_('post title'), max_length=255)

テンプレートではtransを使いましたが、Pythonファイル内ではdjango.utils.translation.gettext_lazy又はgettextをよく使います。lazyとついているのは遅延評価用...reverse関数とreverse_lazy関数の関係と似たようなものです。上の例ならば、titleフィールドの表示名を実際に表示する場面で翻訳をしてくれます。

モデルやフォームのフィールド内にあるlabel, verbose_name,help_text...モジュールのimport時にすぐに読み込まれるような場所、クラス属性内に関してはlazyを使うほうが無難です。

as _ として、gettext_lazy関数をアンダースコアで使えるようにしていますがこれは慣習的なものです。

翻訳ファイルの作成

以下のコマンドを実行しましょう。これは日本語の翻訳ファイルを作成するコマンドです。

django-admin makemessages -l ja

localeディレクトリの中に、jaというディレクトリができたはずです。おそらく、2通りの状態になります。

  1. アプリケーション直下のlocaleディレクトリだけ中身がある

  2. アプリケーション直下と、プロジェクト直下のlocaleディレクトリ両方に中身がある

Djangoが翻訳ファイルをどこに配置するかというと、まずアプリケーション内のlocaleディレクトリに配置しようとしますapp/models.pyに翻訳テキストがありましたが、これはappアプリケーション内のlocaleディレクトリに翻訳ファイルが置かれます。

なので、appアプリケーション内のlocaleディレクトリ内、django.poには次の記述が必ずあるはずです。

#: .\app\models.py:6
msgid "post title"
msgstr ""

特定のアプリケーションに属さないファイルはsettings.pyに定義したLOCALE_PATHSに翻訳ファイルが置かれます。特定のアプリケーションに属さないファイルというのは例えば、settings.pyやプロジェクト直下に置いたtemplates内の翻訳テキストが該当します。

なので、プロジェクト直下にtemplatesを置いている方であれば、プロジェクト直下のlocale内のdjango.poに次の記述があるはずです。

#: .\templates\app\top.html:7
msgid "Hello"
msgstr ""

templatesをアプリケーション内に置いている方ならば、プロジェクト直下のlocaleは空で、アプリケーション内のlocaleにそれがあります。

#: .\app\templates\app\top.html:7
msgid "Hello"
msgstr ""

アプリケーション内にlocaleディレクトリがない場合は、LOCALE_PATHSに翻訳ファイルが置かれます。なので、プロジェクト全ての翻訳を一か所にまとめたい場合は、アプリケーション内にlocaleディレクトリを置かないほうが良いかもしれません。また、LOCALE_PATHSも空の場合はエラーになります。

翻訳する

django.poというファイルがあるので開きましょう。

#: .\app\models.py:6
msgid "post title"
msgstr ""

#: .\app\templates\app\top.html:5
msgid "Hello"
msgstr ""

msgidは、{% trans Hello %}のHello部分であったり、_('post title')のpost title部分です。ここに入れたい日本語の文章をmsgstrに書いていきます。

#: .\app\models.py:6
msgid "post title"
msgstr "記事タイトル"

#: .\app\templates\app\top.html:5
msgid "Hello"
msgstr "こんにちは"

翻訳する文章を作成したら、コンパイルします。

django-admin compilemessages

これにより、django.moというファイルができます。これでひとまず終了です。

今後は、新しい言語を追加したり翻訳したい文章が増えればmakemessagesを行い、修正があればdjango.poを編集し、そしてcompilemessagesを行っていきます。

UnicodeEncodeErrorが出る場合

ロケールの問題が殆どです。CentOS7ならば、以下のようにロケールを変更できます。

localectl set-locale LANG=ja_JP.utf-8
source /etc/locale.conf

翻訳の上書き

翻訳ファイルの探索順序は、次のようになっています。

  1. settings.pyのLOCALE_PATHSが指している場所

    この記事で言うところの、プロジェクト直下のlocale

  2. 各アプリケーション内にあるlocaleディレクトリ(INSTALLED_APPSの上から順に)

    authやadminなどの組み込みのDjangoアプリケーション内にもlocaleがあります。

  3. django/conf/locale

    Django全体で使われる基本翻訳。例えば曜日の翻訳文章などはここで。

もしかしたら、Django標準の翻訳テキストを上書きしたい場合もあるかもしれませんし、サードパーティ製ライブラリの翻訳を上書きしたい、といったケースもあるかもしれません。

Django管理サイトのタイトルは"Django administration"なのですが、これを上書きするならばLOCALE_PATHS内で次のように定義を上書きすればOKです。

msgid "Django administration"
msgstr "管理サイトへようこそ!"

もしくは、アプリケーション内のlocaleで上書きすることも可能です。各アプリケーション内のlocaleディレクトリですが、INSTALLED_APPSの上から順に探索され、見つかったら探索が終わります。各種テンプレートやstaticファイルの探索順序と同じです。INSTALLED_APPSで自作アプリが上書きしたいアプリケーション(管理サイトタイトルならば、admin)より上にある状態にしておくことが必要です。

言語切り替えプルダウン

各ユーザーが、表示したい言語をそれぞれ切り替えれるようにしてみます。

urls.pyに以下を追加し...

path('i18n/', include('django.conf.urls.i18n')),

テンプレート内に以下の記述をしておきます。

{% load i18n %}

<form action="{% url 'set_language' %}" method="post">{% csrf_token %}
    <input name="next" type="hidden" value="{{ redirect_to }}">
    <select name="language">
        {% get_current_language as LANGUAGE_CODE %}
        {% get_available_languages as LANGUAGES %}
        {% get_language_info_list for LANGUAGES as languages %}
        {% for language in languages %}
            <option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected{% endif %}>
                {{ language.name_local }} ({{ language.code }})
            </option>
        {% endfor %}
    </select>
    <input type="submit" value="Go">
</form>

ヘッダーとかに置いておくと良さそうですね。
言語切り替えプルダウン

プルダウンの言語が多すぎる場合は、絞ることもできます。settings.pyに追記しましょう。

from django.utils.translation import ugettext_lazy as _
LANGUAGES = [
    ('en', _('English')),
    ('ja', _('Japanese')),
]

URLで言語を判断する

プロジェクトのurls.pyにて、以下のように定義します。

from django.conf.urls.i18n import i18n_patterns
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),

]

urlpatterns += i18n_patterns(
    path('', include('app.urls')),
)

いつもならば、path('', include('app.urls')),という記述はpath('admin/', admin.site.urls),の下あたりにありますね。しかし、今回は別の場所...i18n_patterns内に移しました。これにより、appの各URLに関しては、URLの頭に/ja/hogehoge とか/en/hogehoge とかでアクセスできるようになります。

https://intellectual-curiosity.tokyo/2018/12/18/django%E3%81%A7%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E3%82%A2%E3%83%83%E3%83%97%E3%83%AD%E3%83%BC%E3%83%89%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95/


Djangoでファイルをアップロードする方法

Djangoでファイルをアップロードする方法をご紹介します。

条件

  • Django 2.1.4
  • Python 3.7.0

アップロード処理の実装

「アップロード画面」と「アップロード完了画面」を作成します。

  • アップロード画面:ファイルを選択してアップロード実行
  • アップロード完了画面:アップロードが完了したら表示

アップロード先ディレクトリ作成

アップロードしたファイルの保存先として以下のディレクトリ(uploads)を作成します。

  1. projectName
  2. ├projectName
  3. └monitor
  4. ├migrations
  5. ├templates
  6. └uploads ←ここにアップロードしたファイルが保存される
  7. ・・
  8. ・・

urls.py

  1. # urls.py抜粋
  2. from django.urls import path
  3. from . import views
  4. app_name = 'monitor'
  5. urlpatterns = [
  6. ・・・
  7. # ファイルアップロード用
  8. path('monitor/upload/', views.upload, name='upload'),
  9. path('monitor/upload_complete/', views.upload_complete, name='upload_complete'),
  10. ]

views.py

  1. # views.py抜粋
  2. import os
  3. from .forms import UploadFileForm
  4. UPLOAD_DIR = os.path.dirname(os.path.abspath(__file__)) + '/uploads/' # アップロードしたファイルを保存するディレクトリ
  5. # アップロードされたファイルのハンドル
  6. def handle_uploaded_file(f):
  7. path = os.path.join(UPLOAD_DIR, f.name)
  8. with open(path, 'wb+') as destination:
  9. for chunk in f.chunks():
  10. destination.write(chunk)
  11. # ファイルアップロード
  12. def upload(request):
  13. if request.method == 'POST':
  14. form = UploadFileForm(request.POST, request.FILES)
  15. if form.is_valid():
  16. handle_uploaded_file(request.FILES['file'])
  17. return redirect('monitor:upload_complete') # アップロード完了画面にリダイレクト
  18. else:
  19. form = UploadFileForm()
  20. return render(request, 'monitor/upload.html', {'form': form})
  21. # ファイルアップロード完了
  22. def upload_complete(request):
  23. return render(request, 'monitor/upload_complete.html')

forms.py

  1. # forms.py
  2. from django import forms
  3. class UploadFileForm(forms.Form):
  4. file = forms.FileField()

テンプレート

アップロード画面:upload.html、アップロード完了画面:upload_complete.htmlを作成します。
(monitor/templates/monitorの下に作成)

  1. <!-- upload.html -->
  2. {% extends 'base.html' %}
  3. {% block content %}
  4. <h1>アップロード サンプル</h1>
  5. <form method="POST" enctype="multipart/form-data">
  6. {% csrf_token %}
  7. {{ form }}
  8. <div class="form-group">
  9. <button type="submit">アップロード</button>
  10. </div>
  11. </form>
  12. {% endblock %}
  1. <!-- upload_complete.html -->
  2. {% extends 'base.html' %}
  3. {% block content %}
  4. <h1>アップロード完了</h1>
  5. <a href="{% url 'monitor:upload' %}"><button>戻る</button></a>
  6. {% endblock %}

実行結果

Djangoを起動して以下のURLにアクセスします。

http://127.0.0.1:8000/monitor/upload/

ファイル選択とアップロードボタンが表示されます。

ファイルを選択せずにアップロードボタンを押すと、警告メッセージが表示されます。

ファイルを選択すると、選択したファイル名が表示されます。

アップロードを実行すると、アップロード完了画面に遷移します。

指定のディレクトリに対象ファイルがアップロードされました。

ファイル種別のバリデーション

アップロードするファイル種別を限定する処理を実装します。
ここではcsvファイルのみを許可するようにします。

forms.py

フォームにバリデーションチェックのメソッドを追加します。
(csv以外の拡張子も受け入れたい場合、対象の拡張子をVALID_EXTENSIONSに追加します。)

  1. # forms.py
  2. from django import forms
  3. import os
  4. VALID_EXTENSIONS = ['.csv']
  5. class UploadFileForm(forms.Form):
  6. file = forms.FileField(
  7. label='アップロードファイル',
  8. )
  9. def clean_file(self):
  10. file = self.cleaned_data['file']
  11. extension = os.path.splitext(file.name)[1] # 拡張子を取得
  12. if not extension.lower() in VALID_EXTENSIONS:
  13. raise forms.ValidationError('csvファイルを選択してください!')

実行結果

csv以外のファイルをアップロードしようとするとエラーメッセージが表示されます。

参考

サンプルソース

サンプルソースをGitHubに公開しています。
https://github.com/kzmrt/graph

Django公式:ファイルのアップロード

https://docs.djangoproject.com/ja/2.1/topics/http/file-uploads/



https://stackoverflow.com/questions/43349031/deploying-django-application-in-aws-raise-disallowed-host-exception

You are privileged to get such a verbose error..

Exception Type: DisallowedHost at / Exception Value: Invalid HTTP_HOST header: 'django-env.vsvztq2hxp.us-west-1.elasticbeanstalk.com'. You may need to add 'django-env.vsvztq2hxp.us-west-1.elasticbeanstalk.com' to ALLOWED_HOSTS.

Just add django-env.vsvztq2hxp.us-west-1.elasticbeanstalk.com to your ALLOWED_HOSTS in settings.py

Do something like this

#in settings.py

ALLOWED_HOSTS = [ 'django-env.vsvztq2hxp.us-west-1.elasticbeanstalk.com', ...]


+ Recent posts