ORM

Nam Eun-Ji·2020년 11월 27일
1

쿼리셋 QuerySet

전달받은 모델의 객체 목록이다.
쿼리셋은 데이터베이스로부터 데이터를 읽고, 필터를 걸거나 정렬을 할 수 있다.

Object-relatinal mapping

객체(클래스)와 관계(관계형 데이터 베이스)와의 설정을 의미한다.
객체 지향 프로그래밍은 클래스를 사용하고 관계형 데이터 베이스는 테이블을 사용하는데 여기서 객체 모델과 관계형 모델간에 불일치가 존재한다. 이 객체간의 관계를 바탕으로 SQL을 자동 생성하여 불일치를 해결 하는 것이 ORM이다.
Object <= 매핑 => DB데이터 에서 매핑의 역할을 하는 것이 ORM이라 할 수 있다.



# models.py
class User(models.Model):
    name = models.CharField(max_length=50)
    password = models.CharField(max_length=300)
    city = models.CharField(max_length = 50)
    created_at = models.DateTimeField(auto_now_add=True)
    
# views.py
class UserView(View):
    def post(self, request)
        data = json.loads(request.body)
        User(
            name = data["name"],
            password = data["password"]
        ).save()

조회

all()

  • 모든 데이터를 가져옴
  • 쿼리셋 형태로 가져옴 [<Name: 이름>, <Name: 이름>]
data = User.objects.all()
print(data)
# <QuerySet [<Users: Users object (1)>, <Users: Users object (2)>]>

# 사용할 땐 data[0]["name"]

get()

  • 하나의 row를 반환하며, 해당되는 데이터가 오직 1개여야만 함.
  • 만약 0개이거나 2개이상일 경우 에러 발생, 0개일 때 DoesNotExist, 2개 이상일 때 MultipleObjectsReturned 발생
  • 쿼리셋 형태가 아닌 파이썬 dictionary 형태로 반환, 때문에 dot notation으로 접근 가능
data = User.objects.get(name="Tim")

try:
   return User.objects.get(id=1)
execpt User.DoesNotExist:
   message = "유저가 존재하지 않습니다"
execpt User.MultipleObjectsReturned:
   message = "여러개의 객체가 반환되었습니다."

filter()

  • 특정 조건에 부합하는 데이터들을 가져오며, 쉼표를 이용해 조건 추가 가능
  • or로 묶기 위해서는 Q를 사용해야함.
  • 쿼리셋 형태로 가져옴 [<Name: 이름>, <Name: 이름>]
from django.db.models import Q     # or을 사용하기 위해서는 Q를 import해줘야함.

User.objects.filter(name='John')
User.objects.filter(name='John', city="seoul")           # and
User.objects.filter(Q(city="seoul") | Q(city="jeju"))    # or

exclude()

  • 특정 조건에 부합하지 않는 데이터들을 가져오며, 쉼표를 이용해 조건 추가 가능
  • AND : 괄호 안에 쉼표를 이용해 조건 추가 가능
  • OR : 괄호 밖에 새로 .exclude()를 지정
  • 쿼리셋 형태로 가져옴 [<Name: 이름>, <Name: 이름>]
User.objects.exclude(city="seoul", email__endswith="naver.com")   # and
User.objects.exclude(city="seoul").exclude(city="jeju")

first() / last()

  • first() : 첫번째 인자를 가져옴
  • last() : 마지막 인자를 가져옴
  • 조건에 맞는 데이터가 없더라도, DoesNotExist 예외가 발생하지 않고 None을 반환
  • 결과물을 dictionary형태로 보여줌
User.objects.first()
User.objects.last()

values()

  • 원하는 속성값만을 가지고 오고 싶을 때는 매개변수로 속성값을 넣어 가져옴
  • 결과물을 dictionary형태로 보여줌

values_list()

  • dict형태가 아닌 tuple형태의 리스트로 가져옴
  • flat을 사용하면 tuple형태가 아닌 list형태로 가져올 수 있음
User.objects.values_list('name', 'city')
# <QuerySet [('cindy', 'seoul'), ('Tim', 'jeju')]>

User.objects.values_list('name')
# <QuerySet ['cindy', 'Tim']>

distinct()

  • 필드이름이 같은 것이 있다면 겹치지 않게 하나만 표시
  • 쿼리가 여러 테이블에 걸쳐있는 경우 쿼리셋을 평가할 때 중복결과를 얻을 수 있음. 이때 주로 사용.
User.objects.filter(city="seoul").distinct()



정렬

order_by()

  • 정렬의 순서를 변경할 수 있음
  • ,를 사용하여 여러 조건 기입 가능
  • 마이너스(-)를 붙이면 내림차순 정렬
  • filter(), order_by()를 values()와 같이 쓰면 어느 순서든 같은 결과를 반환한다.
User.objects.order_by('-created_at')    # 최신순
User.objects.order_by('name')           # 이름순

모델클래스에 Meta 속성으로 ordering 지정

# models.py
class User(models.Model):
    name = models.CharField(max_length=50)
    password = models.CharField(max_length=300)
    city = models.CharField(max_length = 50)
    created_at = models.DateTimeField(auto_now_add=True)
    
    class Meta:
        ordering = ['id']      // 역순 시 [-id]



수정

데이터를 불러와 속성을 변경한 후 save함수를 호출하여 저장

  • 모델 인스탄스별로 db에 업데이트를 요청하기 때문에 성능이 저하됨
data = User.objects.get(id=1)
print(data.name)     # ‘cindy’

data.name = ‘jain’
data.save()          # save를 호출해야 실제로 저장됨

data = User.objects.all()
for user in data:
    user.city = 'korea'
    user.save()      # 각 Model Instance 별로 DB에 update 요청 - 성능저하

update()

하나의 sql로 작동하므로, 동작이 빠름

queryset = User.objects.all()
queryset.update(city='korea')  # 일괄 update 요청



생성

create()

User.objects.create(name='Tim', password='#$%SF@', city='seoul')
# 기존에 있던 객체를 토대로 내용 넣기
city = User.objects.get(id=1)
User.objects.create(name='Tim', password='dfivk1', city=city)

save()

Users.objects.all().count() # 10
Users(nickname='jin', password='123df', email='jin@naver.com').save()
Users.objects.all().count() # 11

a = Users()
a.nickname = 'rm'
a.password = 'tjdnf'
a.email = 'namjoon@naver.com'
a.save()

Users.objects.all().count() # 12



삭제

delete()

  • SQL DELETE를 수행하고, 삭제된 객체의 수와 객체 유형당 삭제된 dict를 반환
  • Django의 ForeignKey는 기본적으로 SQL의 ON DELETE CASCADE를 모방한다. 즉, 삭제 될 객체를 가리키는 외래키가 있는 객체도 함께(CASCADE) 삭제된다.
  • 반환 : (삭제된 객체 수, {pk가 걸려 있는 모델: 삭제된 객체수, ...})
Users.objects.filter(name='Tim').delete()
(1, {'user.Users': 1})
User.objects.exclude(email__endswith="naver.com").delete()
(16, {'product.Orders': 0, 'product.ProductReviews': 2, 'product.ProductAnswers': 1, 'product.UserLikeProducts': 3, 'product.ProductQuestions': 4, 'user.User': 6})



그 외

count()

  • 데이터의 갯수(row의 수)를 세기 위해 사용
  • count()SELECT COUNT(*)를 수행하므로, 모든 레코드를 파이썬 객체에 로드하고 len()를 호출하는 것보다는 count()를 사용하는게 더 빠르다.(객체를 메모리에 로드하지 않을 때는 len()이 더 빠르다.)
User.objects.filter(city='seoul').count()

exists()

최소한 하나의 row가 존재하는지의 여부를 true/false로 반환

check = User.objects.filter(name='John')
check.exists()      # True



Field Lookup

문자열

필드명__조건 = 조건값

# ex
User.objects.filter(name__icontains = 'Jo')
조건내용
exact / iexact정확히 같은 데이터를 탐색 / +대소문자 무시
contains / icontains지정한 문자열을 포함하는 데이터 탐색 / +대소문자 무시
startswith / istartswith지정한 문자열로 시작하는 데이터 탐색 / +대소문자 무시
endswith / iendswith지정한 문자열로 끝나는 데이터 탐색 / +대소문자 무시
in리스트나 튜플 자료형이 있는 값들에 해당하는 데이터 탐색

숫자/날짜/시간

필드명__조건 = 조건값

# ex
User.objects.filter(created_at__lt = date(2010,1,1))
조건내용
gt(=grater than) 필드명 > 조건값
gte(=grater than equal) 필드명 >= 조건값
lt(=less than) 필드명 < 조건값
lte(=less than equal) 필드명 =< 조건값



참고
https://kimdoky.github.io/django/2020/02/03/django-queryset-api/

profile
한 줄 소개가 자연스러워지는 그날까지

0개의 댓글