본문 바로가기
개발일기/SCM

scm(supply chain management) 개발기 -2

by 유주원 2014. 9. 19.

Django에서의 PUT 매소드 사용.


restful api의 규칙을 명확히 따르기 위하여, 데이터 등록 과정은 POST, 데이터 수정 과정은 PUT, 데이터 삭제 과정은 DELETE 매소드를 사용하기로 하였다.

그런데 이게 왠일... 

Django에서는 PUT과 OPTIONS, DELETE에 대해서 파라미터 값을 따로 저장하지 않는다.

(지원하지 않는다고 보는게 더 정확함)

하지만 해당 프로토콜 타입이 POST인지 PUT인지 DELETE인지는 명확하게 식별할 수 있으며, 해당 값만 request폼에 저장되어 있지 않는거라 우회하는 방법으로 데이터를 가져올 수 있다. (body 데이터는 가지고 있다는 소리임)


from django.http import QueryDict


def put_test(request):

    if request.method == 'PUT':

         put = QueryDict(request.body)

         name = put['name']

         id = put['id']

         ....        


위와 같은 방식으로 우회하면 PUT 매소드 데이터를 가져올 수 있다.


model.objects.get과 model.objects.filter의 차이


model.objects.get은 오직 하나의 데이터만 가져온다. 없어서는 안된다.

해서 get을 쓸 경우에는 예외처리를 해주어야 한다.


try:

   model = Model.objects.get(id=1)

except Model.DoesNotExist:

   print "해당 데이터가 존재하지 않습니다."  


model.objects.filter는 다수의 데이터를 가져올 수 있다. 없어도 무방하다.


models = Model.objects.filter(name__contains='A')

if models.count() == 0:

    print "해당 데이터가 존재하지 않습니다."



Group by와 Sum


Django에서의 groupby와 Sum은 어떻게 해야 할까..

만약 아래와 같은 테이블이 있다면..(table 명은 test)


 id

name 

apply

 1

A

1

 2

A

3

 3

B

4


Sum은 아래 코드와 같다. 

test란 테이블의 apply 필드를 모두 더하는데 더한 값이 없을 시에는 0으로 대체한다는 내용의 코드이다.


from django.db.models import Sum

sum = test.aggregate(Sum('apply'))['apply__sum'] or 0


Groupby는 아래와 같으며 name을 기준으로 apply필드의 값을 합쳐라이다. 

Sum이외에도 Count라는 함수는 해당 필드의 개수를 나타낼 때 사용된다.


from django.db.models import Sum,Count

test.values('name').annotate(Sum('apply'))

test.values('name').annotate(count=Count('name'))


Django paginator


Django에서는 pagination 기능도 제공한다.

사용법은 아래와 같다.


from django.core.paginator import Paginator


objs = Models.objects.all()

pages = Paginator(objs, 20)            // 20개 단위로 잘라라


pages_obj = pages.page(1).object_list            // 1페이지에 있는 20개 목록을 가져와라.


위의 코드를 보면, '어 저게 무슨 pagination이야. 모든 object 다 가져와서 자르면 비효율적이지 않나?'

이렇게 생각이 들 것이다.

(나 역시 그렇게 생각해 왔음..)

그런데 여기서 주시해야할 것이... Models.objects.all()을 통해 가져오는 objs는 일반적인 list가 아니라 QuerySet 타입의 데이터이다. 


QuerySet은 결과 데이터를 모두 메모리에 올려놓는 형태가 아니라, 그 상태 정보만을 가지고 있다.

lisp의 lazy evaluation과 동일한 개념인 것 같은데.. 다음 번에 계산될 것은 염두에 두고 다음 번 계산될 수 있는 프로시저만 메모리에 저장되어 있는 형태..라고 생각하면 더 어려울라나...

어쨌든 결과적으로는 QuerySet은 결과 데이터를 모두 메모리에 올려놓은 상태는 아니라는 점.

(DB Curser Index만 가지고 있다고 봐도 무방할런지... 아시는 분은 답변 부탁드려요 ㅠㅠ)

그리고 list()나 len() 함수들을 QuerySet에 적용시키면, ex) list(objs.objects.all()) 이 때에는 모든 메모리에 올라가게 된다.

len()을 통해 QuerySet 개수를 세는 것보다는 count() 함수를 통해 길이를 체크하는게 더 효율적이다.


그래서 결과적으로는 위의 pagination 코드가 효율적이라는 얘기다. (아 설명을 잘 못하겠음 ㅠ_ㅠ)


Serialize


해당 scm 프로젝트를 진행함에 있어 Django View단에서 api마다 각기 다른 serialize 결과 값을 보내줘야 해서,

FieldName을 매개변수로 받으면 해당 값을 serialize해주는 함수를 따로 만들었다.

물론 rest framework에서 serialize 기능을 제공하긴 하는데, 이것 역시 여러 개의 serialize class를 만들어서 제공해야 하는 것은 비슷한 것 같길래, 그냥 손쉽게 만들었다. 

(사실 framework 공부해야 한다는 귀차니즘 때문에... 조만간 바꿔야지요 ㅠ)


models.py

class Test(models.Model):

    id = models.IntegerField(primary_key=True)

    name = models.CharField(max_length=30, blank=True)

    company = models.CharField(max_length=30, blank=True)


     def serialize(self, fieldList):

          data = {}

          for f in fieldList:

              for m in self._meta.fields:

                    if f == m.name:

                         data[f] = getattr(self, m.name)

                         break

          return data


view.py

def test():

    sublist = []

    fieldList = ['id', 'name', 'company']

    objs = Test.objects.all()

    for i in objs:

       temp = i.serialize(fieldList)

       sublist.append(temp)  


last id 가져오기


가장 최근에 저장한 id 값을 가져오는 방법


objs = Test.objects.lastest('id')


orderby


objs = Test.objects.filter(name__contain = "A").order_by('id')


get or create


해당 모델에 데이터를 저장할 시, 기존에 존재하는 데이터라면 저장이 안되어야 하는게 맞다.

하지만 일일이 exception 처리해서 skip하기가 너무나 귀찮다.

이럴 때 get_or_create 매소드를 사용한다.

데이터가 있으면 get, 존재하지 않으면 create를 진행한다.


objs, created = Test.objects.get_or_create(id = 1)

if created == False:

    print "생성 실패"


created 변수를 통해 데이터가 생성되었는지 여부를 알 수 있다.


round


scm 중에 float 계산을 해야 하는 부분이 있어서 round 함수 사용.

round 함수는 해당 값을 반올림 해주는 함수이다.


rate = round( 100 / 5, 2)


뒤에 2는 소수점 둘째 자리까지 표시해주겠다는 표현이다.


transaction


Django에서의 transaction 방법은???

쉬워도 너무 쉽다...

너무 쉬워서 의심이 들 정도...


from django.db import transaction


@transaction.atomic

def test():

...


함수 위에 decoration 패턴으로 transaction.atomic만 표현해 주면 된다.

기존에는 @transaction.commit_on_success 를 썼었는데, 해당 함수는 Django 1.6버전부터 deprecated되었고 1.8에서는 사라질 예정이다.

transaction에 대해 좀 더 자세히 쓰고 싶지만.. 너무 귀찮네...

해당 홈페이지를 참조하시면 될 듯 합니다.

Django 홈페이지 가기