Django ORM 과 SQL

BackEnd_Ash.log·2020년 2월 16일
1

1차 업데이트 2020년 4 월 18일
selected , prefetch

select

models.py

class Site(models.Model):
    name = models.CharField(max_length=255,)

class Category(models.Model):
    name = models.CharField(max_length=255,)

class Article(models.Model):
    subject = models.CharField(max_length=255,)
    url = models.URLField(unique=True,)
    date = models.DateTimeField()
    site = models.ForeignKey(Site)
    category = models.ManyToManyField(Category)
    hit = models.IntegerField(default=0)

위의 클래스는 각각의 테이블이라고 생각하면 된다.

여기에 mysql 에 만약에 연동이 되었다고 가정을 한다면
거기안에 table 명을 지정할 수가 있다 .

class Meta:
    table_db = 'articles'

이렇게 짤 수가 있다.

orm 같은경우
python manage.py shell 로 들어가서
from .model improt Article
입력후에
Article.objects. 까지입력을 하고
tab 키를 누르게 되면 기능들이 나오게 된다. 그렇게 할수도 있다.

all

SQL
select * from test_table;
ORM
Article.objects.all()

where

SQL
select * from test_table where id =1 ;
ORM
Article.objects.filter(id=1)

만약에 데이터 입력을 누군가에게로 부터 받게하고
그것을 table 에 있는지 확인을 한다고 한다면
만약에 Users 라는 테이블이 있다라는 가정하에 ,

data = json.loads(request.body)
if Users.objects.filter(email = data ['email']).exists():

이렇게 할 수가 있다.

limit n

만약에 limit 를 걸어서 그 개수 만큼 가져 오게 하고 싶다면 ?

SQL
select * from test_table limit 10 ;
ORM
Article.objects.all()[:10]

limit n,n

위에 예제는 처음부터 몇까지 가져오는거라면
index 처음 위치를 설정해서 어디까지 정할 수 있는 명령어 이다.

SQL
select * from test_table limit 5,10;
ORM
Article.objects.all()[5:10]

AND

mysql 에서 and 는 예를들어서
User table 에서 성별이 남자이고 , 보유금액이 5500 이상인 사람의 정보를 불러울때

select * from user_table where user_gender = 'male' and user_amount >=5500;

이렇게 적어진다.

SQL
select * from test_table where gender='male' and id > 3;
ORM


>>> Account.objects.filter(id__gt=2)
<QuerySet [<Account: jakdu2>, <Account: jakdu3>, <Account: sory>, <Account: sory2>]>

from django.db.models import Q
>>> Account.objects.filter(Q(name__startswith='jakdu'))
<QuerySet [<Account: jakdu>, <Account: jakdu2>, <Account: jakdu3>]>

>>> Account.objects.filter(id=1,name='jakdu')
<QuerySet [<Account: jakdu>]>

like '%s%'


SQL
mysql> select * from Account where name like '%j%';
+----+------------------+--------+--------+--------------------------------------------------------------+----------------------------+----------------------------+
| id | email            | name   | gender | password                                                     | create_at                  | update_at                  |
+----+------------------+--------+--------+--------------------------------------------------------------+----------------------------+----------------------------+
|  1 | jakdu@gmail.com  | jakdu  | male   | $2b$12$SM7/J0sJG3AN2R5e6YbBiON3Q596F6tTq2ircli2aAWh/r3r8QCSW | 2020-02-16 04:57:14.224284 | 2020-02-16 04:57:14.224337 |
|  5 | jakdu2@gmail.com | jakdu2 | male   | $2b$12$fq.cCKldyXRYtKq7.Zc1XuVhnPJC/Iigq9kTuJFx8tushRj7KIrRi | 2020-02-16 04:59:06.510564 | 2020-02-16 04:59:06.510605 |
|  6 | jakdu3@gmail.com | jakdu3 | male   | $2b$12$y/R.eJ52.TWmtZYoYCDsE.jgyw3xUEEOhXxmokmmYMZvO62pBORmu | 2020-02-16 04:59:33.360545 | 2020-02-16 04:59:33.360587 |
+----+------------------+--------+--------+--------------------------------------------------------------+----------------------------+----------------------------+

ORM
>>> Account.objects.filter(name__icontains='j')
<QuerySet [<Account: jakdu>, <Account: jakdu2>, <Account: jakdu3>]>

like 's%'

SQL
mysql> select * from Account where name like 's%';
+----+-----------------+-------+--------+--------------------------------------------------------------+----------------------------+----------------------------+
| id | email           | name  | gender | password                                                     | create_at                  | update_at                  |
+----+-----------------+-------+--------+--------------------------------------------------------------+----------------------------+----------------------------+
|  7 | sory@gmail.com  | sory  | female | $2b$12$DlW.v/s7wzztja9V5Dyq5uyxetn0Jn.0FRu68pqMu9IqFNYmSrmzG | 2020-02-16 05:00:25.514912 | 2020-02-16 05:00:25.514953 |
|  8 | sory2@gmail.com | sory2 | female | $2b$12$Z5zxU2tXgw.ZuPG5l8lwtepo48PoaCQwjpBFNS4TiW8p/Zin.Hc2C | 2020-02-16 05:00:51.773095 | 2020-02-16 05:00:51.773135 |
+----+-----------------+-------+--------+--------------------------------------------------------------+----------------------------+----------------------------+

ORM
>>> Account.objects.filter(name__startswith='s')
<QuerySet [<Account: sory>, <Account: sory2>]>

like '%s'

SQL
mysql> select * from Account where name like '%2';
+----+------------------+--------+--------+--------------------------------------------------------------+----------------------------+----------------------------+
| id | email            | name   | gender | password                                                     | create_at                  | update_at                  |
+----+------------------+--------+--------+--------------------------------------------------------------+----------------------------+----------------------------+
|  5 | jakdu2@gmail.com | jakdu2 | male   | $2b$12$fq.cCKldyXRYtKq7.Zc1XuVhnPJC/Iigq9kTuJFx8tushRj7KIrRi | 2020-02-16 04:59:06.510564 | 2020-02-16 04:59:06.510605 |
|  8 | sory2@gmail.com  | sory2  | female | $2b$12$Z5zxU2tXgw.ZuPG5l8lwtepo48PoaCQwjpBFNS4TiW8p/Zin.Hc2C | 2020-02-16 05:00:51.773095 | 2020-02-16 05:00:51.773135 |
+----+------------------+--------+--------+--------------------------------------------------------------+----------------------------+----------------------------+


ORM
>>> Account.objects.filter(name__endswith='2')
<QuerySet [<Account: jakdu2>, <Account: sory2>]>

>=

SQL
mysql> select * from Account where id >=2;
+----+------------------+--------+--------+--------------------------------------------------------------+----------------------------+----------------------------+
| id | email            | name   | gender | password                                                     | create_at                  | update_at                  |
+----+------------------+--------+--------+--------------------------------------------------------------+----------------------------+----------------------------+
|  5 | jakdu2@gmail.com | jakdu2 | male   | $2b$12$fq.cCKldyXRYtKq7.Zc1XuVhnPJC/Iigq9kTuJFx8tushRj7KIrRi | 2020-02-16 04:59:06.510564 | 2020-02-16 04:59:06.510605 |
|  6 | jakdu3@gmail.com | jakdu3 | male   | $2b$12$y/R.eJ52.TWmtZYoYCDsE.jgyw3xUEEOhXxmokmmYMZvO62pBORmu | 2020-02-16 04:59:33.360545 | 2020-02-16 04:59:33.360587 |
|  7 | sory@gmail.com   | sory   | female | $2b$12$DlW.v/s7wzztja9V5Dyq5uyxetn0Jn.0FRu68pqMu9IqFNYmSrmzG | 2020-02-16 05:00:25.514912 | 2020-02-16 05:00:25.514953 |
|  8 | sory2@gmail.com  | sory2  | female | $2b$12$Z5zxU2tXgw.ZuPG5l8lwtepo48PoaCQwjpBFNS4TiW8p/Zin.Hc2C | 2020-02-16 05:00:51.773095 | 2020-02-16 05:00:51.773135 |
+----+------------------+--------+--------+--------------------------------------------------------------+----------------------------+----------------------------+

ORM

>>> Account.objects.filter(id__gte=2)
<QuerySet [<Account: jakdu2>, <Account: jakdu3>, <Account: sory>, <Account: sory2>, <Account: sory3>]>

<=

SQL
mysql> select * from account where id <=2;
+----+-----------------+-------+--------+--------------------------------------------------------------+----------------------------+----------------------------+
| id | email           | name  | gender | password                                                     | create_at                  | update_at                  |
+----+-----------------+-------+--------+--------------------------------------------------------------+----------------------------+----------------------------+
|  1 | jakdu@gmail.com | jakdu | male   | $2b$12$SM7/J0sJG3AN2R5e6YbBiON3Q596F6tTq2ircli2aAWh/r3r8QCSW | 2020-02-16 04:57:14.224284 | 2020-02-16 04:57:14.224337 |
+----+-----------------+-------+--------+--------------------------------------------------------------+----------------------------+----------------------------+

ORM

>>> Account.objects.filter(id__lte=2)
<QuerySet [<Account: jakdu>]>

> 그리고 <

SQL
select * from account where id > 2 ;
select * from account where id < 2 ;

2 보다 큰것이 출력 되고 
밑에는 2보다 작은것이 출력됨

ORM
>>> Account.objects.filter(id__gt=2)
<QuerySet [<Account: jakdu2>, <Account: jakdu3>, <Account: sory>, <Account: sory2>, <Account: sory3>]>
>>> Account.objects.filter(id__lt=2)
<QuerySet [<Account: jakdu>]>

insert

SQL
insert into account set name='monster';

ORM
account = Account(name='monster')
account.save()

있으면 가져오고 없으며 집어넣기


ORM
account = Account.objects.get_or_create(name='monster1')
# save 메서드 호출 없이도 바로 입력됨

참고자료
https://blog.naver.com/dudwo567890/220924729927
https://docs.djangoproject.com/en/1.10/topics/db/queries/

order 순서

순서대로 뿌려주고 싶을때가 있다 . 보통 order_by 를 지정하지 않는다면 ,
id 순서대로 뿌려주게 되어있습니다.

Product.objects.all().order_by('name').values('price')
이름 순서대로 뿌려주는데 값을 price 만 보여달라는 orm 입니다.

그런데
Product.objects.select_related('harvest_year' , 'measure').order_by('name').values('price')
이렇게 해도 똑같은 결과가 나오게 된다 .
왜지 ??
select_related 를 무엇을 쓰기위해서 저렇게 하는걸까 ??

select_related 정참조

select_related 는 내가 참조하는것에 대한 데이터를 가져올때 사용한다.
위와 똑같이

Product.objects.select_related('harvest_year' , 'measure').order_by('name').values('harvest_year_id__year')

입력을 해주면

<QuerySet [{'harvest_year_id__year': '2014'}, {'harvest_year_id__year': '2016'}, 
{'harvest_year_id__year': '2014'}, {'harvest_year_id__year': '2015'}, 
{'harvest_year_id__year': '2015'}, {'harvest_year_id__year': '2016'}, 
{'harvest_year_id__year': '2016'}, {'harvest_year_id__year': '2015'}, 
{'harvest_year_id__year': '2016'}, {'harvest_year_id__year': '2015'}, 
{'harvest_year_id__year': '2015'}, {'harvest_year_id__year': '2015'}, 
{'harvest_year_id__year': '2015'}, {'harvest_year_id__year': '2016'}, 
{'harvest_year_id__year': '2016'}, {'harvest_year_id__year': '2016'}, 
{'harvest_year_id__year': '2015'}, {'harvest_year_id__year': '2016'}, 
{'harvest_year_id__year': '2015'}, {'harvest_year_id__year': '2014'},
'...(remaining elements truncated)...']>

이러한 결과값이 나오게되지만

harvest_year    = models.ForeignKey('HarvestYear', on_delete = models.SET_NULL, null = True)

변수를 선언한 곳을 보면 이렇게 되어있다.
참조를 하게되면
harvest_year 뒤에 자동적으로 harvest_year_id 라고 생성이 되기때문입니다.

values('harvest_year_id__year')

를 해주면 참조하고 있는 harvest_year 에 있는 year 에 대한 값을 가져오게 됩니다.

In [21]: Product.objects.select_related('harvest_year').order_by('name').get(id=1).harvest_year
Out[21]: <HarvestYear: HarvestYear object (1)>

이렇게 적을 수도 있는데 ,
Product 테이블에 있는 데이터 중에 id 값이 1 것중에 참고하고있는 harvest 객체를 가져오게 됩니다.

In [22]: Product.objects.select_related('harvest_year').order_by('name').get(id=1).harvest_year.year
Out[22]: '2014'

객체 중에 필드 하나를 입력하면 필드값을 출력해줍니다.

In [30]: Product.objects.select_related('harvest_year').order_by('id').values('id')[1:10]
Out[30]: <QuerySet [{'id': 2}, {'id': 3}, {'id': 4}, {'id': 5}, {'id': 6}, {'id': 7}, {'id': 8}, {'id': 9}, {'id': 10}]>

그리고 마지막에 [ ] 를 해주면 몇개를 출력할 것인지 정할 수가 있습니다.

prefetch_related

prefetch_related 를 사용하는 경우는 크게 두가지입니다.

  • many to many 관계인 경우
  • 역참조

many to many

many to many 의경우 중간테이블에서 관리하게 됩니다.
디비에 들어가서 table 를 보게되면 칼럼이 생기지 않은 것을 볼수 있습니다.

테이블이 이렇게 되어있다고 가정을 하겠습니다.

category 테이블에는 ,

<QuerySet [{'id': 1, 'name': 'Fresh', 'image_url': 'http://cdn.shopify.com/s/files/1/1148/3974/collections/collection_02_fresh_large.png?v=1455202997', 'description': 'You’ll find here a lot of delightful vegtables & fruits that were harvested and are waiting to serve you'}, 
{'id': 2, 'name': 'Frozen', 'image_url': 'https://cdn.shopify.com/s/files/1/1148/3974/collections/collection_03_frozen_large.png?v=1455263893', 'description': 'Look through fruits & vegetables that are saved for later due to the'},
{'id': 3, 'name': 'Dried', 'image_url': 'https://cdn.shopify.com/s/files/1/1148/3974/collections/collection_04_dried_large.png?v=1455263955', 'description': 'We transform herbs, fruits & vegetables from fresh to'}, 
{'id': 4, 'name': 'Liquid', 'image_url': 'https://cdn.shopify.com/s/files/1/1148/3974/collections/collection_05_liquid_large.png?v=1455263995', 'description': 'Almost every cooking requires some oil or sauses in the kitchen. We got you covered here with specially crafted'}, 
{'id': 5, 'name': 'sale', 'image_url': 'https://cdn.shopify.com/s/files/1/1148/3974/collections/collection_06_sale_large.png?v=1456211951', 'description': None}, 
{'id': 6, 'name': 'Specials', 'image_url': None, 'description': None}, 
{'id': 7, 'name': 'Cooked', 'image_url': None, 'description': None}]>

이렇게 들어가 있습니다.

이것을 Product 테이블에서 접근할때는 prefetch_related 를 사용해야합니다.

Product.objects.prefetch_related('category').filter(category__name='Fresh')

위처럼 입력하면 밑에와 같은 결과가 나옵니다.


In [78]: 
Out[78]: <QuerySet [<Product: Product object (5)>,
<Product: Product object (8)>,
<Product: Product object (10)>,
<Product: Product object (11)>,
<Product: Product object (12)>,
<Product: Product object (13)>,
<Product: Product object (14)>,
<Product: Product object (16)>,
<Product: Product object (17)>,
<Product: Product object (19)>,
<Product: Product object (20)>,
<Product: Product object (21)>,
<Product: Product object (22)>,
<Product: Product object (23)>,
<Product: Product object (25)>,
<Product: Product object (27)>,
<Product: Product object (31)>,
<Product: Product object (33)>,
<Product: Product object (35)>,
<Product: Product object (41)>,
'...(remaining elements truncated)...']>

그리고 ,

Product.objects.prefetch_related('category').filter(category__name='Frozen')

라고 입력하면
밑에와 같이 결과가 나오게 됩니다.
왜 이것은 하나가 나왔을까요 ??

Out[76]: <QuerySet [<Product: Product object (44)>]>


하나밖에 없으니깐요..
이런식으로 ManytoMany 를 들고오시면 됩니다.

역참조

역참조는 그러니깐 내가 바라보는것이 아니라
바라보는것을 당하는것이다. ㅡㅡ;; 말이 이상한데

내가 바라보는것이 아니라 , 어떤것이 나를 바라보는것 ?? 이라고 생각하시면 될것 같습니다.

class Product(models.Model):
 ...
	bundle = models.ManyToManyField('Bundle', through = 'ProductBundle')

class Bundle(models.Model):
    title           = models.CharField(max_length = 100)
    price           = models.CharField(max_length = 50, null = True)
    is_in_promotion = models.BooleanField(default = False)
Bundle.objects.prefetch_related('product_set').get(id=1)

역참조를 할땐 뒤에 _set 을 넣어주면 됩니다.
나를 바로보고있는것 _set

여기서는 product 테이블이 bundle 테이블을 바라보고있네요 .

Out[97]: <Bundle: Bundle object (1)>

결과는 이렇게 나옵니다. 왜 ?? id = 1 만을 들고왔으니깐요

Bundle.objects.prefetch_related('product_set').get(id=1).product_set.values('name')

자 그럼 이렇게 입력을 하면 어떻게 되는걸까요 ?
get(id=1) 까지하면 그 객체 하나만 들고오는데 ,
그 레코드 에서 그 레코드를 바라보고있는 Product 테이블의 레코드를 가져오게 됩니다.

무엇을 ?? name 만 가져오게 되죠 .

orm 코드 정렬

  recipe_info = (Recipe
                 .objects
                 .order_by('id')
                 .values('id',
                         'title',
                         'description',
                         'company',
                         'thumbnail_url',
                         'posting_date'
                        )[offset:offset + limit])

이렇게 되어있지 않고 만약에

Recipe.objects.order_by('id).values('id)~~~~

이렇게 되어있다면

( ) 이것으로 감싸줘서 코드정렬을 하면됩니다.
엇?? 그러면 튜플로 나오지 않나 ??
라고 생각하실수도 있습니다.
하지만 나중에 JsonResponse 로 뿌려주게 될때 , list 로변환시키면 됩니다.

class Meta:

    class Meta:
        managed  = False
        db_table = 'companys'

보통 managed 는 default 가 true 이다.
https://docs.djangoproject.com/en/3.1/ref/models/options/

기존 구축된 디비가 있거나 디비를 따로 구축한다면,
장고에서 매니지는 안하고 orm 정도만 사용하는 경우에 사용하게 된다.

  1. inspectdb 만든다.
  2. model 수정을 한다
  3. managed = false 를 제거 한다
  4. migrate 한다
profile
꾸준함이란 ... ?

0개의 댓글