2010년 6월 7일 월요일

03 - 3) Select - 검색

앞서 Model을 이용하여 자료 구조(Table)를 작성하고 자료의 입력, 수정, 삭제에 대해 알아보았습니다.

수 정의 경우 단일 행에 대해서만 알아보았는데요 지금 설명드릴 검색 방법에 대해 알게되면 삭제에서 사용했던 .delete() 메소드와 마찬가지로 .update() 메소드를 이용하여 자료를 수정할 수 있습니다.

방법은 삭제와 동일하였으나 따로 분리드려 설명을 드리는 것은 수정의 경우 특정 조건을 만족하는 복수개의 행에 대해 적용하는 경우가 종종 있어 특정 조건을 검색하는 방법에 대한 이해가 필요하기 때문입니다.

자 그럼 지금부터 django Model에서 검색 방법에 대해 알아 보도록 하겠습니다.

 

먼저 검색의 결과로 반환되는 자료의 종류는 앞서 말씀드렸듯이 QuerySet 이라 불리우는 결과집합과 단일 행의 두가지가 있습니다.

이 사실을 잘 숙지하시기 바랍니다.

 

앞서 UPDATE, DELETE 에서 전체 자료를 불러오는 .objects.all()과 단일행 결과를 반환하는 .objects.get()에 대해 알아보았습니다.

여기서 objects는 django model manager라 불리우며  django Model이 Database에 수행한 Query를 통해 생성된 객체를 의미합니다.

앞서 사용한 Department.objects 는 생성한 Department Table의 모든 객체(즉, 모든 자료)를 가리킵니다.

이런 manager 는 필요에 따라 사용자가 생성하여 사용할 수도 있습니다.

 

앞서 Department.objects.get(dName='Infomation and Statistics')의 결과물이 <Department: Department object>와 같이 나왔던 것을 기억할 것입니다.

objects 는 객체라고 말씀을 드렸는데 .objects.get()이나 .objects.all()은 모두 이 객체를 나타내는 것으로 객체를 화면상에 나타내다 보니 이런 식으로 결과물이 나왔습니다.

이제 객체임을 나타내지 말고 실제 가지고 있는 값을 나타내 보도록 합니다.

실제 값을 나타내기 위해서는 Table을 정의한 models.py의 각 Class에 다음과 같이 __UNICODE__(self)라는 멤버 변수를 작성하여주면 됩니다.

 

  1. models.py
  2. from django.db import models
  3.  
  4. class Department(models.Model):
  5. dName = models.CharField(max_length=40)
  6. def __unicode__(self) :
  7. return self.dName
  8.  
  9. class Student(models.Model):
  10. sID = models.CharField(max_length=8)
  11. sName = models.CharField(max_length=50)
  12. sDept = models.ForeignKey(Department)

    def __unicode__(self) :

       reutrn u"%s, %s, %s, %s" % (self.id, self.sID, self.sName, self.sDept)

  13.  
  14. class Subject(models.Model):
  15. sName = models.CharField(max_length=50)
  16. sStudent = models.ManyToManyField(Student)

    def __unicode__(self) :

       return u"%s, %s, %s" % (self.id, self.sName, self.sStudent)

  17.  

 

__unicode__() 메소드는 Python에게 객체를 UNICODE 로 어떻게 표현할지 알려주는 메소드입니다.

위의 예에서는 일부의 값들만을 출력하도록 하였지만 제작자의 필요에 따라 필요한 정보만 출력하게 할 수 있습니다.

만일 Projection 연 산을 하여 그때 그때 원하는 열들의 값을 확인하기 위해서는 Database에 접근하여 확인하셔야 합니다.

 

__unicode__() 메소드를 적용한 후 검색의 결과를 살펴보겠습니다.

 

>>> Department.objects.all()
[<Department: Infomation and Statistics>, <Department: Computer Science>]

 

결과물이 __unicode__() 메소를 적용하고 나니 출력을 원하는 열의 값이 출력되어 한결 보기 편해 졌습니다.

 

이제 검색에 대해 알아보도록 하겠습니다.

 

 

조건 검색 - .objects.filter()

 

앞서 .objects.get() 메소드에서 열의 값을 검색 조건으로 사용했던 것을 기억할 것입니다.

.objects.get() 은 검색의 결과가 단일 행이었고 이제 QuerySet을 반환하는 조건 검색을 실시해 보도록 하겠습니다.

사용되는 메소드는 .filter() 입니다.

다음의 예를 보도록 하겠습니다.

 

 >>> Department.objects.get(dName='Computer Science')

 [<Department: Computer Science>]

>>> Department.objects.filter(dName='Computer Science')

[<Department: Computer Science>]

 

.objects.filter()나 .objects.get() 모두 똑같은 결과를 반환합니다만 .objects.filter()는 결과의 형태가 QuerySet이란 점을 잘 기억해 주시기 바랍니다.

즉, 1 개 이상의 결과를 가져올 수 있는 반면 .objects.get()은 1개의 결과만을 가져올 수 있습니다.

그리고 .objects.get()의 경우 결과값이 없으면 DoesNotExist 예외를 발생시키지만 .objects.filter()의 경우에는 결과값이 없을 경우 빈값을 반환합니다.

 

검색 조건을 추가로 넣기 위해서는 () 안에 검색에 사용될 열과 값을 튜플로 구성하면 됩니다. 즉,

 

 >>> Department.objects.filter(id=2, dName='Computer Science')
[<Department: Computer Science>]

 

 와 같이 사용할 수 있으며 각 조건은 AND로 결합됩니다.(이 부분은 .objects.get()도 동일합니다.)

 .objects.filter() 나 .objects.get()에서 사용된 =(equal)은 정확한 값을 검색하는데  반해 대소비교(SQL의 연산자)나 패턴(SQL문의 LIKE)등으로 검색할 경우도 있습니다.

예를 들어 값의 시작이 "Computer"로  시작하는 자료를 검색한다든지 하는 경우처럼요...

이를 위해 django 에서는 magic 키워드를 제공하는데 다음 표와 같습니다.

 키워드 설 명 사용예

 __lt / __gt

__lte / __gte

보 다 작다 / 보다 크다

같거나 보다 작다 / 같거나 보다 크다

id가 1보다 큰 자료 검색

>>> Department.objects.filter(id__gt=1)

[<Department: Computer Science>]

 __in 주어진 리스트 안에 존재하는 자료 검색

id 가 2, 3, 5 인 자료 검색

 >>> Department.objects.filter(id__in=[2, 3, 5])
[<Department: Computer Science>]

 __year / __month / __day 해당 년도, 월, 일 자료 검색 >>>Entry.objects.filter(pub_date__year=2005)
__isnull 해 당 열의 값이 null 인 자료  검색

>> Department.objects.filter(dName__isnull=True)

[]

 __contains / __icontains

해당 열의 값이 지정한 문자열을 포함하는 자료 검색

(__icontains 는 대소문자를 구별하지 않음)

>>> Department.objects.filter(dName__contains='puter')

[<Department: Computer Science>]

 __startswith / __istartswith

해당 열의 값이 지정한 문자열로 시작하는 자료 검색

(__istartswith 는 대소문자를 구별하지 않음)

>>> Department.objects.filter(dName__startswith='Com')
[<Department: Computer Science>]
 __endswith / __iendswith

해당 열의 값이 지정한 문자열로 끝나는 자료 검색

(__iendswith 는 대소문자를 구별하지 않음)

>>> Department.objects.filter(dName__contains='nce')
[<Department: Computer Science>]
 __range

문 자, 숫자, 날짜의 범위를 지정함

(SQL의 BETWEEN에

>>> Department.objects.filter(id__range=(2, 10))

 

 

 

자료의 정렬 - .objects.order_by()

 

자료의 정렬은 .objects.order_by() 메소드를 이용합니다.

전달되는 전달이자는 열의 이름을 전달하면 됩니다.

기 본적으로 오름차순으로 정렬하며 내림차순으로 정렬하려면 열 이름 앞에 "-"(minus)를 붙히면 됩니다.

또한 .objects.order_bay()  안에 여러 열을 나열하여 정렬 순서를 결정할 수 있습니다.

다음의 예를 자세히 살펴보시기 바랍니다.

주의할 점은 열 이름 앞뒤에 따옴표(큰 따옴표도 괜찮습니다.)를 붙힌다는 것입니다.

 

- 이름의 오름 차순으로 정렬

>>> Student.objects.order_by('sName')
[<Student: 20100103, Ddochi, Computer Science>,

 <Student: 20100102, Donor, Computer Science>,

 <Student: 20100101, Dooly, Infomation and Statistics>]

 

- 이름의 내림차순으로 정렬
>>> Student.objects.order_by('-sName')
[<Student: 20100101, Dooly, Infomation and Statistics>,

  <Student: 20100102, Donor, Computer Science>,

 <Student: 20100103, Ddochi, Computer Science>]

 

- 1차 정렬 조건은 sName이고 sName이 동일할 경우 sID의 오름차순으로 정렬

>>> Student.objects.order_by('sName', 'sID')
[<Student: 20100103, Ddochi, Computer Science>,

 <Student: 20100102, Donor, Computer Science>,

 <Student: 20100101, Dooly, Infomation and Statistics>]

 

 

앞선 .objects.filter() 메소드 뒤에  다음과 같이 .order_by()를 붙혀 사용할 수 있습니다.

(이 기능은 .objects의 모든 메소드를 같이 연결할 수 있는 chaining 기능입니다.)

 

>>> Student.objects.filter(sDept=Department.objects.get(id=2)).order_by('sName')

[<Student: 20100103, Ddochi, Computer Science>,

  <Student: 20100102, Donor, Computer Science>]

 

 

 

특정행의 선택 - Slicing Data

 

Python의 Slicing Data를 이해한다면 쉽게 이해할 수 있는 기능으로 SQL의 LIMIT ... OFFSET 기능을 의미합니다.

선택된 결과에서 특정 행의 자료를 가져오는 방법을 예제를 통해 알아보도록 하겠습니다.

대괄호 안에 넣은 인덱스에 주의하시면서 자료를 보시기 바랍니다.

 

선택된 결과중 첫번째 행의 자료 가져오기 (본 예는 결과적으로 학번이 가장 빠른 사람을 가져옵니다.)

>>> Student.objects.order_by('sID')[0]
<Student: 20100101, Dooly, Infomation and Statistics>

 

선택된 결과중 처음 두개의 자료 가져오기

>>> Student.objects.order_by('sID')[0:2]
[<Student: 20100101, Dooly, Infomation and Statistics>,

  <Student: 20100102, Donor, Computer Science>]

 

선택된 결과중 두번째 이후 자료가져오기

>>> Student.objects.order_by('sID')[1:]
[<Student: 20100102, Donor, Computer Science>,

  <Student: 20100103, Ddochi, Computer Science>]

 

선택된 결과중 두번째부터 세번째 자료까지 가져오기

 >>> Student.objects.order_by('sID')[1:3]
[<Student: 20100102, Donor, Computer Science>,

  <Student: 20100103, Ddochi, Computer Science>]

 

인 덱스를 보시면 아시겠지만 시작 인덱스는 0번 부터 시작을 하고 콜론(:) 뒤에 나오는 범위 인덱스는 해당 인덱스보다 작은 인덱스까지를 의미합니다.

즉, [1:3]이라고 하면 두번째 자료(인덱스 1)부터 3보다 작은 인덱스인 인덱스 2까지의 자료를 뜻합니다.

Model의 Slicing에서는 음수 인덱스는 사용하지 않습니다.

 

 

선택된 자료의 변경

 

QuerySet 자료의 변경을 의미하는 것으로 사용법은 .objects.all().delete() 와 비슷하게 사용됩니다.

현재 우리의 예에서 학과의 ID가 2인 학생들의 학과 ID 를 1로 변경해 보도록 하겠습니다.


>>> Student.objects.filter(sDept=2).update(sDept=Department.objects.get(id=1).id)
2
>>> Student.objects.all()
[<Student: 20100101, Dooly, Infomation and Statistics>,

  <Student: 20100102, Donor, Infomation and Statistics>,

<Student: 20100103, Ddochi, Infomation and Statistics>]

 

이 글은 스프링노트에서 작성되었습니다.

댓글 1개:

  1. 알짜배기 정보 잘 보고 갑니다. 감사합니다. ^^*

    답글삭제