GET과 POST

코딩 연습/Django 2018. 11. 23. 13:51

GET 방식

- 지정한 URL의 정보를 가져온다.

- 웹 브라우저를 이용하여 서버로부터 웹 페이지, 이미지, 동영상 등을 가져오려 할 때 사용하는 방식이다.

- 서버 시스템의 상태를 바꾸지 않는 요청시 사용한다.

단점

- URL의 길이 제한이 있으므로 보낼 수 있는 데이터의 양이 적다

- 전달되는 데이터가 웹 브라우저의 주소창에 노출되어 보안 측면에서 불리하다

장점

- URL에 데이터가 포함되므로 검색 폼에서 유용하다. URL을 북마크해두고 공유하기 좋다.


POST 방식

- 대표적 기능으로 리소스를 생성하는 것이다. 예시로 블로그에 글이나 댓글을 등록하는 경우다.

- 서버 시스템의 상태를 바꾸는 요청시에 사용한다.

블로그 이미지

쵸잇

,

폼을 만드는 과정을 생각해보자


1. 웹사이트에서 사용자를 통해 어떤 데이터를 입력 받을지 고민


나의 목표는 블로그 방문자의 댓글을 입력 받고 이를 데이터베이스에 보관하는 것이다.

댓글의 기록은 모델에서 Comment 클래스 형태로 username 필드content 필드를 가지고 있다.

데이터베이스는 만들어졌으니 입력 받을 수 있는 폼(form)을 만들어야한다. *폼은 일종의 양식이라 보면 된다.

폼을 통해 입력 받을 데이터는 username과 content이다. 댓글을 남기는 사람댓글 내용을 말한다.



2. 템플릿에서 앞서 고민한 내용을 담은 폼을 만든다.


1
2
3
4
5
6
<form method="post" action="">
    {% csrf_token %}
    <input name="username" />
    <input name="content" />
    <button type="submit">댓글달기</button>    
</form>
cs


*폼에서는 CSRF 공격을 방지하기 위해 {% csrf_token %} 작성한다. 폼을 통해 악의적인 스크립트 문장이 들어올 수 있다.


폼 태그를 사용하려면 method 지정과 action 지정이 필요하다.

method는 get과 post 중에 하나인데, 폼은 데이터를 불러오는게 아니라 저장하는 역할을 하므로 post를 사용해야한다.

action은 입력할 데이터를 받을 웹페이지를 지정하는데 현재페이지인 경우 공백을 둔다.

input은 사용자가 데이터를 입력하는 공간을 만들어준다. 

name은 어떤 데이터인지 명시하고, view에서 데이터를 전달하는 매개변수로도 쓰인다.

button은 말그대로 입력을 완료하고 제출할 수 있도록 버튼을 만들어준다.

submit은 버튼을 제출 형태로 사용하도록 하기 위함이다.



1
2
3
4
5
6
7
8
9
10
if request.method == "POST":
    username = request.POST.get("username")
    content = request.POST.get("content")
    Comment.objects.create(
        article=article_id,
        username=username,
       content=content,
    )
 
    return HttpResponseRedirect("/{}/".format(article_id))
cs



GET 방식은 비어있는 폼 양식을 불러오는 역할을 하는데, 아직은 쓰이지 않으므로 생략했다.

view에서는 템플릿을 통해 입력받은 데이터를 데이터베이스에 저장하는 역할을 하므로 POST 방식 사용한다.


request.POST는 웹사이트에서 사용자가 입력하고 제출된 데이터를 갖고 있다.

request.POST.get는 조건에 맞는 값을 하나 가져오는 쿼리로서 username과 content 값을 호출한다. 

이를 각각 username과 content 변수에 담는다.


Comment.objects.create는 Comment 클래스에 새로운 인스턴스를 추가하는 것이다.

Comment 클래스에는 3개의 필드(article, username, content)가 포함되어 있으므로 순서대로 값을 지정한다.

article의 경우에는 url을 통해서 받은 게시글의 id 값이 입력된다. 


HttpResponseRedirect 메소드화면을 자동으로 새로고침 해주어 데이터베이스의 변화를 즉각 반영한다. 


*Comment 클래스의 인스턴스를 만들 때 주의할 점 - 모든 필드에 데이터 값들이 들어가야한다.



블로그 이미지

쵸잇

,

지난 포스트(https://practice-a-lot.tistory.com/42)에서 "related_name" 치트를 활용하는 방법을 보았다.


그에 대한 연장선으로 view에서 comment_list를 만들어줄 필요 없이 템플릿 자체에서 구현하는 방법을 볼 것이다.

먼저 view에서 작성한 comment_list를 삭제한다 


1
2
3
4
5
6
def detail(request, article_id):
    article = Article.objects.get(id=article_id)
    ctx = {
        "article" : article
    }
    return render(request, "detail.html", ctx)
cs



comment_list를 삭제했지만 comment 데이터는 article 인스턴스에 고스란히 담겨있다.

지난 포스트에서 했던 것처럼 템플릿에서도 article 인스턴스에 접근하여 comment_list를 뽑아내면된다. 



1
2
3
4
5
<div>
   {% for comment in article.article_comments.all %}
   <div>{{ comment.username }} : {{ comment.content }}</div>
   {% endfor %}
</div>
cs



뷰에서 삭제한 comment_list의 값을 템플릿에서 그대로 사용했다. all 다음에 ()를 쓰지 않는다. 함수가 아니므로.


Comment 테이블의 article 필드에서 쓰인 "related_name" 치트가 ForeignKey로 연결된 Article 테이블에서 역으로 Comment 테이블에 접근하게 된다.  

블로그 이미지

쵸잇

,

Article

id

title

content

category

hashtag(ManyToMany(Hashtag__name)) 

1

자바

안드로이드 따라하기 시작

dv

java, android

2

파이썬

장고 배달의 민족 클론하기

dv

python, django

(Aritlce 테이블)


단독 게시글 페이지(상세페이지)에서 댓글 리스트를 출력하고 싶은데,

댓글 테이블에서는 게시글이 다대일 관계로 접근이 가능한데, 역으로는 접근가능한 방법이 없다.


이를 해결하는 방법



Comment 

id

article(ForeignKey(Article))

name

content

1

1

Ryan

좋은 시작이야

2

2

Adrianne

I'm so relieved that you've come to your senses 

 3

2

Jack

나는 어렵더라 

 4

1

Paul

Good job man~


Ariticle 테이블과 Comment 테이블은 다대일 관계를 맺고 있다.


테이블을 살펴보면, Comment 테이블에서 Article 테이블로 접근이 가능하다. 

그러므로 Article 테이블의 데이터를 유용하게 옮겨 쓸 수 있다.


그런데, 반대로 Article 테이블에서 Comment 테이블의 데이터를 가져오려해도 접근 방법이 보이지 않는다.


이를 위해 Comment 테이블의 article 필드에서 "related_name" 치트를 사용하여 Article 테이블에서의 접근을 허용한다.



1
2
3
4
5
6
7
8
class Comment(models.Model):
    article = models.ForeignKey(
        Article,
        related_name="article_comments",
        on_delete=models.CASCADE
    )
    name = models.CharField(max_length=50)
    content = models.CharField(max_length=200)
cs


1
2
3
4
5
6
7
def detail(request, article_id):
    article = Article.objects.get(id=article_id)
    comment_list = article.article_comments.all(article__id=article_id)
    ctx = {
        "article" : article,
        "comment_list" : comment_list,
    }
cs


*ORM 사용시 Article 테이블에서 id 필드로 접근하려면 __ (더블언더바)를 반드시 사용해야한다.

블로그 이미지

쵸잇

,

Article 

 id

title

content

category 

 1

놀러와 

오늘 친구들이 놀러왔다

일기 

 2

혼나다 

게임을 많이해서 부모님께 혼이 났다

일기 


Hashtag 

 id

name 

 1

플스4

 2

위닝2019 

 3

중고나라 



게시글은 해시태그를 3개 중에 아무거나 다 써도 된다.

해시태그도 어떤 게시글에 쓰여도 무방하다. 

서로 중복사용 가능하다. 상호종속관계로 이루어져있다.


Ariticle and Hashtag 

id

Article(ForeignKey)

Hashtag(ForeignKey)

 1

1

 2

1

 3

2

 4

2

 5

2


테이블을 새로 만들면 해당 형태로 완성된다.

두 개 필드 모두 FK로 관계를 설정해서 사용해도 되는데, 게시글에 해시태그 필드를 만들어서 ManyToMany 관계를 설정하면 더 간편하다.


1
2
class Hashtag(models.Model):
    name = models.CharField(max_length=50)
cs
1
2
3
class Article(models.Model):
    (생략)
    hashtag = models.ManyToManyField(Hashtag)
cs


해시태그 테이블을 만들었다.

게시글 테이블에 새로운 필드를 만들고, Hashtag 테이블과 ManyToMany관계를 형성한다.


1
2
hashtag = request.GET.get("hashtag")
article_list = Article.objects.filter(hashtag__name=hashtag)
cs


주소창에서 해시태그의 값을 가져와 변수에 담는데,

필터 사용시 일반 필드와 다른 점이 있다.

게시글 테이블의 해시태그 필드는 해시태그 테이블과 ManyToMany관계로 만들어진 것이다.

ORM대로 게시글 테이블에서 바로 접근할 수 있도록 (hashtag=hashtag) 형태로 필터하고싶으나,

인자를 찾지 못하는 오류가 발생하므로,

해시태그 테이블의 name 필드에 접근하는 (hashtag__name=hashtag) 형태로 필터를 한다.



블로그 이미지

쵸잇

,

블로그를 따라 만드는 중에 카테고리 페이지를 구현하는데서 막혔다. 


이를 공부하다보니 카테고리 리스트 구현이 궁금해 뒤돌아가서 익히고,


카테고리 테이블이 별도로 없는 걸 다시 알고 모델도 또 공부했다.


공부하고 잊히고 다시 알게 되는 중이다. 알고보니 내가 대충 공부했던 것이다.


역으로 공부했으니 복습은 정방향으로 기록하겠다.


1. 카테고리는 게시글 테이블에 선택 가능한 필드 형태로 넣는다.


Article

id

title

content

category

1

안녕

놀러와

development / personal 중에 선택


블로그에서 게시글 테이블은 제목, 내용, 카테고리만 입력하도록 만들었다.


단, 카테고리는 둘 중 하나 선택하도록 추가적인 설정을 해주었다. 


choice 필드에 대한 자료(https://docs.djangoproject.com/en/2.1/ref/models/fields/)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Article(models.Model):
    DEVELOPMENT = "dv"
    PERSONAL = "ps"
    CATEGORY_CHOICES = (
         (DEVELOPMENT, "development"),
         (PERSONAL, "personal"),
    )
 
    title = models.CharField(max_length=200)
    content = models.TextField()
    category = models.CharField(
         max_length=2,
         choices=CATEGORY_CHOICES,
         default=DEVELOPMENT
    )
cs


2. View에서 카테고리 리스트를 만든다.


article_list는 ORM으로 모든 게시글 데이터가 담긴 변수이다.

(ORM(Object-Relational Mapping)은 애플리케이션과 데이터베이스 간의 데이터 처리를 추상화하여 다룰 수 있는 편리한 방법)

카테고리는 메소드를 통해 풀네임을 변수에 담았다. 


1
2
3
4
category_list = set([
    article.get_category_display()
    for article in article_list
])
cs


3. Templete에서 카테고리 리스트 매핑하기 


1
2
3
{% for category in category_list %}
<li>{{ category }}</li>
{% endfor %}
cs


아직 링크가 걸려있지 않아서 카테고리로서의 기능은 없다.


4. View에서 해당 카테고리만의 게시글 리스트 만들기


GET 방식은 웹브라우저에서 주소창에 입력하여 서버에 데이터를 요청하는 것이다.


네이버에서 무언가를 검색하면 주소창에 /?query=무언가 형태로 작성된 것이 보이는데,


쿼리스트링의 형태로 서버에 데이터를 요청하여 검색페이지가 출력되는 것이다.


이를 카테고리에 비슷하게 적용할 수 있다.


1
category = request.GET.get("category")
cs


주소창에서 /?category=dv 또는 /?category=ps 형태의 쿼리스트링은 딕셔너리 형태로 구성되어있다. 


여기서 밸류값인 dv 또는 ps를 가져오기 위해 작성한다. 그럼 변수 category에 담길 것이다.


1
article_list = Article.objects.filter(category=category)
cs


변수 값으로 필터하여 카테고리에 따라 게시글 리스트를 만들 수 있게 되었다.


5. 카테고리에 링크를 걸기 위해 전면 수정


1) View에서 카테고리 리스트 수정


1
2
3
4
category_list = set([
    article.category, article.get_category_display()
    for article in article_list
])
cs


축약형을 리스트에 새로 넣었다. 카테고리 주소에서 단축형로 쓰이므로


2) Templete에서 카테고리에 링크 걸기


1
2
3
{% for alias, category in category_list %}
<li><a href="/{{ alias }}/">{{ category }}</a></li>
{% endfor %}
cs


단축형, 정식형 순으로 변수로 지정되었으므로 매핑된 경우에도 순서대로 값이 반영된다.






블로그 이미지

쵸잇

,

UI 설계


테이블 설계 - 모델 코딩(데이터 구조 설계)


뷰 및 템플릿 코딩

- 요청에서부터 응답까지의 처리 흐름에 대한 로직 설계가 필요


URL - View - Template 의 로직 구조 설계


CRUD

Create, Read, Update, Delete


폼 처리하기

- 폼은 사용자로부터 입력을 받기 위해 사용한다

- 서버 시스템의 상태를 건드리냐 아니냐에 따라 POST/GET 방식으로 나뉜다

블로그 이미지

쵸잇

,

장고 개념

코딩 연습/Django 2018. 11. 15. 14:42

모델 - 데이터베이스에 저장되는 데이터

템플릿 - 사용자에게 보여지는 UI(User Interface)

뷰 - 프로그램 로직(데이터를 가져와 필요에 따라 처리한 결과를 템플릿에 전달)


ex) 블로그

모델 - 블로그의 내용을 가져오거나 저장, 수정

뷰 - 버튼을 눌렀을 때 어떤 함수를 호출하여 데이터를 어떻게 가공할 것인지 결정

템플릿 - 화면을 꾸며서 볼 수 있는 페이지를 만들어주는 역할


MVT패턴 처리과정

1) 클라이언트 요청 URL 분석

2) URL 분석을 통해 처리를 담당할 뷰를 결정

3) 뷰는 함수를 호출하여 필요에 따라 모델을 통해 데이터베이스를 처리 받는다

4) 뷰는 로직 처리 후 템플릿을 사용하여 HTML 파일을 클라이언트에게 보낸다


ORM(Object-Relational Mapping) - 객체와 관계형 데이터베이스를 연결해주는 역할

- 데이터베이스 대신에 클래스(객체)를 사용하여 데이터를 처리

- 클래스끼리 작업을 하면 ORM이 자동으로 데이터베이스를 처리


URLconf

- URL과 뷰(함수)를 매핑하는 것


View - 프로그램 로직을 정의해둔다

- 웹 요청에 따라 URL을 분석하고 해당 URL에 매핑된 뷰를 호출

- 웹 요청을 받고, 데이터베이스 처리 후 결과를 템플릿 처리하여 HTML로 된 데이터를 클라이언트로 반환


Template - 화면 UI를 정의한다

- 장고가 클라이언트(웹브라우저)에게 반환하는 최종 응답은 HTML 텍스트이다

- 개발자가 html 파일을 작성하면, 장고는 이를 클라이언트에 보내고, 클라이언트를 해석하여 화면에 보여준다

- 개발자가 작성하는 html 파일을 템플릿이라고 한다


MVT 코딩 순서

- 독립적으로 개발할 수 있는 모델을 먼저 코딩하고, 뷰와 템플릿을 이어서 코딩하는 것이 일반적

- 뷰와 템플릿의 경우는 UI화면을 생각하면서 로직을 풀어나가는 것이 쉬우므로 템플릿을 먼저 코딩하기도 한다

1) 프로젝트 뼈대 만들기

2) 모델 코딩하기 - 테이블 개발(models.py, admin.py)

3) URLconf 코딩하기 - URL과 뷰의 매핑 관계 정의 (urls.py)

4) 템플릿 코딩하기 - 화면 UI 개발(.html)

5) 뷰 코딩하기 - 애플리케이션 로직 개발(views.py)



블로그 이미지

쵸잇

,

가상환경 폴더는 갖고 있는 데이터가 많고 굳이 깃허브에 올릴 필요가 없다.


따라서 깃허브에 등록시 제외시켜도 무방하다.


내가 지정한 가상환경 폴더명은 "my_project"이다.


지난번에 우리는 ".gitignore"라는 파일을 만들어서 "db.sqlite3" 파일을 제외시켰다.


그와 유사하게 ".gitignore" 파일에 "my_project/"라고 "db.sqlite3"가 적힌 목록에 이어서 작성하면 끝이다.


그러면 똑같은 방식으로 "my_project" 폴더가 깃허브 등록시 제외된다.   

'코딩 연습 > Django' 카테고리의 다른 글

모델 만들기에서 카테고리 선택까지  (0) 2018.11.21
애플리케이션 설계하기  (0) 2018.11.16
장고 개념  (0) 2018.11.15
댓글 입력창 만들기 2 (뷰 다루기)  (0) 2018.10.19
장고 설치하기  (0) 2018.10.07
블로그 이미지

쵸잇

,

GET과 POST는 서버와 통신하는 가장 기본방식이다.


GET은 서버에 단순한 정보를 요청하고 데이터를 내줄 때 쓴다.


POST는 서버에 어떠한 데이터를 저장, 수정, 삭제, 연산작용 등을 위해 쓴다.



우리가 그동안 사용한 뷰에서도 GET과 POST 사용에 대해 구분을 해줘야한다.


1
2
3
4
5
6
7
8
9
10
def detail(request, article_id):
    if request.method == "GET":
        article = Article.objects.get(id=article_id)
        hashtag_list = HashTag.objects.all()
        ctx = {
            "article" : article,
            "hashtag_list" : hashtag_list,
            }
    return render(request, "detail.html", ctx)
 
cs

 

모델에서 데이터를 불러오는 코드는 GET으로 구분해주었다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
def detail(request, article_id):
    if request.method == "GET":
        article = Article.objects.get(id=article_id)
        hashtag_list = HashTag.objects.all()
        ctx = {
            "article" : article,
            "hashtag_list" : hashtag_list,
            }
    elif request.method == "POST":
        username = request.POST.get("username")
        content = request.POST.get("content")
 
    return render(request, "detail.html", ctx)
 
cs


어디서 많이 본 형태이다.


1
2
3
def index(request):
    category = request.GET.get("category")
    hashtag = request.GET.get("hashtag")
cs


index뷰에서 category와 hashtag를 가져올 때 사용했던 GET방식이다.

이를 참고하여 POST방식을 만든 것이다.


그리고 GET방식과 POST방식의 차이점은 GET에서는 데이터를 전부 가져오거나 필터처리했는데,

POST에서는 데이터를 저장하므로 인스턴스를 불러오는 게 아니라 인스턴스를 새로 만든다.


1
2
3
4
5
6
7
8
    elif request.method == "POST":
        username = request.POST.get("username")
        content = request.POST.get("content")
        Comment.objects.create(
            article=article,
            username=username,
            content=content,
        )
cs


Comment 클래스에 인스턴스를 만드는 코드이다. create를 입력하여 우리가 입력한 데이터를 저장시킨다.



우리가 흔히 쓰는 댓글창은 내용 입력 후 자동으로 댓글이 등록된 화면이 나타나는데,

이 기능을 적용하기 위해서는 작업이 필요하다.


1
from django.http import HttpResponseRedirect
cs


1
2
3
4
5
6
7
8
9
10
    elif request.method == "POST":
        username = request.POST.get("username")
        content = request.POST.get("content")
        Comment.objects.create(
            article=article,
            username=username,
            content=content,
        )
 
        return HttpResponseRedirect("/{}/".format(article_id))
cs


HttpResponseRedirect 메소드를 import한다. 이 메소드가 자동리프레시를 시켜준다.


1
return HttpResponseRedirect("/{}/".format(article_id))
cs


페이지 주소는 현재페이지를 사용해야하므로 format 함수를 이용해 article_id를 넘겨주어 페이지를 설정한다.

블로그 이미지

쵸잇

,