웹 프로그래밍/[ Django ]

[ Django ] 01. Django ORM 활용

kim.svadoz 2020. 8. 11. 09:39
반응형

Django ORM

Object Relational Mapping

스프링에서는 mybatis를 썼었고, 장고에서는 이를 위한 기능이 따로 존재한다!

드디어 models.py를 사용하기 시작합니다~

파이썬의 객체와 DB의 객체를 Mapping해주어야 한다.

그럼 파이썬의 객체는 어떻게 생성하는 가?

  • Articles에 models 작성
# models.py

# Create your models here.
class Article(models.Model) : 
    # articles_article
    # CharField 는 글자 수 제한 할 때 사용
    title = models.CharField(max_length=150)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

$ python manage.py makemigrations
$ python manage.py migrate articles
  • python manage.py makemigrations : DB에 설계도 대로 반영을 해라!!
    • migrations폴더에 아래의 설계도가 생성된다.

image-20200615142700436

  • python manage.py migrate articles : 해당 aritcles 설계도 대로만 스키마 테이블 만들어달라!
  • 실제 DB에 반영되기 위해서는?!
$ python manage.py migrate

CREATE

# 직접 shell창 에서 작업하겠다.
$ python manage.py shell
>>> from articles.models import Article
>>> Article.objects.all()

1. INSERT INTO 테이블명 (column1, column2...) VALUES (values1, values2...)

# 첫 번째 방법
>>> article = Article()
>>> article.title = 'first'
>>> article.content = 'django!!'
>>> article.save()
>>> article
### 실제로 DB가 들어갔는지 확인! (N) 이 생겼어요!
<Article: Article object (1)>

# 두 번째 방법
# 어느 변수에 어떤 값을 넣을건지 명시
# id가 생략되어 있을 뿐, 자동으로 생성된다.
>>> article = Article(title='second', content='django~!')
>>> article.save()
>>> article
<Article: Article object (2)>

# 세 번째 방법
# save() 과정 없이 바로 저장이 된다. DB에 반영하는 방법이 포함되어 있다 !
>>> Article.objects.create(title='third', content'django~~')
<Article: Article object (3)>
  • 첫, 두번째 방법은 인스턴스를 생상하고 그 안에 넣었다면 세번째는 그냥 넣어준다.
    • 사용 용도가 다른것이다!!!!

READ

2. SELECT * FROM articles_article(테이블명)

# 전체 조회
>>> article = Article.objects.all()
>>> article
<QuerySet [<Article: Article object (1)>, <Article: Article object (2)>, <Article: Article object (3)>]>

## 보기가 좋지 않으니
>>> article[0].title
'first'

## 설정을 해줍시다~~

image-20200615152758808

스키마의 형태가 변경이 되면 migration을 해줘야 하지만 단지 데이터의 출력 형태가 변경되는 것은 쉘 창을 종료하고 다시 켜준다.

$ exit()
$ python manage.py shell
>>> from articles.models import Article
>>> Article.objects.all()
<QuerySet [<Article: 1번째 글 - first : django!!>, <Article: 2번째 글 - second : django~!>, <Article: 3번째 글 - third : django~~>]>

3. SELECT * FROM articles_article WHERE title = 'first'

# 특정 제목 불러오기
>>> Article.objects.filter(title='first')
<QuerySet [<Article: 1번째 글 - first : django!!>]>

>>> Article.objects.create(title='first', content='hahahahahahahaha')
<Article: 4번째 글 - first : hahahahahahahaha>

>>> Article.objects.filter(title='first')        
<QuerySet [<Article: 1번째 글 - first : django!!>, <Article: 4번째 글 - first : hahahahahahahaha>]>

SELECT * FROM articles_article WHERE title='first' LIMIT= 1

>>> Article.objects.filter(title='first').first()
>>> Article.objects.filter(title='first').last()

>>> Article.objects.filter(title='first')[0]
<Article: 1번째 글 - first : django!!>

SELECT * FROM articles_article WHERE id=1

PK처럼 고유값을 가지고 있어서 단 하나만 가지고 올 수 있는 방법!

>>> Articles.objects.get(id=1)
>>> Articles.objects.get(pk=1)
<Article: 1번째 글 - first : django!!>

# !!주의점!!
# 1.고유값이 아닌 내용을 필터링 해서 2개 이상의 값이 찾아지면 오류를 발생한다.
#     -> .get()은 반드시 하나의 객체만 가져올 수 있다.
# 2. 없는 것을 가지고 오려고 해도 오류가 발생한다.
#     -> filter는 빈 쿼리셋이 반환이 된다.
>>> Article.objects.filter(pk=10)
<QuerySet []>

Like / startswith / endswith

# 특정 문자로 가져오기
# XXX__contains : XXX에 해당 ''을 포함하고 있는 객체 반환
>>> Article.objects.filter(title__contains='fir')
<QuerySet [<Article: 1번째 글 - first : django!!>, <Article: 4번째 글 - first : hahahahahahahaha>]>

>>> Article.objects.filter(title__startswith='se')
<QuerySet [<Article: 2번째 글 - second : django~!>]>

>>> Article.objects.filter(content__endswith='ha')
<QuerySet [<Article: 4번째 글 - first : hahahahahahahaha>]>

ASC / DESC

# 오름차순
>>> Article.objects.all().order_by('pk')

# 내림차순
>>> Article.objects.all().order_by('-pk')

## Order_by는 DB단에서 역순으로 가져오는 것이고

## 이것은 이미 가져온 것을 파이썬에서 역순으로 처리하는 것이다.
>>> Article.objects.all()[::-1]

UPDATE

UPDATE articles_article SET title='byebye' WHERE id=1

# 수정
>>> article = Article.objects.get(pk=1)
>>> article.title = 'byebye'
>>> article
<Article: 1번째 글 - byebye : django!!>

## 보이는 shell에는 바뀐 것처럼 보이지만 실제 DB에 저장이 되려면 저장을 해줘야 한다.
>>> article.save()

DELETE

DELETE FROM articles_article WHERE id=1

# 삭제
>>> article = Article.objects.get(pk=1)
>>> article.delete()
(1, {'articles.Article': 1})

>>> article = Article.objects.get(pk=1)
## pk=1은 삭제하고 없기 때문에 오류가 난다.

## delete는 별도로 저장을 해주지 않아도 자동으로 DB에 반영이 된다.

관리자 페이지로 확인하기

image-20200615161910998

python manage.py runserver

image-20200615162017084

  • 파이썬에는 슈퍼계정이 존재한다~
# 모든 설정파일을 migrate 해준 뒤 슈퍼유저 생성
$ python manage.py migrate
$ python manage.py createsuperuser

image-20200615162943645

image-20200615162500379

  • admin.py에서 커스터마이징하기!

image-20200615162943645image-20200615162957932

customizing된 admin페이지를 확인할 수 있다 :)

Django ORM 복습 버억

index페이지에 전체 DB 보여주기

# views.py

from articles.models import Article

# Create your views here.
def index(request):
    # 전체 데이터 가져오기
    # 그 데이터 템플릿에게 넘겨주기
    # 템플릿에서 반복문으로 각각의 게시글 pk, title 보여주기
    article = Article.objects.all()
    context = {
        'articles' : article
    }
    return render(request, 'articles/index.html', context                            
# index.html
{% extends 'base.html' %}
{% block body %}
<h1>게시판</h1>
<hr>
<a href="{% url 'articles:new' %}">NEW</a>
<a href="{% url 'articles:introduce' %}">introduce</a>

{% for article in articles %}
  <h3>{{article.pk}}번 째 글</h3>
  <h4>{{article.title}}</h4>
  <h5>{{article.content}}</h5>
  <hr>
{% endfor %}

{% endblock %}

image-20200616111039547

new페이지에서 글 작성하기

# views.py
from django.shortcuts import render, redirect

def new(request):
    return render(request, 'articles/new.html')

def create(request):
    title = request.POST.get('title')
    text = request.POST.get('text')
    Article.objects.create(title=title, content=text)

    return redirect('articles:index')
# new.html

{% block body %}
<h1>글 작성 페이지</h1>

<form action="{% url 'articles:create' %}" method="POST">
  {% csrf_token %}
  <label for="name">제목 : </label>
  <input type="text" name="title">

  <label for="cnt">내용 : </label>
  <input type="text" name="text">

  <input type="submit" value="글 작성">
</form>
{% endblock %}

# create.html

{% block body %}
<p>{{ title }}, {{ text }}</p>
{% endblock %}
  • csrf_token 이란??
    • 내 DB에 어떠한 조작을 할 수 있는 요청을 보낼 땐 항상 세트로 넣어줘야 한다.
    • 보안을 위한 것. ( 없어도 요청은 감 )
  • redirect를 import하여 index로 redirect한다.

index페이지에서 상세페이지보기

# urls.py
path('<int:article_pk>detail/', views.detail, name="detail"),

# views.py
# 1. 상세 페이지를 보기위한 경로
# 1-1. 특정 게시글에 대한 고유 값
# 1-2. /articles/1/, /articles/2/...
# 2. 해당 게시글에 대한 상세 내용
# 2-1. 게스글의 pk, title, ...
# 3. 인덱스 페이지로 돌아가는 링크
def detail(request, article_pk):
    article = Article.objects.get(pk=article_pk)
    context = {
        'article' : article
    }
    return render(request, 'articles/detail.html', context)
# index.html
{% for article in articles %}
  <a href="{% url 'articles:detail' article.pk %}"><h3>{{article.pk}}번 째 글</h3></a>
  <h4>{{article.title}}</h4>
  <h5>{{article.content}}</h5>
  <hr>
{% endfor %}

# detail.html
{% block body %}
    <h1> 상세 페이지^^</h1>
    <h2>{{article.pk}}번 째 글</h2>
    <h4>제목 : {{article.title}}</h4>
    <h5>내용 : {{article.content}}</h5>
    <p>생성 시간 : {{article.created_at}}</p>
    <p>수정 시간 : {{article.updated_at}}</p>
    <a href="{% url 'articles:index' %}">[back]</a>
{% endblock %}

상세페이지에서 글 삭제하기

# urls.py
path('<int:article_pk>/delete/', views.delete, name="delete"),

# views.py
# 1. 특정 글 삭제를 위한 경로 작성
# 1.1 /articles/delete/
# 2. 글 삭제 처리를 해주는 view 작성
# 3. 글 삭제 후, index page로 redirect
# 4. 글 삭제를 위한 링크 detail에 작성
def delete(request, article_pk):
    article = Article.objects.get(pk=article_pk)
    article.delete()
    return redirect('articles:index')
# detail.html
{% block body %}
  <h1> 상세 페이지^^</h1>
    <h2>{{article.pk}}번 째 글</h2>
    <h4>제목 : {{article.title}}</h4>
    <h5>내용 : {{article.content}}</h5>
    <p>생성 시간 : {{article.created_at}}</p>
    <p>수정 시간 : {{article.updated_at}}</p>
    <a href="{% url 'articles:delete' article.pk %}">삭제</a>
    <a href="{% url 'articles:index' %}">[back]</a>
{% endblock %}

게시글 수정하기

# urls.py
path('<int:article_pk>/edit', views.edit, name="edit"),
path('<int:article_pk>/update', views.update, name="update"),

# views.py
# 1. 특정 글 수정을 위한 경로 생성
# 1-1. /articles/1/edit
# 2. 글 수정 template를 render하는 edit view 작성
# 2-1. 해당 templateㄹ에 form tag 생성
# 2-2. 각 input tag 내부에 기존 내용이 들어있어야 함.
# 3. edit 보낸 데이터 처리를 위한 경로 생성
# 3-1. /articles/1/update
# 4. 글 수정 처리를 하는 update view 작성
# 5. 해당 글 상세 페이지로 redirect
# 6. 글 수정을 위한 edit 링크 해당 글 상세 페이지에 생성
# 6-1. {% url 'articles:edit' articles.pk %}
def edit(request, article_pk):
    article = Article.objects.get(pk=article_pk)
    context = {
        'article' : article
    }
    return render(request, 'articles/edit.html', context)

def update(request, article_pk):
    edit_title = request.POST.get('edit_title')
    edit_content = request.POST.get('edit_content')
    article = Article.objects.get(pk=article_pk)
    article.title = edit_title
    article.content = edit_content
    article.save()
    return redirect('articles:detail', article_pk)
# detail.html
{% block body %}
  <h1> 상세 페이지^^</h1>
    <h2>{{article.pk}}번 째 글</h2>
    <h4>제목 : {{article.title}}</h4>
    <h5>내용 : {{article.content}}</h5>
    <p>생성 시간 : {{article.created_at}}</p>
    <p>수정 시간 : {{article.updated_at}}</p>
    <a href="{% url 'articles:edit' article.pk %}">수정</a>
    <a href="{% url 'articles:delete' article.pk %}">삭제</a>
    <a href="{% url 'articles:index' %}">[back]</a>
{% endblock %}

# edit.html
{% block body %}
<form action="{% url 'articles:update' article.pk %}" method="POST">
  {% csrf_token %}
  {{ article.pk }}번 째 글
  제목 : <input type="text" name="edit_title" value="{{ article.title }}">
  내용 : <input type="text" name="edit_content" value="{{ article.content }}">

  <input type="submit" value="수정하기">
</form>

<a href="{% url 'articles:index' %}">[back]</a>
{% endblock %}
반응형