Pycharm으로 manage.py startrunserver나


아니면 자체적으로 서버를 런할경우


자꾸 Process Exit (0) 이러면서 꺼지는경우가 있는데;


이것은 마소자체적인 버그라고 한다;


아래처럼 설정해주면 해당 에러가 나지 않는다.



Posted by C마노
,

* Abstract Base Class

   

   - 어떠한 공통적인 필드를 여러 모델에 삽입하려는 경우에 유용

   - 실제로 테이블을 생성하지 않음

   - 기본 클래스를 작성후에 그안에 Meta 클래스 작성후 abstract=True 를 넣어줌

   - 부모필드 ( 즉 여기선 abstract Class Model ) 과 같은 이름을 가지는 자식클래스의 안의 필드를 정의 할 수 없습니다.


from django.db import models

class CommonInfo(models.Model):
    name = models.CharField(max_length=100)
    age = models.PositiveIntegerField()

    class Meta:
        abstract = True

class Student(CommonInfo):
    home_group = models.CharField(max_length=5)

여기서 Student 는 name, age, home_group의 필드를 가집니다.

그리고 Student는 name과 age를 정의할 수 없다는 뜻입니다.


최대의 장점으로는 자식 모델당 하나의 데이터베이스 테이블만 생성하면서 Python 레벨에서 공통적인 정보를 제외시킬수 있다는 점입니다.


자식 클래스가 자신의 Meta 클래스를 선언하지 않으면, 부모클래스의 Meta를 상속 받습니다.  자식이 부모의 Meta 클래스를 확장하려고 하면 아래와 같이합니다.


from django.db import models

class CommonInfo(models.Model):
    # ...
    class Meta:
        abstract = True
        ordering = ['name']

class Student(CommonInfo):
    # ...
    class Meta(CommonInfo.Meta):
        db_table = 'student_info'

[Multi table 상속]


Django 가 지원하는 모델 상속의 두번째 유형은 계층 구조의 각 모델이 모두 하나의 모델일때 입니다.

이 방법은 복잡하고, 느리므로 최대한 사용하지 않는 것을 권장합니다.


각 모델은 자체 데이터베이스 테이블에 해당하며 개별적으로 쿼리하고 생성 할 수 있습니다.


from django.db import models

class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)

class Restaurant(Place):
    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=False)

Place의 모든 필드는 Restaurant에서 사용 할 수 있고, 데이터는 다른 데이터베이스 테이블에 저장됩니다. 그래서 둘다 가능.


[ Proxy Model ]


다중 테이블 상속을 사용할 때 모델의 각 하위 클래스에 대해서 새로운 데이터 베이스 테이블을 생성하는 방법입니다. 


프록시의 제일큰 특정은 원본을 변경하지 않고 프록시에서 기본 모델 순서 또는 기본 관리자와 같은 것을 변경할 수 있다는 것입니다.


from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

class MyPerson(Person):
    class Meta:
        proxy = True

    def do_something(self):
        # ...
        pass

여기서 보면 MyPerson 클래스는 상위 Person 클래스와 동일한 데이터 베이스 테이블에서 작동합니다. 특히 Person의 새 인스턴스는 MyPerson을 통해 엑세스 할 수 있으며, 그 반대의 경우도 가능하다는 것입니다.


>>> p = Person.objects.create(first_name="foobar")
>>> MyPerson.objects.get(first_name="foobar")
<MyPerson: foobar>

또한 프록시 모델을 사용해서 모델에서 원본모델에서 사용하는 ordering을 정의할 수도 있습니다. 


class OrderedPerson(Person):
    class Meta:
        ordering = ["last_name"]
        proxy = True


Posted by C마노
,

[파이썬 스타일]


1. 파이썬 파일은 들여쓰기에 space 4칸을 사용하십시오. HTML은 2칸을 사용합니다.


2. 따로 명시가 되어있지 않는 한은 PEP8을 따릅니다.


flake8을 사용해서 문제가 있는 코드를 확인하고, 특히 하나가 PEP8이랑 다른데 바로 79길이제한입니다.


코드줄을 79로 제한할 필요는 없습니다. 79자의 코드 길이는 GitHub 코드를 볼때 최적화된 너비이므로 Django에서는 119자까지 허용합니다. 


3. 들여쓰기에는 4개의 스페이스를 사용하세요


4. 변수, 함수 및 메소드 이름은 camelCase가 아닌 밑줄을 사용하시기 바랍니다.


poll.getUniqueVoters (X)

poll.get_unique_voters 가 맞습니다.


5. 클래스 이름에는 첫글자를 대문자로 사용합니다.


6. DocString은 꼭 사용하는걸로 합시다.


def foo():
    """
    Calculate something and return the result.
    """
    ...


7. testCode에서는 assetRaises() 대신에, assertRaisesMessage()를 사용해서 예외 메세지를 확인할 수 있습니다. 


[임포트]


1. isort를 사용해서 import 정렬을 자동화하시기 바랍니다.


$ pip install isort
$ isort -rc .

만약, 특별한 경우에 import 정렬이 필요없다고 생각되신다면 

아래의 방침을 따라하십시오.


import module  # isort:skip

2. import를 쓰기전에는 항상 from을 사용하시기를 바랍니다.

import module이 아닌 from module import objects가 맞는겁니다.


3. import 시에는 무조건 상대적 가져오기가 맞는겁니다.

import corn from xx 가아닌.

import . from xx 가 맞습니다.


4. 만약 긴줄의 import가 필요하다면 아래와같이 하십시오. 


from django.http.response import (
    Http404, HttpResponse, HttpResponseNotAllowed, StreamingHttpResponse,
    cookie,
)

import 마지막에는 , 를 포함해야 하고, 닫는 괄호는 마지막 줄에 넣습니다.


5. 마지막 import와 module 코드 사이에는 빈줄 하나를 사용하고 첫번째 함수 또는 클래스위에 빈줄 2개를 사용하시기 바랍니다.


전체적인 예입니다.


# future
from __future__ import unicode_literals

# standard library
import json
from itertools import chain

# third-party
import bcrypt

# Django
from django.http import Http404
from django.http.response import (
    Http404, HttpResponse, HttpResponseNotAllowed, StreamingHttpResponse,
    cookie,
)

# local Django
from .models import LogEntry

# try/except
try:
    import pytz
except ImportError:
    pytz = None

CONSTANT = 'foo'


class Example(object):
    # ...

6. 가능한 한, 편리한 임포트 방식을 사용하십시오.


from django.views.generic.base import View

위의 방법 대신


from django.views import View

위의방법이 더 좋습니다.


[템플릿 스타일]


{{foo}}

위의 방법은 안되고,

{{ foo }}

위의 방법을 사용하십시오.


[뷰 스타일]


1. 항상 첫번째 파라미터는 request입니다. 명심하세요.


def my_view(request, foo):
    # ...

맞는방식이고


def my_view(req, foo):
    # ...

잘못된 방식입니다.


[모델 스타일]


1. 필드 이름은 모두 소문자 이어야만 하고, camelCase 대신에 밑줄을 사용해야합니다.


class Person(models.Model):
    first_name = models.CharField(max_length=20)
    last_name = models.CharField(max_length=40)

아래는 잘못된 예입니다.

class Person(models.Model):
    FirstName = models.CharField(max_length=20)
    Last_Name = models.CharField(max_length=40)


2. 메타 클래스는 필드가 다 정의 되고 난다음에 작성되어야 하고, 필드와 클래스를 정의를 구분하는 하나의 빈줄이 필요합니다.


아래는 정상적인방식


class Person(models.Model):
    first_name = models.CharField(max_length=20)
    last_name = models.CharField(max_length=40)

    class Meta:
        verbose_name_plural = 'people'

아래는 안됩니다.


class Person(models.Model):
    first_name = models.CharField(max_length=20)
    last_name = models.CharField(max_length=40)
    class Meta:
        verbose_name_plural = 'people'

아래도 안됩니다.


class Person(models.Model):
    class Meta:
        verbose_name_plural = 'people'

    first_name = models.CharField(max_length=20)
    last_name = models.CharField(max_length=40)


3. [이거는 꼭 해야되는지 모르겠는데 사실 PYTHON3 만 쓰겟다! 하면 안해도됩니다.]

__str__ method를 사용했다면

python_2_unicode_compatible() 을 사용해서 모델을 꾸미세요.


4. 모델의 내부 클래스와 표준 메소드의 순서는 다음과 같아야 합니다. 


[1] 모든 db 필드

[2] 커스텀 매니저 속성들

[3] 메타클래스

[4] def __str__()

[5] def save()

[6] def get_absolute_url()

[7] 다른 커스텀 메소드들


5. 만약 choice 필드를 사용한다면, 모델의 클래스 속성으로 대문자 이름을 가진 튜플의 튜플로 각 선택사항을 정의해야합니다.


아래의 예를보세요


class MyModel(models.Model):
    DIRECTION_UP = 'U'
    DIRECTION_DOWN = 'D'
    DIRECTION_CHOICES = (
        (DIRECTION_UP, 'Up'),
        (DIRECTION_DOWN, 'Down'),
    )

[django.conf.settings]


모듈은 일반적으로 django.conf.settings에 저장된 설정을 최상위 레벨에서 사용하지 말아야 합니다.


[기타 잡다한것]


1. 국제화를 위해서 모든 국제 문자를 지원하십시오.i18n 설명서를 참조하세요


2. 사용하지 않는 import 는 제거하십시오.


3. 제발 코드에 이름을 넣지마십시오 이것은 굉장히 멍청한 짓입니다. 

Posted by C마노
,

Manager는 Django 모델에 데이터베이스 쿼리작업을 제공하는 인터페이스입니다.

Django 응용프로그램의 모든 모델에는 최소 하나 이상의 Manager가 있습니다.


기본적으로 Django는 모든 Django 모델 클래스에 objects 라는 이름의 Manager를 추가합니다.


그러나, 객체 필드를 이름으로 사용하거나 Manager용 객체 이외의 이름을 사용하려는 경우에는 모델별로 이름을 변경할 수 있습니다.


주어진 클래스에 대해 Manager의 이름을 바꾸려면, 해당 모델에서 models.Manager() 유형을 클래스 속성을 정의하십시오.


from django.db import models

class Person(models.Model):
    #...
    people = models.Manager()


기본 Manager 클래스를 확장하고 모델에서 사용자 정의 Manager를 인스턴스화 하여 특정 모델에서 사용자 정의 Manager를 사용 할 수 있습니다.


사용자 정의 Manager를 사용하는 이유는 2가지가 있습니다.


Manager 메소드를 추가하거나 Manager가 반환하는 초기 QuerySet을 수정하는 이유입니다.


[대표적인 예]


class PostManager(models.Manager):
def active(self, *args, **kwargs):
# Post.objects.all() = super(PostManager, self).all()
return super(PostManager, self).filter(draft=False).filter(publish__lte=timezone.now())


추가 관리자에 메서드 추가하기


우선은 모델에 "테이블 수준"의 기능을 추가하고 싶을 때 사용할 수 있습니다.


행 수준의 기능을 추가할때는 Model 메소드를 사용하십시오 여기서는 컬럼기준일떄 이야기합니다.


사용자 정의 관리자 메소드는 원하는 모든 것을 리턴 할 수 있습니다. QuerySet을 리턴할 필요는 없습니다.


예를 들어, 사용자 정의 관리자는 모든 OpinionPoll 객체의 목록을 반환하는 with_counts() 메서드를 제공합니다.


그런데 각 쿼리에는 집계 쿼리의 결과인 num_responses 속성이 추가로 있습니다.


from django.db import models

class PollManager(models.Manager):
    def with_counts(self):
        from django.db import connection
        with connection.cursor() as cursor:
            cursor.execute("""
                SELECT p.id, p.question, p.poll_date, COUNT(*)
                FROM polls_opinionpoll p, polls_response r
                WHERE p.id = r.poll_id
                GROUP BY p.id, p.question, p.poll_date
                ORDER BY p.poll_date DESC""")
            result_list = []
            for row in cursor.fetchall():
                p = self.model(id=row[0], question=row[1], poll_date=row[2])
                p.num_responses = row[3]
                result_list.append(p)
        return result_list

class OpinionPoll(models.Model):
    question = models.CharField(max_length=200)
    poll_date = models.DateField()
    objects = PollManager()

class Response(models.Model):
    poll = models.ForeignKey(OpinionPoll, on_delete=models.CASCADE)
    person_name = models.CharField(max_length=50)
    response = models.TextField()


이 예제에서는 OpinionPool.objects.with_counts() 를 사용해서 num_responses 속성을 가지는 OpinionPoll 객체의 목록을 반환합니다.


관리자의 기본 QuerySet은 시스템의 모든 객체를 반환합니다.


예를들어 이런 모델이 있다고 가정합시다.


from django.db import models

class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.CharField(max_length=50)

Book.objects.all() 문은 데이터베이스의 모든 책을 반환합니다.


Manager.get_queryset() 메서드를 재정의 해서 Manager의 기본 QuerySet을 재정의 할 수 있습니다.


get_queryset()은 QuerySet을 반환해야만 합니다.


예를들어 다음 모델에는 두개의 관리자가 있고, 하나는 모든 객체를 반환하고 하나는 Roald Dahl책만 반환합니다.


# First, define the Manager subclass.
class DahlBookManager(models.Manager):
    def get_queryset(self):
        return super(DahlBookManager, self).get_queryset().filter(author='Roald Dahl')

# Then hook it into the Book model explicitly.
class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.CharField(max_length=50)

    objects = models.Manager() # The default manager.
    dahl_objects = DahlBookManager() # The Dahl-specific manager.

이 샘플 모델을 사용하면 Book.objects.all()은 데이터베이스의 모든책을 반환하지만, Book.dahl_objects.all()은 Roald Dahl이 작성한 책만 반환합니다.


물론 get_queryset()은 QuerySet 객체를 반환하기 때문에 filter(), exclude() 및 기타 모든 QuerySet 메서드를 사용 할 수 있습니다. 


Book.dahl_objects.all()
Book.dahl_objects.filter(title='Matilda')
Book.dahl_objects.count()

그리고 하나의 모델에서 여러관리자를 사용 할 수 있다에 주목해주세요


모델에 원하는 만큼의 Manager() 인스턴스를 첨부할 수 있습니다. 이렇게하면 모델에 대한 일반적인 "필터"를 쉽게 정의 할 수 있습니다.


예제입니다.


class AuthorManager(models.Manager):
    def get_queryset(self):
        return super(AuthorManager, self).get_queryset().filter(role='A')

class EditorManager(models.Manager):
    def get_queryset(self):
        return super(EditorManager, self).get_queryset().filter(role='E')

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    role = models.CharField(max_length=1, choices=(('A', _('Author')), ('E', _('Editor'))))
    people = models.Manager()
    authors = AuthorManager()
    editors = EditorManager()

이 예제에서는 Person.authors.all(), Person.editors.all() 및 Person.people.all()을 요청하여 예측 가능한 결과를 얻을 수 있습니다.




Posted by C마노
,

Restful 공부중. permission에 대해선 확실히 알고 가야 할것같아. 따로 포스팅한다.


[Permission]


Permission 체크는 항상 view 의 시작에 하게 되는데. ( 다른코드의 진행 전에 실행된단 이야기다 )

기본적으로 Permission Check는 request.user 와 reuqest.auth 의 정보를 사용해서 인증을 처리한다.


Permission은 사용자가 정해진 클래스 이외의 클래스의 접근을 거부하거나 허용하는데 사용한다.


가장 간단하게 사용하는 방법은 인증된 사용자에게는 Access를 허용하고 인증되지 않은 모든 사용자는 Access를 거부하는방법이다.


이것은 REST 프레임 워크의 IsAuthenticated  에 해당합니다.


IsAuthenticated  보다 조금 덜 엄격한 스타일은 권한 없는(인증 되지 않은)사용자에게 읽기 전용 액세스를 허용하는 IsAuthenticatedOrReadOnly  가 있겠다.


[어떻게 권한을 결정하나?]


REST Framework의 권한은 항상 권한 클래스의 목록으로 정의되는데,

View의 본문이 실행되기 전에 목록의 각 권한이 검사된다.


만약 권한통과에 실패하게 되면 exceptions.PermissionDenied 나 exceptions.NotAuthenticated 의 Exception이 raise 하게되고, View의 본문은 실행되지 않는다.


권한 검사가 실패하면, 403 Forbidden또는 401 Unauthorized 응답이 반환되게 된다.


* 요청이 성공적으로 인증되었지만, 권한이 거부되었습니다. - 403에러가 리턴된다.

* 요청이 성공적으로 인증되지 않고 헤더가 WWW-Authenticate 헤더를 사용하지 않았다. - 403에러가 리턴된다.

* 요청이 성공적으로 인증되지않고 헤더가  WWW-Authenticate 를 사용했다 - 401에러가 리턴된다.


[객체레벨 권한]


Object-level-permission은 사용자가 특정한 객체에 대한 작업을 허용해야 하는지 (일반적으로 model 인스턴스입니다) 에 대한 결정을 하는데 사용되어 진다. 


객체 레벨 권한은 .get_object()가 호출 될때, REST 프레임워크의 일반 뷰에 의해 실행됩니다.


사용자가 만약 해당 객체에 권한이 없으면 exceptions.PermissionDenied 가 리턴됩니다.


자신의 View를 작성하고 있고, 오브젝트 레벨 권한을 적용하려는 경우나 일반 View에서 get_object를 오버라이드 하는 경우 오브젝트를 검사한 시점에서 뷰에서 .check_object_permissions(request, obj) 를 호출해야 합니다.


[예제]


def get_object(self):
    obj = get_object_or_404(self.get_queryset())
    self.check_object_permissions(self.request, obj)
    return obj

[권한 정책 설정]


기본 권한 정책을 설정할 수 있는데 DEFAULT_PERMISSION_CLASSES 를 사용해서 기본 권한 정책을 사용 할 수 있습니다.


REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    )
}

지정하지 않으면 설정은 기본적으로 제한없는 엑세스를 허용합니다.


'DEFAULT_PERMISSION_CLASSES': (
   'rest_framework.permissions.AllowAny',
)

또한 클래스 기반 APIView 를 사용해서 VIew마다 또는 viewSet마다 인증 정책을 설정할 수 있습니다.


from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView

class ExampleView(APIView):
    permission_classes = (IsAuthenticated,)

    def get(self, request, format=None):
        content = {
            'status': 'request was permitted'
        }
        return Response(content)

데코레이터를 사용 할 수도 있습니다.


from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response

@api_view(['GET'])
@permission_classes((IsAuthenticated, ))
def example_view(request, format=None):
    content = {
        'status': 'request was permitted'
    }
    return Response(content)

[API 레퍼런스]


AllowAny


제한되지 않은 엑세스를 허용합니다.

IsAuthenticated


인증되지 않은 사용자에게 권한을 거부하고 그렇지 않으면 허용합니다.

IsAdminUser


IsAdminUser 권한 클래스는 user.is_staff가 True인 경우를 제외하고 모든 사용자의 권한을 거부합니다.

IsAuthenticatedOrReadOnly


이 권한은 API에서 익명 사용자에게 읽기 권한만 허용하고, 인증된 사용자에게는 쓰기 읽기권한을 허용하려는 경우에 적합합니다.



[커스텀퍼미션]


BasePermission을 상속받고,


  • .has_permission(self, request, view)
  • .has_object_permission(self, request, view, obj)

해당 2가지 함수를 오버라이딩 해야합니다.

요청에 대한 엑세스 권한이 부여되면 메서드는 True를 반환, 그렇지 않으면 False를 반환해야만 합니다.


요청이 읽기 작업인지, 아니면 쓰기 작업인지 알아야 하는경우 SAFE_METHODS 상수와 비교해서 요청방법을 확인해합니다.


if request.method in permissions.SAFE_METHODS:
    # Check permissions for read-only request
else:
    # Check permissions for write request

검사 방법은 has_permission 검사가 통과 되어야지만, has_object_permission이 호출 된다는것을 명심하시기 바랍니다.

테스트가 실패 할 경우 사용자 지정 권한은 PermissionDenied 예외를 발생시킵니다.


예외와 관련된 오류 메세지를 변경하려면 message = '에러메세지' 를 사용하십시오.


from rest_framework import permissions

class CustomerAccessPermission(permissions.BasePermission):
    message = 'Adding customers not allowed.'

    def has_permission(self, request, view):

[예제]


다음은 들어오는 요청의 IP 주소를 블랙리스트와 대조해서 IP가 블랙리스트에 올랐으면 요청을 거부하는 권한클래스의 예입니다.


from rest_framework import permissions

class BlacklistPermission(permissions.BasePermission):
    """
    Global permission check for blacklisted IPs.
    """

    def has_permission(self, request, view):
        ip_addr = request.META['REMOTE_ADDR']
        blacklisted = Blacklist.objects.filter(ip_addr=ip_addr).exists()
        return not blacklisted

들어오는 모든 요청 (POST,GET,PUT...) 에 대해 실행되는 전역 권한 뿐만 아니라 특정 개체 인스턴스에 영향을 주는 작업에 대해서만 실행되는 개체수준 사용권한을 만들 수도 있습니다.


class IsOwnerOrReadOnly(permissions.BasePermission):
    """
    Object-level permission to only allow owners of an object to edit it.
    Assumes the model instance has an `owner` attribute.
    """

    def has_object_permission(self, request, view, obj):
        # Read permissions are allowed to any request,
        # so we'll always allow GET, HEAD or OPTIONS requests.
        if request.method in permissions.SAFE_METHODS:
            return True

        # Instance must have an attribute named `owner`.
        return obj.owner == request.user


Posted by C마노
,

함수 기반 View 가아닌 Class 기반 View를 사용해서 API View를 작성할 수도 있습니다.

여러분도 잘 알듯이 클래스기반 View는 일반적인 기능을 재사용 할수있는 강력한 패턴입니다. 그리고 코드의 유지를 훨씬 효율적으로 할수 있습니다.


[ 클래스 기반 View를 사용해서 다시 API를 작성해봅시다.]


먼저 Root View를 클래스 기반 View로 다시 작성해봅시다. views.py를 열어주세요.


from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from django.http import Http404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status


class SnippetList(APIView):
    """
    List all snippets, or create a new snippet.
    """
    def get(self, request, format=None):
        snippets = Snippet.objects.all()
        serializer = SnippetSerializer(snippets, many=True)
        return Response(serializer.data)

    def post(self, request, format=None):
        serializer = SnippetSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

이전의 경우와 매우 비슷하게 보이지만 Http메소드를 구분하기에 훨씬 쉽습니다. 


class SnippetDetail(APIView):
    """
    Retrieve, update or delete a snippet instance.
    """
    def get_object(self, pk):
        try:
            return Snippet.objects.get(pk=pk)
        except Snippet.DoesNotExist:
            raise Http404

    def get(self, request, pk, format=None):
        snippet = self.get_object(pk)
        serializer = SnippetSerializer(snippet)
        return Response(serializer.data)

    def put(self, request, pk, format=None):
        snippet = self.get_object(pk)
        serializer = SnippetSerializer(snippet, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk, format=None):
        snippet = self.get_object(pk)
        snippet.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

Detail도 마찬가지로 함수뷰와 비슷하지만 보기에 훨씬 이뻐보입니다. 

클래스 기반 View를 사용하기 때문에 url.py를 살짝 리팩토링해야합니다.


from django.conf.urls import url
from rest_framework.urlpatterns import format_suffix_patterns
from snippets import views

urlpatterns = [
    url(r'^snippets/$', views.SnippetList.as_view()),
    url(r'^snippets/(?P<pk>[0-9]+)/$', views.SnippetDetail.as_view()),
]

urlpatterns = format_suffix_patterns(urlpatterns)

이제, 개발서버를 실행하면 모든 것이 이전과 같이 작동해야 합니다.


[ mixins 사용 ]


클래스 기반 뷰를 사용하면서 얻은 가장 큰 장점 중 하나는 재사용을 쉽게 구성할 수 있다는 것입니다.

지금까지 구현되어온것을 보면 create/retrieve/update/delete 는 공통적으로 사용되는데요.

이러한 공통된 동작은 REST FRAMEWORK의 mixin 클래스에서 구현가능합니다.


mixin 클래스를 사용해서 뷰를 구성하는 방법을 살펴보겠습니다. 여기에 views.py 모듈이 있습니다.


from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework import mixins
from rest_framework import generics

class SnippetList(mixins.ListModelMixin,
                  mixins.CreateModelMixin,
                  generics.GenericAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

GenericAPIView를 사용하고 ListModelMixin 및 CreateModelMixin 을 추가해서 뷰를 작성합니다.


베이스클래스는 핵심기능을 제공하고 mixin 클래스는 .list() 및 .create() 를 제공합니다.

그런다음 get및 post 메소드를 명시적으로 적절한 액션에 바인딩합니다. 


이전과 거의 비슷하지만. GenericAPIView 클래스를 사용해서 핵심 기능을 제공하고 .retrieve(), .update(), 및 .destroy() 액션을 제공하기 위해서 mixin 을 추가합니다.


[ 제네릭 클래스 기반 뷰 사용 ]


mixin 클래스를 사용해서 이전보다 적은 코드를 사용하기 위해 뷰를 새로작성했지만 좀더 나아가봅시다.


from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework import generics


class SnippetList(generics.ListCreateAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer


class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer

와.. 이번엔 진짜 간결합니다. 굉장히 코드는 훌륭하고 깨끗하며 관용적인 장고처럼 보입니다.



Posted by C마노
,

[Request 객체]


REST FRAMEWORK는 일반 HttpRequest 를 확장하고 Request Parsing에 있어서 좀더 유연한 방법을 제공합니다.


Request 객체의 핵심기능은 request.data 속성입니다. 이것은 request.POST와 유사합니다. 그러나 WEB API에 있어서 훨씬더 유용합니다.


request.POST  # 오직 form Data만 핸들링가능. 오직 POST방식만 동작.
request.data  # 속성 데이터를 핸들링.  POST, PUT, PATCH 방식에 동작.

[Response 객체]


REST FRAMWORK는 또한 Response 객체도 도입했는데 이것은 TemplateResponse 타입으로 랜더링 되지 않은 콘텐츠를 사용하고 콘텐츠를 올바르게 가공하여 클라이언트에게 올바른 콘텐츠를 반환 할 수 있도록 합니다.



return Response(data)  # 클라이언트가 요청한 타입으로 컨텐츠를 리턴합니다.

[상태코드]


View에서 HTTP 상태 코드를 사용한다고 해서 분명하게 확인 할 수 있는 것은 아니며, 오류 코드를 통지하는 것이 쉽지는 않습니다. REST FRAMEWORK는 각 상태코드에 대해서 보다 명확히 식별자를 제공합니다. 예를들면 status 모듈 안의  HTTP_400_BAD_REQUEST 입니다. 숫자 식별자를 사용하는 것 보다 전체적으로 이러한 방식을 사용하는것이 훨씬 효과적입니다.


[ API views ]


REST FRAMEWORK는 API 보기를 작성하는데 사용할 수 있는 2개의 Wrapper 를제공합니다.

1. 함수 기반 View 작업을 위한 @api_view 데코레이터

2. 클래스 기반 View 작업을 위한 APIView 클래스


이러한 Wrapper는 Reqest 인스턴스를 전달 받는지 확인하는 것과 같은 몇가지 기능을 제공합니다. 그리고 Content를 가공 할 수 있도록 컨텍스트를 Response객체에 추가합니다.


해당 랩퍼는 405 Methond Not Allowed 와 같은 허용하지 않는 응답을 리턴하는 것과 같은 동작도 제공합니다.


request.data에 엑세스 할떄 발생하는 모든 ParseError 예외를 처리할 수도 있습니다.


[실전]


이제 위의 구성요소들을 사용해서 몇가지 예를 작성해보겠습니다.


첫번째 포스팅에서 사용했던 예제를 사용하겠습니다.


기존의 snippets/view.py를 열고 내용을 아래로 대체합니다.


from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer


@api_view(['GET', 'POST'])
def snippet_list(request):
    """
    List all snippets, or create a new snippet.
    """
    if request.method == 'GET':
        snippets = Snippet.objects.all()
        serializer = SnippetSerializer(snippets, many=True)
        return Response(serializer.data)

    elif request.method == 'POST':
        serializer = SnippetSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

방금 삭제한 소스코드 보다 지금의 예제는 훨씬더 세련되어져 있습니다. 코드가 좀더 간결해지고, Forms API를 사용하는 경우와 매우 비슷합니다. 또한 응답의 의미를 분명하게 해주는 상태코드를 사용하고 있습니다.


list를 작성했으니 detail을 작성해볼게요.


@api_view(['GET', 'PUT', 'DELETE'])
def snippet_detail(request, pk):
    """
    Retrieve, update or delete a snippet instance.
    """
    try:
        snippet = Snippet.objects.get(pk=pk)
    except Snippet.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)

    if request.method == 'GET':
        serializer = SnippetSerializer(snippet)
        return Response(serializer.data)

    elif request.method == 'PUT':
        serializer = SnippetSerializer(snippet, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    elif request.method == 'DELETE':
        snippet.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

위의 예제들은 일반적으로 Django View의 작업과 크게 다르지 않다는 것을 알수 있습니다.

더이상 특정 콘텐츠에 대한 타입에 대한 요청이나 응답을 명시적으로 묶어두지 않습니다.

request.data는 들어오는 json 요청을 처리할 수 있지만, 다른 형식도 처리할 수 있습니다.

마찬가지로 data로 응답 객체를 반환하지만, REST FRAMEWORK가 Response를 올바른 콘텐츠로 랜더링 하도록 허용합니다.


[ URL에 접미사 추가하기 ]


어떤 방식인지 한마디로 설명하자면 http://example.com/api/items/4.json.과 같이 처리 할수 있다는 말입니다.


list와 detail 2가지 view 모두에 format 키워드 인수를 추가하는것으로 시작합니다.


def snippet_list(request, format=None):
def snippet_detail(request, pk, format=None):
이제 urls.py 파일을 약간 손보기로 하겠습니다. format_suffix_patterns 를 추가해줍니다.


from django.conf.urls import url
from rest_framework.urlpatterns import format_suffix_patterns
from snippets import views

urlpatterns = [
    url(r'^snippets/$', views.snippet_list),
    url(r'^snippets/(?P<pk>[0-9]+)$', views.snippet_detail),
]

urlpatterns = format_suffix_patterns(urlpatterns)

이러한 추가 URL 패턴을 반드시 추가 할필요는 없지만, 특정 형식을 간단하고 명확하게 참조 할 수 있도록 합니다.


[결과를 봅시다]


첫번쨰 포스트에서 테스트했던것 처럼 테스트해보세요. 첫번째 포스트와 비슷하게 작동할 겁니다. 유효하지 않은 request 에 대해서 훨씬 nice하게 error을 핸들링 할 수 있습니다.


http http://127.0.0.1:8000/snippets/

HTTP/1.1 200 OK
...
[
  {
    "id": 1,
    "title": "",
    "code": "foo = \"bar\"\n",
    "linenos": false,
    "language": "python",
    "style": "friendly"
  },
  {
    "id": 2,
    "title": "",
    "code": "print \"hello, world\"\n",
    "linenos": false,
    "language": "python",
    "style": "friendly"
  }
]

Accept 헤더를 사용해서 응답의 형식을 제어할수도 있죠


http http://127.0.0.1:8000/snippets/ Accept:application/json  # Request JSON
http http://127.0.0.1:8000/snippets/ Accept:text/html         # Request HTML

또는 format 접미사를 추가해도 됩니다.


http http://127.0.0.1:8000/snippets.json  # JSON suffix
http http://127.0.0.1:8000/snippets.api   # Browsable API suffix

마찬가지로 Content-Type 헤더를 사용해서 보내는 요청의 형식을 제어할수 있습니다.


# POST using form data
http --form POST http://127.0.0.1:8000/snippets/ code="print 123"

{
  "id": 3,
  "title": "",
  "code": "print 123",
  "linenos": false,
  "language": "python",
  "style": "friendly"
}

# POST using JSON
http --json POST http://127.0.0.1:8000/snippets/ code="print 456"

{
    "id": 4,
    "title": "",
    "code": "print 456",
    "linenos": false,
    "language": "python",
    "style": "friendly"
}

위의 http 요청에 --debug 스위치를 추가하면 요청 헤더에서 요청 유형을 볼 수 있습니다.



Posted by C마노
,

원래는 Django 자체에 관한것을 계속적으로 포스팅하고 있었지만,  RESTFRAMEWORK를 실제로 사용할 일이 생겨서 정리차원으로 RestFulFramwork 공식 홈페이지에서 제공하는 튜토리얼을 제가 공부한 방식대로 번역한 글입니다.


== 환경설정 ==


1. virtual env 설정.


virtualenv env
source env/bin/activate

2. django와 restfulframwork 설치.


pip install django
pip install djangorestframework
pip install pygments # Syntax Highlight용.

프로젝트를 시작해봅시다.


cd ~
django-admin.py startproject tutorial
cd tutorial

app을 하나 만들어주시고~

python manage.py startapp snippets
rest_framework와 방금만든 snippets를 settings.py안의 INSTALL_APPS에 추가해봅시다.


INSTALLED_APPS = (
    ...
    'rest_framework',
    'snippets.apps.SnippetsConfig',
)

[모델 만들기]


간단하게 snippet 모델을 한번 만들어 보겠습니다.


snippets/models.py에 들어가서 해당 파일을 수정해봅시다.


from django.db import models
from pygments.lexers import get_all_lexers
from pygments.styles import get_all_styles

LEXERS = [item for item in get_all_lexers() if item[1]]
LANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS])
STYLE_CHOICES = sorted((item, item) for item in get_all_styles())


class Snippet(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    title = models.CharField(max_length=100, blank=True, default='')
    code = models.TextField()
    linenos = models.BooleanField(default=False)
    language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)
    style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)

    class Meta:
        ordering = ('created',)

그후 makemigration과 migrate를 해줍시다~.


python manage.py makemigrations snippets
python manage.py migrate

[직렬화 클래스 만들기]


WEB으로 API를 만들기 전에 중요한 일은 JSON데이터를 파이썬에서 제공하는 Dictionary객체로 직렬화하고 비직렬화 하는 방법이 제일 중요할겁니다. 가장 기초적이기도 하구요.

Django에서 제공하는 Model의 형식과 비슷한 serializers.py를 선언해서 작업을 수행할 수 있습니다.

snippets 디렉토리에 serializers.py 라는 파일을 만들고 아래를 추가해주세요.


from rest_framework import serializers
from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES


class SnippetSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    title = serializers.CharField(required=False, allow_blank=True, max_length=100)
    code = serializers.CharField(style={'base_template': 'textarea.html'})
    linenos = serializers.BooleanField(required=False)
    language = serializers.ChoiceField(choices=LANGUAGE_CHOICES, default='python')
    style = serializers.ChoiceField(choices=STYLE_CHOICES, default='friendly')

    def create(self, validated_data):
        """
        Snippet 인스턴스를 생성하고 리턴합니다. 검증된 데이터가 주어집니다.
        """
        return Snippet.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """
        존재하는 Snippet인스턴스를 업데이트하거나 리턴합니다. 검증된 데이터가 주어집니다.
        """
        instance.title = validated_data.get('title', instance.title)
        instance.code = validated_data.get('code', instance.code)
        instance.linenos = validated_data.get('linenos', instance.linenos)
        instance.language = validated_data.get('language', instance.language)
        instance.style = validated_data.get('style', instance.style)
        instance.save()
        return instance
    id = serializers.IntegerField(read_only=True)
    title = serializers.CharField(required=False, allow_blank=True, max_length=100)
    code = serializers.CharField(style={'base_template': 'textarea.html'})
    linenos = serializers.BooleanField(required=False)
    language = serializers.ChoiceField(choices=LANGUAGE_CHOICES, default='python')
    style = serializers.ChoiceField(choices=STYLE_CHOICES, default='friendly')

위의 부분은 serialize / deserialize 되는 필드를 정의하는 겁니다.

create() 및 update() 메서드는 serializer.save()를 호출 할때, 인스턴스가 create 되거나 update되는것을 정의합니다.


serializer 클래스는 Django Form 클래스와 비슷하며, required, max_length 및 default와 같은 여러가지 필드에 유효성 검사 Flag를 포함 합니다.


[ 직렬화 / 비직렬화 방법 ] 


우선 해당 방법을 배우기 전에 django의 shell에 접속합니다.

python manage.py shell
그리고 아래의 코드를 한번 입력해 봅시다.

from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser

snippet = Snippet(code='foo = "bar"\n')
snippet.save()

snippet = Snippet(code='print "hello, world"\n')
snippet.save()

해당 코드에 대한 Snippet을 생성하였습니다.


해당 코드를 한번 serialize 해보겠습니다.


serializer = SnippetSerializer(snippet)
serializer.data
# {'id': 2, 'title': u'', 'code': u'print "hello, world"\n', 'linenos': False, 'language': u'python', 'style': u'friendly'}

위의 시점에서 모델 인스턴스를 Python 에서 제공하는 딕셔너리 데이터 유형으로 변환하였고, 직렬화를 마루리 하기위해서 데이터를 json으로 변환합니다.


content = JSONRenderer().render(serializer.data)
content
# '{"id": 2, "title": "", "code": "print \\"hello, world\\"\\n", "linenos": false, "language": "python", "style": "friendly"}'

자 여기까지는 OK이라고 가정하고 그렇다면 JSON 데이터를 Python 딕셔너리 타입으로 바꾸려면 어떻게해야할까요?


from django.utils.six import BytesIO

stream = BytesIO(content)
data = JSONParser().parse(stream)

우선 JSONParser를 사용해서 해당 stream을 파싱한 후에,

SnippetSerializer에 해당 값을 주입해서 완전한 객체 인스턴스로 복원시킵니다. (아래코드를 보세요)


serializer = SnippetSerializer(data=data)
serializer.is_valid()
# True
serializer.validated_data
# OrderedDict([('title', ''), ('code', 'print "hello, world"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])
serializer.save()
# <Snippet: Snippet object>

또한 Django의 쿼리셋을 model대신에 직렬화 할 수도 있습니다.

이럴 때는 간단하게 many=True를 매개변수에 집어넣으면 됩니다.


serializer = SnippetSerializer(Snippet.objects.all(), many=True)
serializer.data
# [OrderedDict([('id', 1), ('title', u''), ('code', u'foo = "bar"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 2), ('title', u''), ('code', u'print "hello, world"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 3), ('title', u''), ('code', u'print "hello, world"'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])]

[ ModelSerializers 사용 ]


SnippetSerializer 클래스는 Snippet 모델에 포함된 많은 정보를 복제하고 있습니다. 그렇지만 실제로 보면 코드가 너무 복잡합니다. 코드의 내용만 좀 다를뿐 중복된 내용도 존재하구 있구요.

Django에서 From 클래스와 ModelForm 클래스를 제공하는 것 처럼 RESTFUL FRAMEWORK도 Serializer클래스와 ModelSerializer 클래스를 모두 지원합니다.


자, 그럼 ModelSerializer 클래스를 사용해서 serializer를 리팩토링 하는 방법을 실제로 한번 해보겠습니다.

파일 snippets / serializers.py 파일을 다시 열고, SnippetSerializer 클래스를 바꿔줍니다.


class SnippetSerializer(serializers.ModelSerializer):
    class Meta:
        model = Snippet
        fields = ('id', 'title', 'code', 'linenos', 'language', 'style')

serializer 가 가지고 있는 장점 중 하나는 serializer 인스턴스의 모든 필드를 검사 할 수 있다는 점입니다. 

Django 쉘을 python manage.py 쉘로 엽니다.


from snippets.serializers import SnippetSerializer
serializer = SnippetSerializer()
print(repr(serializer))

* ModelSerializer 는 아래와 같은 기능을 합니다.

1. 자동으로 필드 셋을 결정해줍니다.

2. create 와 update 메소드를 기본적으로 구현해줍니다.


[ 실제로 Django 의 View에 적용해보기]


snippets/views.py 파일에 아래를 추가합니다.


from django.http import HttpResponse, JsonResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer

API의 루트는 모든 snippet을 나열하거나 새로운 snippet을 만드는것을 지원하는 view 입니다.


@csrf_exempt
def snippet_list(request):
    """
    List all code snippets, or create a new snippet.
    """
    if request.method == 'GET':
        snippets = Snippet.objects.all()
        serializer = SnippetSerializer(snippets, many=True)
        return JsonResponse(serializer.data, safe=False)

    elif request.method == 'POST':
        data = JSONParser().parse(request)
        serializer = SnippetSerializer(data=data)
        if serializer.is_valid():
            serializer.save()
            return JsonResponse(serializer.data, status=201)
        return JsonResponse(serializer.errors, status=400)

만약 CSRF 토큰이 없는 클라이언트로부터 이 View로 POST를 할 수 있기를 원한다면 View에다가 csrf_exempt 데코레이터를 추가해줘야 합니다.

이것은 일반적으로 이렇게 구현하면 아니지만, REST Framwork view는 실제로 이러한 방법을 사용하지 않지만 지금은 설명을 위해 사용할것입니다.


또한 개별 snippet에 해당하는 view가 필요하며, snippet을 검색 업데이트 삭제 하는데 사용 되어질 수 있습니다.


@csrf_exempt
def snippet_detail(request, pk):
    """
    Retrieve, update or delete a code snippet.
    """
    try:
        snippet = Snippet.objects.get(pk=pk)
    except Snippet.DoesNotExist:
        return HttpResponse(status=404)

    if request.method == 'GET':
        serializer = SnippetSerializer(snippet)
        return JsonResponse(serializer.data)

    elif request.method == 'PUT':
        data = JSONParser().parse(request)
        serializer = SnippetSerializer(snippet, data=data)
        if serializer.is_valid():
            serializer.save()
            return JsonResponse(serializer.data)
        return JsonResponse(serializer.errors, status=400)

    elif request.method == 'DELETE':
        snippet.delete()
        return HttpResponse(status=204)

마지막으로 이러한 view을 연결해봅시다. snippets/urls.py 파일을 만듭니다.


from django.conf.urls import url
from snippets import views

urlpatterns = [
    url(r'^snippets/$', views.snippet_list),
    url(r'^snippets/(?P<pk>[0-9]+)/$', views.snippet_detail),
]

그리고 totorials/urls.py에 해당 라인을 추가합니다.


from django.conf.urls import url, include

urlpatterns = [
    url(r'^', include('snippets.urls')),
]

그렇지만 현재 문제점이 존재하기는 합니다. 정형화된 json을 보내지 않거나 request에 정상적인 데이터를 넣지않으면 500에러가 발생합니다. 해결방법은 다음 포스팅에서 정리하도록 하겠습니다.


[ API 테스팅 시도하기 ]


우선 shell을 닫고 quit()


django를 실행합니다.


python manage.py runserver

Validating models...

0 errors found
Django version 1.8.3, using settings 'tutorial.settings'
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

그뒤 아래 URL로 요청해보면.


http http://127.0.0.1:8000/snippets/

HTTP/1.1 200 OK
...
[
  {
    "id": 1,
    "title": "",
    "code": "foo = \"bar\"\n",
    "linenos": false,
    "language": "python",
    "style": "friendly"
  },
  {
    "id": 2,
    "title": "",
    "code": "print \"hello, world\"\n",
    "linenos": false,
    "language": "python",
    "style": "friendly"
  }
]

ID로도 조회가 가능합니다.~


http http://127.0.0.1:8000/snippets/2/

HTTP/1.1 200 OK
...
{
  "id": 2,
  "title": "",
  "code": "print \"hello, world\"\n",
  "linenos": false,
  "language": "python",
  "style": "friendly"
}

우선은 여기까지로 포스팅을 끝내고 2부를 계속 포스팅하도록 하겠습니다.

Posted by C마노
,

저번 포스트에서 포스팅 했듯이 시스템에는 기본 제공 태그와 필터가 함께제공됩니다.

이번 포스트에서는 가장 일반적인 태그와 필터를 정리해보겠습니다.


[태그]


* if / else


{% if %} 태그는 변수를 평가합니다. 


{% if today_is_weekend %}
<p>Welcome to the weekend!</p>
{% endif %}

An `{% else %}` tag is optional:

{% if today_is_weekend %}
<p>Welcome to the weekend!</p>
{% else %}
<p>Get back to work.</p>
{% endif %}

elif 절도 사용 가능하죠.


{% if athlete_list %}
Number of athletes: {{ athlete_list|length }}
{% elif athlete_in_locker_room_list %}
<p>Athletes should be out of the locker room soon! </p>
{% elif ...
...
{% else %}
<p>No athletes. </p>
{% endif %}

{% if %} 태그는 and, or 또는 not을 사용하여 여러 변수를 테스트하거나 주어진 변수를 무효화 합니다.


{% if athlete_list and coach_list %}
<p>Both athletes and coaches are available. </p>
{% endif %}

{% if not athlete_list %}
<p>There are no athletes. </p>
{% endif %}

{% if athlete_list or coach_list %}
<p>There are some athletes or some coaches. </p>
{% endif %}

{% if not athlete_list or coach_list %}
<p>There are no athletes or there are some coaches. </p>
{% endif %}

{% if athlete_list and not coach_list %}
<p>There are some athletes and absolutely no coaches. </p>
{% endif %}

동일한 태그 내에서 and와 or절을 모두 사용할 수 있으며 and 가 or 보다 우선순위가 높습니다.


{% if athlete_list and coach_list or cheerleader_list %}

결과는 아래와 같죠


if (athlete_list and coach_list) or cheerleader_list

하지만 if 문에서 ()의 사용은 잘못된 방법입니다. 즉 다른 연산자를 결합하지 마세요.


만약 우선 순위를 나타 내기 위해서 괄호가 필요한 경우는 중첩된 if 태그를 사용해야 합니다.


조작 순서 제어에 괄호를 사용하는 것은 지원되지 않습니다.


괄호가 필요하다고 생각되면 템플릿 외부에서 먼저 로직을 수행하고 그 결과를 전용 템플릿 변수로 전달하는 방법을 먼저 고려하세요..


또는 아래와 같이 중첩된 태그를 사용하세요.


 {% if athlete_list %}
     {% if coach_list or cheerleader_list %}
         <p>We have athletes, and either coaches or cheerleaders! </p>
     {% endif %}
 {% endif %}

동일한 논리연산자를 여러번 사용하는 것은 좋지만, 다른 연산자를 결합 할 수는 없습니다. 예를 들면 아래와 같습니다.


{% if athlete_list or coach_list or parent_list or teacher_list %}

각 {% if %}와 {% endif %}를 닫아야 합니다. 그렇지 않으면 Django는 TemplateSyntaxError를 던집니다.


* for


{% for %} 태그를 사용하면 시퀀스의 각 항목을 반복할 수 있습니다.


<ul>
    {% for athlete in athlete_list %}
    <li>{{ athlete.name }}</li>
    {% endfor %}
</ul>

반대방향으로 루핑하려면 태그에 반전을 추가하세요


{% for athlete in athlete_list reversed %}
...
{% endfor %}

for태그를 중첩할 수도 있습니다.


{% for athlete in athlete_list %}
<h1>{{ athlete.name }}</h1>
<ul>
    {% for sport in athlete.sports_played %}
    <li>{{ sport }}</li>
    {% endfor %}
</ul>
{% endfor %}

리스트 안의 리스트를 반복해야 하는 경우 각 하위 목록의 값을 개별 변수로 따낼수 있습니다.


예를 들면 컨텍스트에 point라고 불리는 (x,y) 좌표 목록이 있으면 다음을 사용하여 점 목록을 출력 할 수 있습니다.


{% for x, y in points %}
<p>There is a point at {{ x }},{{ y }}</p>
{% endfor %}

딕셔너리의 항목에 엑세스하는 경우에도 유용하게 사용할 수 있습니다.


{% for key, value in data.items %}
{{ key }}: {{ value }}
{% endfor %}

매우 일반적인 패턴은 반복하기 전에 list의 크기를 확인하고 목록이 비어있는 경우에는 특수 텍스트를 출력하는 방법입니다.


{% if athlete_list %}

{% for athlete in athlete_list %}
<p>{{ athlete.name }}</p>
{% endfor %}

{% else %}
<p>There are no athletes. Only computer programmers.</p>
{% endif %}

이 패턴은 매우 일반적이므로 for 태그는 목록이 비어있는 경우 출력할 내용을 정의할수 있는 {% empty %} 절을 지원합니다!.


{% for athlete in athlete_list %}
<p>{{ athlete.name }}</p>
{% empty %}
<p>There are no athletes. Only computer programmers.</p>
{% endfor %}

알아 두어야 할 것은 break 와 continue를 지원하지 않는 다는것을 유념하십시오.

각 {% for %} 루프 내에서 forloop이라는 템플릿 변수에 엑세스 할 수 있습니다.


이 변수는 루프의 진행상황에 대한 정보를 제공하는 몇가지 속성이 있습니다.


forloop.counter는 루프가 돈 count 입니다. 1부터 시작합니다.


 {% for item in todo_list %}

  <p>{{ forloop.counter }}: {{ item }}</p>
  {%  endfor %}

forloop.counter0 는 1이아니고 0부터 시작하는것만 빼고는 위와같습니다.

forloop.revcounter는 항상 루프의 나머지 항목수를 나타냅니다. 마지막루프를 돌때 1입니다.

forloop.revcounter0는 마지막 0입니다. 위와같습니다.

forloop.first는 루프를 처음 실행 할 경우 True입니다. (특수 한경우 처리에 편합니다.)



  {% for object in objects %}

  {% if forloop.first %}<li class="first">{% else %}<li>
      {% endif %}
      {{ object }}
  </li>
  {% endfor %}

forloop.last 동일한데 마지막입니다.


{% for link in links %}
  {{ link }}{% if not forloop.last %} | {% endif %}
{% endfor %}

위의 결과는 아래처럼 나올 수 있습니다.


  Link1 | Link2 | Link3 | Link4

단어사이에 쉼표도 넣을수 있죠


  <p>Favorite places:</p>
      {% for p in places %}{{ p }}{% if not forloop.last %}, {% endif %}
      {% endfor %}

forloop.parentloop 부모 실행에 대한 forloop 객체에 대한 참조입니다.


 {% for country in countries %}
  <table>
      {% for city in country.city_list %}
      <tr>
          <td>Country #{{ forloop.parentloop.counter }}</td>
          <td>City #{{ forloop.counter }}</td>
          <td>{{ city }}</td>
      </tr>
      {% endfor %}
  </table>
  {% endfor %}

forloop 변수는 루프 내에서만 사용할 수 있습니다.

템플릿 파서가 {% endfor %}에 도달하면 forloop를 소멸시켜버립니다.


* ifequal/ifnotequal


<<우선 여기까지만 적기로 했습니다.>>

'Python > Django' 카테고리의 다른 글

[Django] RestFrameWork 튜토리얼 - 2. Request와 Response  (0) 2017.03.10
[Django] RestFrameWork 튜토리얼 - 1. 직렬화  (0) 2017.03.10
[Django] 템플릿  (0) 2017.03.08
[Django] Dynamic URL  (0) 2017.03.08
[Django] VIew와 URL conf  (0) 2017.03.07
Posted by C마노
,

[Django] 템플릿

Python/Django 2017. 3. 8. 17:31

이전 장에서 View에서 텍스트를 반환하는 방법에서 특이한 점이 있었을 것인데,

아래와 같이 HTML이 하드코딩 되었다는 점입니다.


def current_datetime(request):
    now = datetime.datetime.now()
    html = "It is now %s." % now
    return HttpResponse(html)

이 방법은 작동원리를 설명하기에는 정말 편리하겠지만, HTML을 하드코딩 하는 방법은 좋은 방법이 아닙니다.

이유는 아래와 같습니다.


1. 페이지의 디자인을 변경하려면 PYTHON 코드를 변경해야합니다. 사실 HTML과 CSS 등은 파이썬 코드보다 훨씬더 자주 변경됩니다. 그렇기 때문에 파이썬코드와 디자인을 변경하는 편이 훨씬 더 편할 것입니다.


2. 일반적인 웹페이지 템플릿에는 수백에서 수천줄의 HTML과 스크립트가 있습니다.


3. 파이썬 코드를 작성하고 HTML을 디자인 하는것은 각각 다른분야입니다. 결국 나누게 되면 각각의 책임을 별도의 사람들에게 나눠서 분업할 수 있다는 장점이 있습니다.


4. 백단이 Python 코드로 작업할 수 있고 프론트 단이 동시에 템플릿을 작업할 수 있다면 가장 Best 하다고 볼 수 있고, 한 사람이 다른 사람이 파이썬과 HTML을 모두 포함하는 하나의 파일 편집을 끝내기를 기다릴 필요가 없습니다.

즉 분업이 완벽하게 될 수 있다고 볼수 있습니다.


이러한 1~4의 이유로 페이지의 디자인을 파이썬 코드 자체와 분리하는 것이 훨씬 깔끔하고 유지보수가 용이하다고 볼 수있습니다.


우리는 이것을 Django의 템플릿 시스템으로 할 수 있습니다.


우선 Django의 템플릿은 문서의 표시를 데이터와 분리하기 위한 텍스트 문자열입니다.


[Django 템플릿의 예입니다.]


<html> <head> <title>Ordering notice</title> </head> <body> <h1>Ordering notice</h1> <p>Dear {{ person_name }},</p> <p>Thanks for placing an order from {{ company }}. It's scheduled to ship on {{ ship_date|date:"F j, Y" }}.</p> <p>Here are the items you've ordered:</p> <ul> {% for item in item_list %} <li>{{ item }}</li>{% endfor %} </ul> {% if ordered_warranty %} <p>Your warranty information will be included in the packaging.</p> {% else %} <p> You didn't order a warranty, so you're on your own when the products inevitably stop working. </p> {% endif %} <p>Sincerely,<br />{{ company }}</p> </body> </html>

이 템플릿은 기본 HTML이며, 일부 변수와 템플릿 태그가 들어있습니다.


단계별로 살펴보겟습니다.


1. 한 쌍의 중괄호로 묶인 텍스트 ( EX : {{ pserson_name }} ) 는 변수입니다. 

그렇다면 변수값을 어떻게 할당할 것인지에 관한건 조금뒤에 살펴보기로 합시다.

2. 중괄호와 %기호 (EX : {% if ordered_warranty %} )로 둘러싸인 텍스트는 템플릿 태그입니다. 

태그의 정의는 매우 광범위 합니다. 이 태그는 템플릿 체제에 "무언가를 하도록" 지시합니다.

3. 위의 예제 템플릿은 for 태그와 if 태그를 포함합니다.

for 태그는 Python의 for 문과 매우 유사하게 작동하고 if 태그는 예상대로 if 문으로 작동합니다.

4. 마지막으로 위의 예제를 보시면 filter 예가 포함되어있습니다. {{ ship_date|date:"F j, Y" }} 인데요, ship_date 변수를 date 필터에 전달합니다. date 필터는 해당 인수에 지정된 날짜 형식으로 반환합니다.

필터는 Unix 파이프 방식으로 문자 (|)를 사용하려 연결됩니다.


각 Django 템플릿은 여러 내장 태그와 필터에 액세스할 수 있습니다.


[템플릿 시스템 사용하기]


Django 프로젝트는 하나 또는 여러개의 템플릿 엔진으로 구성 될 수 있습니다.

Django는 자체 템플릿 시스템을 위한 Django-Template-Language(DTL)을 제공합니다.

장고 1.8부터는 Jinja2 도 지원하기 시작하였습니다.


다른 백엔드를 선택할 이유가 없다면 DTL을 무조건 사용하십쇼 


Django에서 contrib 에서 django 템플릿을 포합합니다.


django.contrib.admin 처럼 DTL을 사용하시기 바랍니다. 이번 포스트의 모든 예제는 DTL을 사용할 것입니다.


Django의 템플릿 시스템을 파이썬 코드로 사용할 수 있는 가장 기본적인 방법은 아래와 같습니다.


1. raw 템플릿 코드를 문자열로 제공해서 템플릿 객체를 만듭니다.

2. 주어진 변수세트 template.Context({'name': 'Nige'}) 로 Template 객체의 render() 메소드를 호출하세요

이렇게 하면 완전히 렌더링 된 템플릿이 문자열로 반환되며 모든 변수와 템플릿 대그는 Context 에 따라서 Return 이 다르게 됩니다. 


 >>> from django import template
 >>> t = template.Template('My name is {{ name }}.')
 >>> c = template.Context({'name': 'Nige'})
 >>> print (t.render(c))
 My name is Nige.
 >>> c = template.Context({'name': 'Barry'})
 >>> print (t.render(c))
 My name is Barry.

[템플릿 객체 만들기]


Template 객체를 생성하는 가장 쉬운 방법은 직접 인스턴스화 하는 것입니다.

Template 클래스는 django.template 모듈에 존재하고 있고, 생성자는 raw 템플릿 코드인 하나의 인수를 가집니다. (Ex : Template('My name is {{ name }}.') )

파이썬 인터프리터에서 해당 코드를 어떻게 해석하는지 살펴보겠습니다.


이전 포스트에서 만든 mysite 프로젝트 디렉토리에서 python manage.py shell 을 입력해서 인터프리터를 시작합니다.


아래의 코드를 칩니다.


>>> from django.template import Template
>>> t = Template('My name is {{ name }}.')
>>> print (t)

그럼 아마 아래와 같이 결과가 나올겁니다.


<django.template.base.Template object at 0x030396B0>

0x030396B0는 매번 달라지며 관련이 없습니다. Template 객체를 만들면 템플릿 시스템에서 Raw 템플릿 코드를 내부의 최적화 된 형식으로 컴파일 하여 랜더링 할 준비를 합니다.


그러나 템플릿 코드에 구문 오류가 있는 경우 Template() 을 호출하면 TemplateSyntaxError 가 발생합니다.


>>> from django.template import Template
>>> t = Template('{% notatag %}')
Traceback (most recent call last):
File "", line 1, in ?
...
django.template.base.TemplateSyntaxError: Invalid block tag: 'notatag'

TemplateSnytaxErorr는 아래과 같은 경우에 발생합니다.

1. 유효하지 않은 tag

2. 태그는 유효하지만, 잘못된 인수가 있을경우.

3. 유효하지 않은 필터

4. 필터는 유요하지만, 잘못된 인수가 있을경우.

5. 잘못된 템플릿 구문.

6. 닫히지 않은 태그.


[템플릿 랜더링하기]


Template 객체를 얻은 후에는 컨텍스트를 지정하여 데이터를 전달 할 수 있습니다.

컨텍스트는 단순히 템플릿 변수 이름 및 관련 값의 집합입니다.


템플릿은 이 컨텍스트를 변수를 사용하여 템플릿에 변수를 할당하고 태그를 평가합니다.


컨텍스트는 Django 에서 django.template 모듈에 있는 Context 클래스로 표현됩니다.


생성자는 하나의 선택적 인수를 사용하는데, 즉 변수이름을 변수값에 맵핑하는 딕셔너리를 사용합니다.


컨텍스트를 사용해서 Template 객체의 render() 메서드를 호출하여 템플릿을 채웁니다.


>>> from django.template import Context, Template
>>> t = Template('My name is {{ name }}.')
>>> c = Context({'name': 'Stephane'})
>>> t.render(c)
'My name is Stephane.'

* 여기서 잠깐 : 왜 쉘에서 python3 로 실행하면 되지 python manage.py shell로 실행하나요?

=> 가장 큰 차이는 Django shell은 Django 설정파일을 기본적으로 불러오기 때문입니다.

인터프리터를 시작하기 전에 Django에게 어떤 설정파일을 사용할 것인지를 알려줍니다. 템플릿 시스템을 포함한 Django의 많은 부분은 설정에 의존하는 부분이 많고, 프로그램워크가 사용할 설정을 아는 경우가 아니라면 사용할 수 없습니다.


Django shell을 실행하면 Django는 DJANGO_SETTINGS_MODULE이라는 환경변수를 찾습니다.

이는 settings.py의 경로를 말합니다.


예를들면 DJANGO_SETTINGS_MODULE은 아마 mysite.settings 일겁니다.


이제 python manage.py shell 을 실행할때 이 명령은 DJANGO_SETTINGS_MODULE 설정을 처리합니다.


[딕셔너리와 컨텍스트]


파이썬의 딕셭너리는 키와 변수값 사이를 맵핑하는 것인데, Context는 사전과 유사하지만 컨텍스트는 추가 기능을 제공합니다.


>>> from django.template import Template, Context
>>> raw_template = """<p>Dear {{ person_name }},</p>
...
... <p>
    Thanks for placing an order from {{ company }}. It's scheduled to
    ... ship on {{
ship_date|date:"F j, Y"
    }}.
</p>
...
... {% if ordered_warranty %}
... <p>Your warranty information will be included in the packaging.</p>
... {% else %}
... <p>
    You didn't order a warranty, so you're on your own when
    ... the products inevitably stop working.
</p>
... {% endif %}
...
... <p>Sincerely,<br />{{ company }}</p>"""
>>> t = Template(raw_template)
>>> import datetime
>>> c = Context({'person_name': 'John Smith',
...     'company': 'Outdoor Equipment',
...     'ship_date': datetime.date(2015, 7, 2),
...     'ordered_warranty': False})
>>> t.render(c)
"<p>Dear John Smith,</p>\n\n<p>Thanks for placing an order from Outdoor Equipment. It\
's scheduled to\nship on July 2,2015.</p>\n\n\n<p>You didn't order a warranty, so you\
're on your own when\nthe products inevitably stop working.</p>\n\n\n<p>Sincerely,<br\
 />Outdoor Equipment</p>"

1. 먼저 django.template 모듈에 있는 Template과 Context 클래스를 가져옵니다.

2. 템플릿의 raw 텍스트를 raw_template 변수에 저장합니다.

3. 여러 줄에 걸쳐있기 때문에 문자열을 지정하기 위해서 """를 사용합니다.

4. raw_template을 Template 클래스 생성자에 전달해서 템플릿 객체 t를 만듭니다.

5. 파이썬 표준 라이브러리에서 datetime 모듈을 가지고 옵니다.

6. 그런 다음 Context 객체를 만듭니다. Context 생성자는 변수이름을 값에 맵핑하는 딕셔너리를 사용합니다.

7. 마지막으로 템플릿 객체에서 render() 메소드를 호출하여 컨텍스트를 전달합니다.

이것은 랜더링 되어진 템플릿을 반환합니다. 즉 템플릿 변수를 실제 변수값으로 바꾸고 템플릿 태그를 실행한 값을 반환합니다. ( 랜더링 되어진 템플릿은 필터까지 같이 처리된 결과가 리턴됩니다. )


[같은 템플릿에 여러가지의 콘텍스트]


Template 객체를 얻으면 이를 통해 여러가지 컨텍스트를 랜더링 가능합니다.


>>> from django.template import Template, Context
>>> t = Template('Hello, {{ name }}')
>>> print (t.render(Context({'name': 'John'})))
Hello, John
>>> print (t.render(Context({'name': 'Julie'})))
Hello, Julie
>>> print (t.render(Context({'name': 'Pat'})))
Hello, Pat

이와 같은 여러 콘텍스트를 랜더링 하기 위해서 동일한 템플릿 소스를 사용할 때마다 Template 객체를 한번 생성한 다음에 render ()를 여러번 호출 하는 것이 훨씬 효율적입니다.

 # Bad

for name in ('John', 'Julie', 'Pat'):
t = Template('Hello, {{ name }}')
print (t.render(Context({'name': name})))

# Good
t = Template('Hello, {{ name }}')
for name in ('John', 'Julie', 'Pat'):
print (t.render(Context({'name': name})))

Django의 템플릿 파싱은 꽤 빠른편이고, 내부적으로는 정규식 기반으로 작동합니다. 이것은 XML 기반 템플릿 엔진과 완전히 대조됩니다. XML파서의 오버헤드도 없기 때문에 일반적으로 XML기반 템플릿 엔진보다 훨씬 빠릅니다.


[ 콘텍스트 .검색 ] 


지금까지의 예제는 컨텍스트에서 간단한 값을 전달하였습니다. 대부분 문자열과 datetime 예제였습니다. 그러나 템플릿 시스템은 리스트, 딕셔너리 및 사용자정의 객체와 같이 보다 복잡한 데이터 구조를 효율적으로 처리합니다. Django 템플릿에서 복잡한 데이터 구조를 탐색하는 핵심은 dot(.) 입니다.


.을 사용해서 딕셔너리 키, 속성, 메소드 또는 index에 액세스 하세요.

이것은 몇가지 예를 통해 설명해드리겠습니다.


예를들어, 파이썬 딕셔너리를 템플릿에 전달한다고 가정하볼게요.


해당 딕셔너리의 값에 딕셔너리 키로 액세스 하려면 점을 사용하십시오.


>>> from django.template import Template, Context
>>> person = {'name': 'Sally', 'age': '43'}
>>> t = Template('{{ person.name }} is {{
person.age
}} years old.')
>>> c = Context({'person': person})
>>> t.render(c)
'Sally is 43 years old.'

비슷하게 객체 속성에 대한 엑세스도 마찬가지입니다.


예를 들어, 파이썬 datetime.date 객체는 year, month, day 속성을 가지고 있으며, .을 사용하여 장고 템플릿의 속성에 엑세스할수 있습니다.


>>> from django.template import Template, Context
>>> import datetime
>>> d = datetime.date(1993, 5, 2)
>>> d.year
1993
>>> d.month
5
>>> d.day
2
>>> t = Template('The month is {{ date.month }}
and the year is {{ date.year }}.')
>>> c = Context({'date': d})
>>> t.render(c)
'The month is 5 and the year is 1993.'

아래의 예제에서는 사용자 정의 클래스를 사용해서 dot(.) 이 임의의 객체에 대한 속성 액세스도 허용한다는 것을 보여줍니다.


>>> from django.template import Template, Context
>>> class Person(object):
...     def __init__(self, first_name, last_name):
...         self.first_name, self.last_name =
first_name, last_name
>>> t = Template('Hello, {{ person.first_name }} {{ person.last_name }}.')
>>> c = Context({'person': Person('John', 'Smith')})
>>> t.render(c)
'Hello, John Smith.'

.은 객체의 메서드를 참조할 수도 있습니다. 예를 들어, 각각의 파이썬 문자열은 upper() 와 isdigit() 메서드를 가지고 있으며, 같은 닷 구문을 사용하여 Django 템플릿에서 호출 가능합니다.


>>> from django.template import Template, Context
>>> t = Template('{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}')
>>> t.render(Context({'var': 'hello'}))
'hello -- HELLO -- False'
>>> t.render(Context({'var': '123'}))
'123 -- 123 -- True'

파이썬과 다르게 메서드 호출에 괄포를 포함하지 않는것을 보세요. 또한 메서드에 매개변수를 전달할 수도 없습니다. 매개 변수가 필요없는 메서드만 호출 가능합니다. 마지막으로 점들도 리스트에 엑세스 하는데 사용됩니다 예를 들면 아래와 같습니다.


>>> from django.template import Template, Context
>>> t = Template('Item 2 is {{ items.2 }}.')
>>> c = Context({'items': ['apples', 'bananas',
'carrots']})
>>> t.render(c)
'Item 2 is carrots.'

- 연산자를 가진 색인은 허용되지 않습니다. 예를들어 템플릿 변수 {{items.-1}} 은 TemplateSyntaxError를 발생시킵니다.


템플릿 . 조회를 요약하자면 템플릿 시스템이 변수 이름에 점을 발견하면 다음 순서로 조회합니다.


1. Dictinary 조회

2. 속성 조회

3. 메서드 콜링.

4. 리스트 인덱스 조회


시스템은 동작하는 첫번째 조회 유형을 사용합니다. 도트 조회는 여러 단계로 중첩도 가능합니다. 예를 들어 다음예제는 {{person.name.upper}}를 사용합니다.


딕셔너리 조회 (person['name'])과 메소드 호출 (upper())로 변환됩니다.


>>> from django.template import Template, Context
>>> person = {'name': 'Sally', 'age': '43'}
>>> t = Template('{{ person.name.upper }} is {{
person.age
}} years old.')
>>> c = Context({'person': person})
>>> t.render(c)
'SALLY is 43 years old.'

[메소드 호출 방법]


메소드 호출은 다른 조회 유형보다 조금 더 복잡합니다. 다음은 유의사항입니다. 메소드 조회중에 메서드가 예외를 발생시키면 silent_variable_failure 값이 Ture이면 변수는 엔진의 string_if_invalid ( 기본적으로 빈 문자열 )으로 렌더링 됩니다.


아래의 예제를 보세요.


  >>>
  t = Template("My name is {{ person.first_name }}.")
  >>> class PersonClass3:
  ...     def first_name(self):
  ...         raise AssertionError("foo")
  >>> p = PersonClass3()
  >>> t.render(Context({"person": p}))
  Traceback (most recent call last):
  ...
  AssertionError: foo

  >>> class SilentAssertionError(Exception):
  ...     silent_variable_failure = True
  >>> class PersonClass4:
  ...     def first_name(self):
  ...         raise SilentAssertionError
  >>> p = PersonClass4()
  >>> t.render(Context({"person": p}))
  'My name is .'

* 메서드 호출은 메서드가 매개변수가 필요없는 경우에만 작동합니다. 그렇지 않으면 시스템은 다음 조회유형으로 (리스트) 이동합니다.


* Django에서는 템플릿에서 사용할 수 있는 처리량을 의도적으로 제한하기 때문에 템플릿 내에서 엑세스 되는 메서드 호출에 매개변수를 전달 할 수 없는 것입니다.


[ 유효하지 않은 변수를 처리하는 방법 ]


일반적으로 변수가 없으면 템플릿 시스템은 엔진의 string_if_invalid 구성 옵션값을 삽입합니다 (기본적으로 빈 문자열입니다.)


>>> from django.template import Template, Context
>>> t = Template('Your name is {{ name }}.')
>>> t.render(Context())
'Your name is .'
>>> t.render(Context({'var': 'hello'}))
'Your name is .'
>>> t.render(Context({'NAME': 'hello'}))
'Your name is .'
>>> t.render(Context({'Name': 'hello'}))
'Your name is .'

이러한 방법은 사람의 Operation 에따라 오류가 해결될 수 있기 때문에 예외를 발생시키는 것보다 효율적인 방법입니다.

이러한 경우 변수의 대소문자나 이름이 잘못되어서 모든 조회가 실패하였습니다.

현실적으로는 작은 템플릿 구문 오류 ( 대소문자, 이름실수 등 )으로 인해 웹사이트에 엑세스 할수 없게되는것은 굉장히 비효율 적입니다.



'Python > Django' 카테고리의 다른 글

[Django] RestFrameWork 튜토리얼 - 1. 직렬화  (0) 2017.03.10
[Django] 템플릿 태그와 필터  (0) 2017.03.08
[Django] Dynamic URL  (0) 2017.03.08
[Django] VIew와 URL conf  (0) 2017.03.07
[Django] urls.py 특징.  (0) 2017.03.07
Posted by C마노
,