django queryset tutorial 1

dooh kim·2020년 9월 8일
0

queryset-tutorial

목록 보기
1/5

https://docs.djangoproject.com/en/3.1/topics/db/queries/

Making queries

`models.py'

from django.db import models


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

    def __str__(self):
        return self.name


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

    def __str__(self):
        return self.name


class Entry(models.Model):
    blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
    headline = models.CharField(max_length=255)
    pub_date = models.DateField(null=True)
    authors = models.ManyToManyField(Author)
    number_of_comments = models.IntegerField(default=0)
    number_of_likes = models.IntegerField(default=0)
    rating = models.IntegerField(default=0)

    def __str__(self):
        return self.headline

django 3.0 부터 extensions 에서 orm 사용할 때 error 난다

https://stackoverflow.com/questions/59119396/how-to-use-django-3-0-orm-in-a-jupyter-notebook-without-triggering-the-async-con

import os 
os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"

Creating objects

To represent database-table data in Python objects, Django uses an intuitive system: A model class represents a database table, and an instance of that class represents a particular record in the database table.

모델 클래스는 테이블이다
인스턴스는 테이블의 하나의 로우 한 줄의 데이터 이다.

하나의 열을 만들기 위해서
인스턴스를 만들고 save() 해주면 된다!

from queryset.models import Blog

# 로우생성
# create 데이터 생성

# 방법 1
b = Blog(name='Beatles Blog')
b.save()

# 방법2
b = Blog.objects.create(name='kimdooh Blog')

data update

# get model class field or pk 로 업데이트할 데이터를 찾는다
b1 = Blog.objects.get(name='kimdooh Blog')
# 이 시점에는 데이터를 호출 하지 않는다.
b1.name = 'kimdooh Blog change name'
# 이 시점에 호출
b1.save()

Saving ForeignKey and ManyToManyField fields

Updating a ForeignKey field works exactly the same way as saving a normal field – assign an object of the right type to the field in question. This example updates the blog attribute of an Entry instance entry, assuming appropriate instances of Entry and Blog are already saved to the database (so we can retrieve them below)

ForeignKey field 역시 다른 일반 field와 같은 방식으로 저장하면 된다. 다만 올바른 유형의 객체를 넣어야 한다.

Foreign Key필드 저장은

  • 이미 db에 저장된 모델 인스턴스를 불러와(get) 객체 유형으로 넣어줘 저장한다.

Entry.objects.create(blog=b, headline='Sample Entry')

## Saving ForeignKey

# entry의 blog값을 변경
entry = Entry.objects.get(pk=1)
print(entry.blog)

# name이  Cheddar Talkdls인 Blog를 가져오거나, 없으면 생성
cheese_blog, _=Blog.objects.get_or_create(name='Cheddar Talk')

# entry의 blog값을 변경
entry.blog = cheese_blog

# DB에서 entry의 blog값이 변경되는 시점
entry.save()
print(entry.blog)

Many-to-many필드는 처음에 만들때 shell에서 저장되지 않는다
인스턴스, 즉 하나의 로우 데이터가 만들어진 데이터 객체 entry를 create 한후 entry 필드의 authors 필드에 add 추가해준다


joe = Author.objects.create(name='Joe')
# Many-to-many필드에 내용을 추가, 이때는 save()를 호출하지 않아도 DB저장
entry.authors.add(joe)

Retrieving objects

To retrieve objects from your database, construct a QuerySet via a Manager on your model class.

A QuerySet represents a collection of objects from your database. It can have zero, one or many filters. Filters narrow down the query results based on the given parameters. In SQL terms, a QuerySet equates to a SELECT statement, and a filter is a limiting clause such as WHERE or LIMIT.

You get a QuerySet by using your model’s Manager. Each model has at least one Manager, and it’s called objects by default. Access it directly via the model class, like so:

데이터 베이스에 있는 데이터 로우(objects)를 찾는데 model class 의 objects를 이용한다 (Blog.objects)
데이터가 없어 제로일 수 있거나 filter를 통해 여러 데이터 로우를 가져올 수 있다.

model 클래스에서만 가져올 수 있다.

블로그 인스턴스에서는 가져올 수 없다

b.objects
AttributeError   | Traceback (most recent call last)
<ipython-input-15-035e8dd57be2> in <module>
----> 1 b.objects

~/.pyenv/versions/queryset-learning-
env/lib/python3.7/site-
packages/django/db/models/manager.py in __get__(self, 
instance, cls)
    174     def __get__(self, instance, cls=None):
    175         if instance is not None:
--> 176             raise AttributeError("Manager isn't
accessible via %s instances" % cls.__name__)
    177 
    178         if cls._meta.abstract:

AttributeError: Manager isn't accessible via Blog 
instances

Note

Managers are accessible only via model classes, rather than from model instances, to enforce a separation between “table-level” operations and “record-level” operations.

인스턴스에서 왜 접근을 못하게 했을까?
model class는 테이블을 나타낸다.

  • 클래스단위 작업은 테이블 레벨에서의 작업이다
  • 하나의 로우에서 작업할 때 하나의 인스턴스에서 작업하는 것을 의미한다.

filter(**kwargs)

2006년에 출판된 데이터를 가져오고 싶다면
Entry.objects.fillter(pub_date__year==2006)

Chaining filters

The result of refining a QuerySet is itself a QuerySet, so it’s possible to chain refinements together. For example:


import datetime
Entry.objects.filter(
    headline__startswith='What'
).exclude(
    pub_date__gte=datetime.date.today()
).filter(
    pub_date__gte=datetime.date(2005, 1, 30))


# headline이 "What"으로 시작하는 Entry를 검색
q1 = Entry.objects.filter(headline__startswith='What')
# 첫번째의 필터된 데이터 q1에서 오늘 혹은 오늘 이후 데이터 제외
q2 = q1.exclude(pub_date__gte=datetime.date.today())
# 두번째 필터된 데이터 q2에서 1990-01-30 이후 출반된 데이터 추출
q3 = q2.filter(pub_date__gte=datetime.date(1990,1,30)

QuerySets are lazy

QuerySets are lazy – the act of creating a QuerySet doesn’t involve any database activity. You can stack filters together all day long, and Django won’t actually run the query until the QuerySet is evaluated. Take a look at this example:

# 이 때는 데이터 베이스에 접근함
# 표현값을 보기 때문에 데이터 베이스에 갔다옴
Entry.objects.all()
<QuerySet [<Entry: Sample Entry>, <Entry: Sample Entry>]>

# 이때는 데이터 베이스에 접근하지 않는데
q = Entry.objects.all()
print(type(q))

<class 'django.db.models.query.QuerySet'>

print(q.query)
SELECT "queryset_entry"."id", "queryset_entry"."blog_id",
"queryset_entry"."headline", "queryset_entry"."pub_date",
"queryset_entry"."number_of_comments",
"queryset_entry"."number_of_likes",
"queryset_entry"."rating" FROM "queryset_entry"

퀘리셋 객체를 만드는 건 쿼리 문만 만든다
쿼리 셋 안에 있는 내용 필드나 데이터를 봐야할 때 갔다온다.

Though this looks like three database hits, in fact it hits the database only once, at the last line (print(q)). In general, the results of a QuerySet aren’t fetched from the database until you “ask” for them. When you do, the QuerySet is evaluated by accessing the database. For more details on exactly when evaluation takes place, see When QuerySets are evaluated

[참조]
https://docs.djangoproject.com/en/3.1/ref/models/querysets/#when-querysets-are-evaluated


q = Entry.objects.filter(headline__startswith="What")
q = q.filter(pub_date__lte=datetime.date.today())
q = q.exclude(body_text__icontains="food")
# 이 때만 쿼리셋을 평가한다(데이터 접근한다)
print(q)

Retrieving a single object with get()

one_entry = Entry.objects.get(pk=1)

  • get은 하나의 결과만 가져올 때 유용하다

one_entry = Entry.objects.filter(name__contains='dooh')

  • 한 개 이상의 데이터를 가져올 때 쓰인다.
하나도 데이터가 없을 때

get은 DoesNotExist Error가 발생
fillter는 IndexError가 발생한다

get에서 두개 이상 데이터가 발생하면 MultipleObjectsReturned Error가 발생한다.

Limiting QuerySets

Use a subset of Python’s array-slicing syntax to limit your QuerySet to a certain number of results. This is the equivalent of SQL’s LIMIT and OFFSET clauses

리스트의 slice기능과 LIMIT 과 OFFSET과 같다
Entry.objects.all()[:5]

Negative indexing (i.e. Entry.objects.all()[-1]) is not supported.

역순 정렬은 python에서 지원되지만 데이터베이스에 없기 때문에 지원 되지 않는다

단순 슬라이싱은 평가가 되지 않는다 하지만 step을 설정하게되면 이 줄에서 평가 된다. 이유는? sql에 step을 건너뛰는 방법이 없기 때문에
이 줄에서 결과물을 가져와서 짜른다.

Entry.objects.all()[:10:2]

Field lookups

Field lookups are how you specify the meat of an SQL WHERE clause. They’re specified as keyword arguments to the QuerySet methods filter(), exclude() and get().

Field lookups은 SQL에 Where절에 해당한다.

2006-01-01 보다 작거나 같은 데이터를 가져오겠다.
Entry.objects.filter(pub_date__lte='2006-01-01')

SELECT * FROM blog_entry WHERE pub_date <= '2006-01-01';

조건! primary key 값으로 조회 해야한다.
데이터 베이스 컬럼명이 "blog_id" 이기 때문에 조회 가능하다.

[참고]
https://docs.djangoproject.com/en/3.1/ref/models/querysets/#field-lookups

iexact

  • 대소문자 구별 없이 조회

Blog.objects.get(name__iexact="beatles blog")

contains

  • text를 포함하는 데이터 조회
    Entry.objects.get(headline__contains='Lennon')

startswith / endswith
찾고자하는 keyword가 앞에서 시작 / 찾고자하는 keyword가 마지막

Lookups that span relationships

e1, e2, e3 = [
    Entry.objects.create(
        blog=Blog.objects.get(name='Beatles Blog'),
        headline=f'Beatles Entry {i}',
    )
    for i in range(3)
]

# Entry 테이블에서 headline 필드 값에 
# "Beatles" 텍스트를 포함한 데이터를 조회
Entry.objects.filter(headline__contains='Beatles')

<QuerySet [<Entry: Beatles Entry 0>, 
<Entry: Beatles Entry 1>, <Entry: Beatles Entry 2>]>


# Entry 테이블에서 blog Foreign Key 관계로 넘어가서 "Cheddar" 
라는 텍스트를 포함시킨 Entry 데이터 가져오기
q = Entry.objects.filter(blog__name__contains='Cheddar')
print(q)

<QuerySet [<Entry: Sample Entry>]>

# Entry 테이블에서 blog Foreign Key 관계로 넘어가서 "e" 라는 
텍스트를 포함시킨 Entry 데이터 가져오기
# 1. blog 테이블에서 'e'를 포함한 객체를 가져온가
# 2. 'e'를 포함한 blog 객체와 관계를 가진 Entry 테이블을 조회한다
al = Entry.objects.filter(blog__name__contains='e')
print(al)

<QuerySet [<Entry: Sample Entry>, <Entry: Sample Entry>,
<Entry: Beatles Entry 0>, <Entry: Beatles Entry 1>, 
<Entry: Beatles Entry 2>]>

# 첫번째, blog 테이블 입장에서 
# entry model의 field headline "Beat"를 포함한 데이터를 가져온다
# 두번째, 전체 entry 에서 "Beat"를 포함한 각각의 entry에
# Foreign key로 걸린 Blog 객체를 전체 가져온다

# many-to-many에서 related_query_name 설정하지 않으면
# class name을 기본적으로 소문자로 설정한다.
Blog.objects.filter(entry__headline__contains='Beat')

distinct()

  • 중복을 제거함 (리소스를 많이 잡아먹는 행위)

qs3.distinct()

profile
testify to the light

0개의 댓글