TIL - 0219(Making queries)

[ summary ]

  1. 멜론 song_search로 Django 복습
  2. Django_document : Making queries

[ 과제 ]

  1. field lookups : 필드에 대해서 조건을 걸 수 있는 것
  2. 멜론 songs 과제
  3. 오늘 배운거 문서로 정리할 것

Making queries

# weblog.models.py
from django.db import models
from django.db import models

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def __str__(self):
        return self.name

class Author(models.Model):
    name = models.CharField(max_length=200)
    email = models.EmailField()

    def __str__(self):
        return self.name

class Entry(models.Model):
    blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
    headline = models.CharField(max_length=255)
    body_text = models.TextField(blank=True)
    pub_date = models.DateField(blank=True, null=True)
    mod_date = models.DateField(auto_now = True)
    authors = models.ManyToManyField(Author,blank=True)
    n_comments = models.IntegerField(default=0)
    n_pingbacks = models.IntegerField(default=0)
    rating = models.IntegerField(default=0)

    def __str__(self):
        return self.headline

Create objects

  • models.py에 있는 class 는 데이터베이스의 테이블을 의미한다. 그리고 각 클래스의 인스턴스들은 데이터베이스 테이블의 레코드를 의미
  • save()를 이용하여 데이터베이스에 저장, 이 전까지는 데이터베이스에 접근하지 않음
  • save()호출 후, INSERT SQL 문이 실행
# 메모리상에만 b라는 인스턴스가 만들어진 것
b = Blog(name='BEatles Blog', tagline = 'ALl the latest Beatles news')
# 데이터베이스에 저장
b.save()

Saving change to objects

# 속성을 변경한후 save() 하면, UPDATE SQL 명령문이 실행
b.name = 'New name'
b.save()

Saving ForeignKey and ManyToManyField

  • ManyToManyField의 경우, add()를 사용해서, 레코드를 추가
joe = Author.objects.create(name ='Joe')
entry.author.add(joe)
# 여러명을 할때 이런식으로
paul, george, ringo = [Author.objects.create(name = name) for name in ['Paul','George','Ringo']]
entry.authors.add(john, paul, george, ringo)

Retrieving objects(객체 검색하기)

  • 검색과 관련된 SQL 용어는 SELECT, 그리고 WHERE 또는 LIMIT 가 있다.
  • 쿼리셋은 모델의 Manager 을 통해 사용할 수 있다.
  • 각 모델은 하나의 매니저들을 가지고 있고, objects 라는 기본 매니저가 있다.

Retrieving all objects

# 전체 가져오기
Entry.objects.all()

Retrieving specific objects with filters

  • filter
  • exclude = WHERE NOT
from datetime import date
Entry.objects.create(blog = b, headline ='test', pub_date = date(2006,01,01))

# 같은 결과
Entry.objects.filter(pub_date__year=2006)
Entry.objects.all().filter(pub_date__year=2006)

Chaining filters

  • 여러개 겹쳐서 filter을 할 수 있다.
# 오류 났음 왜 났는지 질문

In [15]: Entry.objects.filter(headline__startwith = 'What')
-----------------------------------------------------------------------
FieldError: Unsupported lookup 'startwith' for CharFiel

Filtered QuerySets are unique

# q1을 가지고 q2,q3 쿼리셋이 만들어지지만, q1에는 영향이 없다.
q1 = Entry.objects.filter(headline__startswith="What")
q2 = q1.exclude(pub_date__gte=datetime.date.today())
q3 = q1.filter(pub_date__gte=datetime.date.today())

QuerySet are lazy

쿼리셋이 평가 될 때까지 실행되지않음 쿼리셋실행 관련 자료

Retrieving a single object with get()

  • get : Does not exist
  • filter : 빈 쿼리셋 생성
  • get()은 반드시 한개하고만 연결되야 한다. 한개 이상인 경우 오류

Limiting querysets

  • 슬라이스 한 연산에는 ordering이 불가
# 이 경우에는 그냥 쿼리셋만 만드는 것
q = Entry.objects.all()[5:10]

# step을 사용했을 경우에면 쿼리문이 평가가 된다. (실행)
# 평가가 되고 난 후에 리스트로 반환
q = Entry.objects.all()[5:10:2]

Field lookups

  • SQL WHERE
  • exact / iexact / contains

Lookups that span relationships

  • 정방향, 역방향 참조 관련 내용
  • 관련 모델이 없는 경우, 오류가 발생하지 않는다. 그래서 NULL 이 있을 경우 오류가 나게 하려면 __isnull = False
# 역방향 entry -> authors -> name
Blog.objects.filter(entry__authors__name='Lennon')

Spanning multi-valued relationships

1.headline ‘Lennon’이 있고, 2008년에 출판된 Entry 두 조건 만족하는 blog e1 = Entry(headline = Lennon, pub_date = 2008)

2.headline에 ‘Lennon’이 있는 Entry와 2008년에 출판된 Entry가 있는 blog e2 = Entry(headline=’lhy’, pub_date=2008) e3 = Entry(headline=’Lennon’,pub_date=2000)

# 1번의 경우
# 같은 entry에 조건이 걸리는 경우
Blog.objects.filter(entry__headline__contains='Lennon',entry__pub_date__year=2008)
# 2번 조건의 경우
# filter가 실행된 값에서 Blog에서 두번째 조건을 건다.

# entry가 다중 상속인 경우, and 조건이 아니라,
# 첫번째 Filter 에서 나온 결과값에서 다시 한번 filter 를 적용
Blog.objects.filter(entry__headline__contains='Lennon').filter(entry__pub_date__year=2008)


# exclude 의 경우는 filter처럼 작동하지 않는다.

# 조건 두개가 각각 만족되는 경우 삭제
Blog.objects.exclude(
    entry__headline__contains='Lennon',
    entry__pub_date__year=2008,
)

# 한번에 두 조건 모두를 만족 시키는 것을 삭제
Blog.objects.exclude(
    entry__in=Entry.objects.filter(
        headline__contains='Lennon',
        pub_date__year=2008,
    ),
)

Filters can reference fields on the model

  • 쿼리셋이 실행된 후에는 변경된 데이터를 가져오지 못하므로, 변경되면 다시한번 실행시켜줘야 한다.
  • F = 동일한 모델 인스턴스에서 다른 필드를 비교 할때 사용

Caching and QuerySets

  • 한번 평가된 쿼리셋은 데이터베이스에서 정보를 가져온다. 이후에 같은 쿼리셋이 평가되는 경우엔, 데이터베이스에서 다시 가져오는 것이 아닌 이전에 저장된 정보를 사용한다.
  • 그렇기 때문에 새로운 정보가 업데이트 될때마다 이를 반영되는게 어렵다.
# 예상된 결과값이 다른 것을 방지하기 위해선 아래와 같은 방법 사용
queryset = Entry.objects.all()
print([p.headline for p in queryset]) # Evaluate the query set.
print([p.pub_date for p in queryset]) # Re-use the cache from the evaluation.

# 쿼리셋을 일부분만 할 경우, 데이터베이스를 다녀오긴 하나 데이터를 가지고 있지 않는다'
queryset = Entry.objects.all()
print(queryset[5]) # Queries the database
print(queryset[5]) # Queries the database again

Complex lookups with Q objects

  • Q = OR 문을 사용했을 때
  • 조건만 만들 수 있다.
# INVALID QUERY
# 키워드 인자는 위치인자보다 항상 뒤에 와야 하기 때문에 오류!
Poll.objects.get(
    question__startswith='Who',
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)

Comparing objects

각 속성들 비교할 때는 PK비교를 하는게 가장 중요하다.

Deleting objects

CASCADE 로 걸려있는 경우에는 delete 메소드가 실행되지 않을 수도 있다. 사용자가 delete재정의할 경우, 그 메소드가 실행되지 않을 수도 있다. 이것을 실행시키기 위해선 수동으로 실행시켜야한다

Copying model instances

# copy
blog = Blog(name='My blog', tagline='Blogging is easy')
blog.save() # blog.pk == 1

blog.pk = None
blog.save() # blog.pk == 2

그러나 many_to_many의 경우, MTM은 그대로이기 때문에 복사 불가
one_to_one도 A-B,A-C 이런식이 되면 A가 어떤걸 선택해야 할지 모르기 때문에, 1:1 조건에 맞게끔 해야한다.

Updating multiple objects at once

  • update()의 경우, 데이터베이스에 바로 적용이 되고, 결과값으로 업데이트된 갯수들이 반환됨.
  • 데이터베이스에 적용이 바로 되기 때문에, save()를 하지 않아도 됨
  • Signals : 중복되는 일이 여러 모델에서 사용될 때 signals로 알리는 것 ex) entry 가 만들어질때마다 구독자에게 이메일이 전송 됨. 모델 50개가 되어야 하고 save가 50개 필요 그래서 이메일을 보내야한다는 Signals
  • 자기자신의 테이블에서만 사용가능

postgreSQL

설치 사이트

<!-- INSTALL -->
sudo apt-get update
sudo apt-get install postgresql postgresql-contrib

<!-- USER MAKE  -->
sudo -u postgres createuser --interactive
Enter name of role to add: kahee
Shall the new role be a superuser? (y/n) y

<!-- P: password s: superuser  -->
~ sudo -u postgres createuser -P -s kahee
Enter password for new role:
Enter it again:

<!-- 사용자 확인  -->
psql postgreSQL
\du

List of roles
Role name |                         Attributes                         | Member of
-----------+------------------------------------------------------------+-----------
kahee     | Superuser, Create role, Create DB                          | {}
postgres  | Superuser, Create role, Create DB, Replication, Bypass RLS | {}
ykh       | Superuser, Create role, Create DB                          | {}

<!-- 데이터베이스 생성 -->
sudo -u postgres createdb fc-django-document --owner=kahee

기존 데이터 백업하기

git에 업로드 되지 않도록 주의

<!-- dump.json파일로 백업  -->
./manage.py dumpdata weblog auth --indent=4 > dump.json

postgresSQL을 Django에서 사용하기 위해서 설정

# config/seetings.py

# Database
# https://docs.djangoproject.com/en/2.0/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'fc-django-document',
        'USER': 'kahee',
        'PASSWORD' : '설정된 암호 입력',
        'HOST': 'localhost',
        'PORT': '5432',
    }
}

설정이 끝나면 makemigrations 실행하면 아래와 같은 오류가 발생 이는 postgres와 python을 연결해주는 라이브러리 psycopg2가 없어서 발생된다. 그래서 설치 후, makemigrations하면 해결!

jango.core.exceptions.ImproperlyConfigured: Error loading psycopg2 module: No module named 'psycopg2'

pip install psycopg2
./manage.py makemigrations
./manage.py migrate

<!-- 백업파일적용  -->
<!-- 그러나 나의 경우, 오류가 생겨서 백업이 제대로 안됬음 ㅜㅠ -->
./manage.py loaddata dump.json