https://cjh5414.github.io/django-settings-separate/



Django는 settings.py라는 모듈을 제공하여 환경설정을 하도록 한다. $ django-admin startproject [myproject] 으로 Django 프로젝트를 생성하면 [myproject]/[myproject]/settings.py 파일이 생성되는데 대부분의 환경설정이 기본값으로 적용되어있다. 그 중에 DEBUG = TRUE로 기본 설정 돼 있는 변수가 있는데 개발 환경에서는 TRUE로, 실제 운영 환경에서는 FALSE로 수정하여 사용하곤 한다. 하나의 settings.py 파일만을 사용하지 않고 공통되는 객체 하나를 상속받아서 개발 환경, 테스트 환경, 운영 환경 등에 따라서 서로 다른 세팅 파일로 나누고 버전 컨트롤 시스템에서 관리하는 방법이 있다.


환경을 분리하여 사용하는 이유

Django 프로젝트의 환경을 분리하여 사용하면 if DEBUG 또는 if not DEBUG 등을 사용하지 않을 수 있고 반복되는 코드(설정)들을 없앨 수 있다. 개인적으로 사용하는 local 환경을 버전 컨트롤로 관리할 수 있고 필요한 경우 팀원들 간에 공유 할 수도 있다.


settings.py 파일 분리하기

_myproject_/
    _myproject_/
        settings/
            __init__.py
            base.py
            development.py
            production.py

settings.py 대신에 위와 같은 구조로 파일을 생성한다. 공통으로 사용되는 환경들을 base.py 에 관리할 것이므로 우선 settings.py 의 내용을 그대로 base.py 에 복사한다. 이제 각각의 환경마다 다르게 사용될 설정들은 base.py 에서 제거하고 환경에 따라 적절하게 작성한다. base.py 를 import 해서 공통으로 사용되는 설정들을 상속받는다.

development.py

from .base import *

DEBUG = True

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

production.py

from .base import *

DEBUG = False

ALLOWED_HOSTS = ['mywebsite.com']

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'name',
        'USER': 'user',
        'PASSWORD': 'password'
    }
}

Django project을 생성할 때 settings.py 에 기본으로 설정되는 BASE_DIR이라는 값이 있다. 프로젝트의 root path를 저장해두는 것으로 DB파일이 저장되는 위치를 지정하는 등의 파일 경로를 지정할 때 사용된다. root path를 구하는 과정을 살펴보면 현재 실행되는 파일의 절대경로를 기준으로 root 폴더까지의 디렉토리의 수 만큼 os.path.dirname()을 사용하여 구하는 방식인데, 우리가 사용하는 설정 파일은 기본적으로 생성되는 settings.py 의 위치보다 한 단계 더 하위에 존재하므로 아래와 같이 os.path.dirname()을 한 번 더 입력해서 수정해줄 필요가 있다.

base.py

import os

BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

[...]


settings 적용

이렇게 각각 나눠진 development.py  production.py 의 설정을 이용하여 shell과 runserver 명령을 이용하기 위해서는 --settings 옵션을 이용하면 된다.

  • $ python manage.py shell --settings=myproject.settings.development
  • $ python manage.py runserver --settings=myproject.settings.development

하지만 매번 --settings 옵션을 입력하는 것은 매우 번거로운 일이다.

DJANGO_SETTINGS_MODULE 환경변수를 원하는 환경으로 설정하면 --settings 옵션 없이도 이용할 수 있다.

$ export DJANGO_SETTINGS_MODULE=myproject.settings.development
$ python manage.py runserver

이것 또한 shell을 실행시킬 때마다 환경변수를 설정해줘야 하므로 번거롭다. python 가상환경을 사용하고 있다면 환경을 실행할 때 DJANGO_SETTINGS_MODULE 환경변수를 등록하도록 설정해주면 편하다. 여기서는 autoenv를 사용할 때, .env 파일을 사용하여 설정하는 방법을 이용했다. (참고 : python 가상 환경 구축하기)

.env

pyenv activate myproject-env
DJANGO_SETTINGS_MODULE=myproject.settings.development

shell 에서 .env 파일이 있는 경로로 이동하면 위의 내용을 알아서 실행하는데, 우선 myproject-env라는 가상환경을 실행하고 DJANGO_SETTINGS_MODULE 환경변수를 등록한다.


pytest-django pytest.ini

pytest.ini 파일을 이용하여 pytest-django를 사용하고 있다면 DJANGO_SETTINGS_MODULE를 아래와 같이 수정해줘야 된다. (참고 : Django에서 pytest 사용하기)

pytest.ini

[pytest]
DJANGO_SETTINGS_MODULE=myproject.settings.development
python_files = tests.py test_*.py *_tests.py *_test.py


django 환경에 맞게 requirements.txt 분리

어떤 패키지는 운영 시에는 사용되지 않지만 개발의 편의를 위해서 필요한 경우가 있다. 이런 경우 requirements.txt requirements-dev.txt 등과 같이 패키지 리스트를 분리하여 사용하기도 한다. Django 환경을 분리하여 사용하는 경우 requirements.txt 파일들도 각각의 환경에 맞게 분리하여 사용하는 것이 좋다. 위에서 settings/ 의 구조와 마찬가지로 requirements/을 아래와 같이 분리한다.

requirements/
    base.txt
    development.txt
    production.txt

마찬가지로 공통으로 사용되는 의존성은 base.txt 에 넣고 따로 사용되는 의존성은 분리하여 각각의 파일에 넣는다.

base.txt

Django==1.10.5
[...]

development.txt

-r base.txt
pytest==3.0.5
[...]


참고자료


https://blueshw.github.io/2016/03/03/django-using-custom-templatetags/
[django] 커스텀 템플릿태그(templatetags) 활용하기


웹 개발을 하다보면, html 코드 상에서 다양한 연산을 해야하는 경우가 발생합니다. 그래서 php, jsp, asp, jade 등 각 언어별 웹 프레임워크에서 이와 같은 경우를 처리해주기 위한 기능을 제공하고 있습니다. 장고(django) 템플릿(template)에서도 위와 같은 웹 프레임워크와 같이 동일한 기능을 지원하는 템플릿태그(templatetags)라는 것이 있습니다. 장고의 템플릿태그는 다른 웹 프레임워크와 마찬가지로 기본적으로 개발자가 필요한 기능은 대부분 제공하고 있습니다.

웹 프레임워크가 기본적인 기능을 대부분 제공하고 있지만, 개발을 하다보면 자신이 원하는 기능이 없는 경우가 간혹 있습니다. 그래서 장고에서는 개발자가 커스텀으로 템플릿태그를 만들수 있는 기능을 제공하고 있습니다.

우선 아래와 같이 앱(app) 아래에 templatetags 라는 폴더를 만들어 줍니다. temaplatetags 라는 폴더 이름은 고정값이므로 반드시 동일하게 생성합니다.

proj/
	app/
		__init__.py
		models.py
		view.py
		templatetags/
			__init__.py
			custom_tags.py    (커스텀 템플릿태그를 저장할 모듈 파일)

이때, app 은 반드시 setting 파일의 INSTALLED_APPS 에 추가가 되어 있어야 합니다. 그리고 한가지 주의할 점은 여러 앱에 각각 templatetags 가 있는 경우, 모듈의 이름이 겹치지 않도록해야 합니다. 이유는, template 에서 커스텀 태그는 앱의 위치와 상관없이 모듈 이름으로 로드되므로 이름이 겹치게 되면 충돌이 발생하게 됩니다. 즉,

proj/
	app/
		templatetags/
			custom_tags.py
	common/
		templatetags/
			common_tags.py    (태그모듈 이름이 겹치지 않도록 주의!!!)

태그 모듈을 사용하는 방법은 간단합니다. 커스텀 태그를 사용하고자하는 템플릿 파일의 상단에 아래와 같이 한줄만 추가해주면 됩니다.

{% load custom_tags %}

그렇다면 실제로 커스텀 태그를 만들어서 사용하는 예제를 만들어 보도록 하겠습니다. custom_tags.py 파일을 열어 사용하려는 태그 이름으로 메서드 이름으로 지정하여 만들어줍니다.

@register.filter            # 1
def add_str(left, right):
return left + right

@register.simple_tag            # 2
def today():
return datetime.now().strftime("%Y-%m-%d %H:%M")

@register.assignment_tag            # 3
def max_int(a, b):
return max(int(a), int(b))

@register.inclusion_tag(div.html, takes_context=True)            # 4
def include_div(context):
return {
'div_param': context['param']
}

대략 위의 4 가지 태그로 구분할 수 있는데, 각 태그에 따라서 사용법이 조금씩 다릅니다. 이 4 가지 커스텀 태그만 이용하면 웬만한 기능은 다 만들어 낼 수 있습니다. 번호별 사용법은 아래와 같습니다.

# 1.
# filter 태그는 앞의 값(left)에다가 뒤의 값(right)을 연산하는 태그입니다.
# filter이기 때문에 여러개의 필터를 붙여서 사용가능합니다.
# add_str 메서드의 left 파라미터가 prefix에 해당하고, right 파라미터가 url에 해당합니다.
# 결과적으로 prefix + url이 add_str 메서드를 통해 div의 text가 되는 것이지요.

<div>
{{ prefix | add_str: url | add_str: name | add_str: params }}
</div>

# 2.
# simple_tag는 단순히 어떤 특정값을 출력합니다.
# 아래와 같이 today를 입력하면, "2016-3-2 10:00"과 같이 현재 시간이 출력됩니다.

<div>
{{ today }}
</div>

# 3.
# assignment_tag는 템플릿에서 사용가능한 변수에 결과를 저장하는 역할을 합니다.
# 어찌보면 with 태그와 유사한 형태라 할 수 있으나, with과는 다르게 {% endwith %} 처럼 끝을 맺어줄 필요가 없습니다.
# 즉, 좀 더 간편하게 변수를 설정해 줄 수 있고, 필요한 기능을 태그 모듈에 별도로 삽입할 수 있다는 장점이 있습니다.

{% max_int first_count second_count as max_count %}

# 4.
# inclusion_tag는 저도 프로젝트에 직접 사용해 보진 않았지만, 테스트는 해보았습니다.
# 간략히 설명해서 inclusion_tag를 사용하면 데코레이터의 첫번째 파라미터인 템플릿을 호출하여 부모 템플릿에 출력합니다.
# 이때, 호출되는 템플릿에 부모 템플릿(호출하는 템플릿)의 각종 파라미터를 전달해 줄 수 있습니다.
# 데코레이터의 takes_context=True로 설정해주면,
# 부모 템플릿의 context의 값을 가져와 호출하는 템플릿으로 전달할 수 있습니다.

parent.html
{{ include_div }}

div.html
<div>{{ div_param }}</div>

이상 커스텀 태그의 종류와 사용법에 대해서 알아보았습니다. 제가 설명드린 커스텀 태그는 아주 기초적인 부분이라 제작 및 사용법이 아주 간단한데요. 커스텀 태그 파일은 파이썬 모듈이기 때문에 파이썬에서 사용할 수 있는 내장함수와 모든 확장 모듈을 사용할 수 있기 때문에, 얼마든지 복잡하고 파워풀한 기능을 가진 태그를 만들어 낼 수 있습니다.

하지만, 복잡한 연산을 처리하는 것은 템플릿보다는 웹서버 단에서 처리하는 것이 우선이고, 서버에서 처리가 곤란하거나 불가피한 상황인 경우에 태그를 사용해서 처리하는 것이라 생각합니다. 아마 장고에서도 사용가능한 기본 태그를 최소한으로 만들어 놓은 것도 같은 이유 때문일 거라 생각이 드네요.

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 とかでアクセスできるようになります。

+ Recent posts