멋쟁이사자처럼 장고 강의 정리

2022. 5. 19. 16:53해커톤, 개인 프로젝트/멋쟁이사자처럼 10기 해커톤

반응형

장고에 필요한 파이썬의 핵심 문법들

1. 자료형(딕셔너리)

  • 대응이 되는 데이터를 표현해주고 싶을 때 사용

 

2. 예외처리

  • 문법 에러(파싱 에러) : 실행 자체에 영향을 주는 치명적인 오류
  • 예외 : 실행중 감지되는 오류, 프로그램 실행 자체를 멈추지는 않는 오류
  • 프로그램을 에러로 인해 멈춤 없이 사용하고 싶을 때 사용
 try:
 	# 일단 시도해 볼 것
        # 오류가 생길 여지가 있는 코드
 except 발생 오류:
 	# 발생 오류가 발생했을 때 실행할 코드
 finally:
 	#예외가 발생했든 안했든 최종적으로 실행할 코드

 

3. 클래스와 객체

  • 이 세상에 있는 것들은 모두 상태와 동작으로 나타낼 수 있다.
  • 상태 -> 변수, 동작 -> 함수
  • 상태와 동작을 한번에 여러 개 정의할 수 있는 방법 -> 객체 지향 프로그래밍
class Panda():
	hp = 10
    
    def move():
    	pass
    
    def attack():
    	pass
        
        
 # 객체
 
 panda1 = Panda()
 panda2 = Panda()
 panda3 = Panda()

 

 

4. 모듈, 패키지, 라이브러리

  • 모듈 -> 파이썬으로 정의된 파일 ex) a.py, b.py
# a.py
def sum(a, b):
	return a+b
    

# b.py
import a
a.sum(1, 2)
  • 패키지 -> 모듈의 집합(폴더), 모듈의 계층 단위
# a.py가 data 폴더에 있을 때 data == 패키지
def sum(a, b):
	return a+b
    

# b.py
import data.a
a.sum(1, 2)
  • 라이브러리 -> 쓸 만한 기능들을 미리 모듈 / 패키지로 만들어 놓은 것 ex) 내장함수, pip
  • 사람들이 만든 라이브러리는 PyPI에 저장되고, 라이브러리를 설치할 때 pip를 이용한다.

 

웹 서비스란?

  • 웹 -> 정보의 그물망
  • URL -> 정보 자원이 어디있는지 나타내는 표식
  • HTTP -> 정보 자원으로 접근하고 통신하게 해 주는 약속
    • GET -> 정보 자원을 가져다줘!
    • POST -> 이 데이터를 처리해줘!
  • HTML -> 응답으로서의 정보 자원 자체, a태그를 통한 다른 정보 자원가 연결 매개체
  • Server -> 요청을 예상하고, 그에 따른 정보 자원을 미리 저장해놓고 제공하는 것
  • 웹 서비스란? HTML과 URL을 미리 준비해놓고, 사용자 요청에 대한 응답을 보낼 수 있는 프로그램

 

웹 프레임워크란?

  • 프레임워크 -> 복잡한 문제를 해결하거나 서술하는데 사용되는 기본 개념 구조
  • 더 쉬운 말로 정형화 되어있는 웹 개발을 반복되는 코드 작업을 효율적으로 하기위해 미리 만들어 놓은 웹개발의 기능, 설계 단위의 집합
  • 프레임워크(django) -> 명확한 목적을 달성하기 위해 이미 설계까지 만들어진 구조, 을 무조건 지켜야함
  • 라이브러리(react) -> 도구의 모음

 

MVC, MTV란?

  • MVC 디자인 패턴(장고에서는 MTV 패턴이라고 부름) 
    • 1. DB와 상호작용하는 부분 -> model, model(장고 기준)
    • 2. 사용자들 눈에 보이는 부분 -> view, templates(장고 기준)
    • 3. 내부 동작의 논리를 담당하는 부분 -> controller, view(장고 기준)

https://beomy.tistory.com/43

 

[디자인패턴] MVC, MVP, MVVM 비교

웹 개발자로 일을 하면서 가장 먼저 접한 디자인패턴이 바로 MVC 패턴이었습니다. 그만큼 유명하고 많이 쓰이는 디자인패턴인 MVC 패턴과 MVC 패턴에서 파생되어져 나온 MVP 패턴과 MVVM 패턴을 이야

beomy.tistory.com

 

가상환경이란?

  • 독립적인 개발환경 만들기
    • 버젼 충돌이나 여러 상황들로부터 독립적인 환경을 만들어줌.
    • 1. 가상환경 생성
      • python -m venv 가상환경이름
    • 2. 가상환경 실행
      • source myvenv/Scripts/activate
    • 3. 가상환경 끄기
      • deactivate

 

 

장고의 기본 파일들

__init__.py란

  • __init__.py가 위치한 곳이 패키지라는 것을 알려주는 파일

 

urls.py란

  • url들을 매핑해주고, 관리해주는 곳

 

장고 프로젝트 시작하기

django-admin startproject 프로젝트명

 

 

manage.py란

  • 1. 서버 켜기
python manage.py runserver
  • 2. Application 만들기
    • Application : 하나의 장고 프로젝트를 이루는 단위 ex) 결제기능앱, 게시판기능앱
python manage.py startapp 앱이름
  • 3. Database 초기화 및 변경사항 반영
python manage.py migrate
  • 4. 관리자 계정 만들기
python manage.py createsuperuser

 

settings.py란

  • 여러 설정값을 저장하고, 지정하는 곳

 

 

url을 효율적으로 매핑시키는 방법

  • include를 사용하는 방법 ex) product로 시작하는 url은 product app의 urls.py에서 담당할 것이다.
path('product/', include('product.urls'), name='product'),

 

product app의 아래 url은 product/first/로 매핑된다.

urlpatterns = [
    path('first/', views.products, name='products'),
]

 

웹 서비스 내부 데이터

  • Static : 미리 준비된 데이터
  • Media : 업로드 된 데이터

 

static 파일을 관리 하는 법

 

  • STATICFILES_DIRS : static 파일들의 경로 작성

static 파일들을 하나의 폴더에서 관리 할 때

STATICFILES_DIRS = [
    BASE_DIR / 'static',  
]

# 최상위 디렉토리 아래 static 폴더를 만들겠다는 의미입니다.

 

static 파일들을 앱마다 각자의 폴더에서 관리 할 때

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, '앱이름', 'static'),  
    os.path.join(BASE_DIR, '앱이름', 'static'),  
    os.path.join(BASE_DIR, '앱이름', 'static')  
]

 

  • STATIC_ROOT : 배포시 static 파일들을 복사하여 모아 놓을 경로
STATIC_ROOT = os.path.join('staticfiles')
python manage.py collectstatic
  • STATIC_URL : static 파일을 제공할 url
STATIC_URL = '/static/'

 

 

장고에서는 최상위 디렉토리는 Base Directory라고 부른다.

 

 

템플릿에서 staic 폴더 안의 것을 사용하는 방법

{% load static %} <--- {% %}은 템플릿에서 장고 기능을 사용할 수 있는 템플릿 언어이다.
<link rel ="stylesheet" type="text/css" href="{% static 'css/style.css' %}">

 

 

템플릿 언어란?

<a href="{% url 'url name' %}"></a>

 

 

템플릿 상속이란?

  • 반복되는 html의 구문을 줄이기 위해서 사용한다.
  • 주로 부트스트랩의 Nav Var을 사용할 때 사용한다.

 

부모 html

          중복되는 코드 입력


{% block content %}
	자식의 html의 코드가 들어가는 부분
{% endblock %}


	중복되는 코드 입력

 

자식 html

{% extends '부모.html' %}

{% block content %}
	자식 html만의 코드 작성
{% endblock %}

 


 

Model 

 

https://devdongbaek.tistory.com/65

 

Django의 모델에 대해서

출처 : https://www.inflearn.com/course/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%9E%A5%EA%B3%A0-%EC%9B%B9%EC%84%9C%EB%B9%84%EC%8A%A4/dashboard 장고(Django)를 배우기 시작한 입문자이시거나, 또는 배우고 싶은 생..

devdongbaek.tistory.com

 

모델 활용 순서

Django model을 통해, 데이터베이스 형상을 관리할 경우

  1. 모델 클래스 작성
  2. 모델 클래스로부터 변경사항이 담긴 마이그레이션 파일 생성 -> makemigrations 명령
  3. 변경사항이 있는 마이그레이션 파일을 데이터베이스에 적용 -> migrate 명령

 

migrate -> 초기화, 변경사항 적용

 

admin 사이트에서 object 1, 2, 3이 아닌 object 이름으로 표시되게 하는 법

class Blog(models.Model):
    title = models.CharField(max_length=200)
    body = models.TextField()
    date = models.DateTimeField(auto_now_add=True)
    
    def __str__(self):
        return self.title

 


 

Html 폼 이용하기

https://devdongbaek.tistory.com/81

 

Django HTML Form

출처 : https://www.inflearn.com/course/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%9E%A5%EA%B3%A0-%EC%9B%B9%EC%84%9C%EB%B9%84%EC%8A%A4/dashboard 장고(Django)를 배우기 시작한 입문자이시거나, 또는 배우고 싶은 생..

devdongbaek.tistory.com

 

<form action="{% url 'create' %}" method="POST">
    {% csrf_token %}
    <div>
        <label for="title">제목</label><br/>
        <input type="text" name="title" id="title">
    </div>
    <div>
        <label for="body">제목</label><br/>
        <textarea name="body" id="body" cols="30" rows="10"></textarea>
    </div>  
</form>
  • 위 코드에서는 팀 이름을 입력하기 위한 텍스트 필드를 단지 한개만 가지는데, 폼이 가질수 있는 입력 요소와 관련 라벨의 갯수에는 제한이 없다. 필드의  type 속성은 어떤 종류의 위젯이 표시될지 정의한다.  필드의 name과 id 가 JavaScript/CSS/HTML에 있는 필드를 확인하는데 사용되고 value는 필드가 처음 표시될 때의 초기값을 정의한다. 관련 팀 라벨은 label태그(  위 코드에서 "제목 : "을 확인)를 이용해 명시된다.  여기서 for필드는 관련된 input의  id값을 포함하고 있다. 
  • submit 타입의 input 태그는 (기본적으로) 사용자가 누를수 있는 버튼으로 표시되는데, 버튼의 동작에 의해 폼의 다른 모든 input 요소의 데이터가 서버로 업로드된다.  폼 속성으로는 데이터를 보내기 위해 사용되는 HTTP method와 서버상에서 데이타의 목적지를 ( action으로) 정의한다:

 

html form을 database에 저장하는 로직

from django.shortcuts import render, redirect
from django.utils import timezone
from .models import Blog

def home(request):
    return render(request, 'index.html')

# 블로그 글을 저장하는 함수
def create(request):
    if(request.method == 'POST'):
        post = Blog()
        post.title = request.POST['title']
        post.body = request.POST['body']
        post.date = timezone.now()
        post.save()    
    
    return redirect('home')
  • render 는 템플릿을 불러오고, redirect 는 URL로 이동합니다. URL 로 이동한다는 건 그 URL 에 맞는 views 가 다시 실행될테고 여기서 render 를 할지 다시 redirect 할지 결정할 것 입니다. 이 점에 유의해서 사용하신다면 상황에 맞게 사용하실 수 있을 겁니다.

 


 

Django Form

Html Form의 기본 형태

<form action=”데이터가 전달될 주소(요청/이동할 주소)” method=”http 요청 방식"> <input type=”text” name=”title”/>
<button type=”submit”>입력</button>
</form>

form이라는 태그 내에 input 태그, button 태그들로 구성되어 있고 form이 시작되는 form 태그 내부에서는 action과 method의 속성을 기술하도록 되어있다.

Action

입력되는 정보들을 받는 url

Method

HTTP 요청 방식에는 GET / POST / PUT / DELETE 가 있습니다. 보통은 GET / POST를 사용한다.

GET

주소가 노출되어도 괜찮고, 다른 사용자에게 공유가 가능한 정보를 처리할 때 사용

POST

회원가입이나 결제와 같은 다른 사용자와 결제해서는 안되는 정보를 처리할 때 사용

장고에서는 폼을 구성하기 쉽도록 프레임워크단에서 지원하고 있다. 특징은 다음과 같다.

  • 모델 클래스의 모델 정보들과 연동할 수 있다. (binding)
  • Validation 체크 (입력된 정보들의 유효성 검사)를 쉽게 해준다.
  • 악의적인 데이터를 필터링 한다. (sanitisation)
  • 짧고 간결한 코드로 폼 인터페이스를 구현한다.

https://devdongbaek.tistory.com/83

 

Django를 더 Django스럽게 만들어주는 Form

출처 : https://www.inflearn.com/course/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%9E%A5%EA%B3%A0-%EC%9B%B9%EC%84%9C%EB%B9%84%EC%8A%A4/dashboard 장고(Django)를 배우기 시작한 입문자이시거나, 또는 배우고 싶은 생..

devdongbaek.tistory.com

 

forms

from django import forms
from .models import Blog

class BlogForm(forms.Form):
    # 내가 입력받고자 하는 값들
    title = forms.CharField()
    body = forms.CharField(widget=forms.Textarea)

 

views

# django form을 이용해서 입력값을 받는 함수
# GET 요청(= 입력값을 받을 수 있는 html을 가져다 줌)
# POST 요청(= 입력한 내용을 데이터베이스에 저장. form에서 입력한 내용을 처리)
# 둘 다 처리가 가능한 함수
def formcreate(request):
    # 입력 내용을 DB에 저장
    if request.method == 'POST':
        # BlogForm 객체를 만들고 인자로 입력한 값들을 넘김
        form = BlogForm(request.POST, request.FILES) 
        # 유효성 검사에 통과했다면
        if form.is_valid():
            post = Blog()
            post.title = form.cleaned_data['title']
            post.body = form.cleaned_data['body']
            # DB에 최종 저장
            post.save()
            return redirect('home')
        
    # 입력내용을 받을 수 있는 html 전달
    else:
        form = BlogForm()
    
    # render()의 세번째 인자로 views.py의 데이터를 딕셔너리 자료형으로 html에 넘겨줄 수 있다.
    return render(request, 'form_create.html', {'form':form} )

 

 

Django ModelForm

https://devdongbaek.tistory.com/87

 

Django ModelForm이란?

출처 : https://www.inflearn.com/course/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%9E%A5%EA%B3%A0-%EC%9B%B9%EC%84%9C%EB%B9%84%EC%8A%A4/dashboard 장고(Django)를 배우기 시작한 입문자이시거나, 또는 배우고 싶은 생..

devdongbaek.tistory.com

 

forms

class BlogModelForm(forms.ModelForm):
    class Meta:
        model = Blog
        #fields = '__all__'
        fields = ['title', 'body']

 

views

def modelformcreate(request):
    # 입력 내용을 DB에 저장
    if request.method == 'POST':
        # BlogForm 객체를 만들고 인자로 입력한 값들을 넘김
        form = BlogModelForm(request.POST, request.FILES) 
        # 유효성 검사에 통과했다면
        if form.is_valid():
            # DB에 최종 저장
            form.save()
            return redirect('home')
        
    # 입력내용을 받을 수 있는 html 전달
    else:
        form = BlogModelForm()
    
    # render()의 세번째 인자로 views.py의 데이터를 딕셔너리 자료형으로 html에 넘겨줄 수 있다.
    return render(request, 'form_create.html', {'form':form} )

 

    <table>
        {{ form.as_ul }}
    </table>
  • {{ 인자.as_감쌀태그 }}를 통해서views로 부터 넘겨받은 form 인자를 감싸는 태그를 자동으로 생성할 수 있다.

 

model 값 update

https://velog.io/@fregataa/django-QuerySet.update-vs-QuerySet.bulkupdate-vs-Model.save

 

[django] model update

어무해django에서 업데이트, 무엇이 다른가?? QuerySet.bulk_update() QuerySet.update() Model.save()

velog.io

 


 

쿼리셋

https://devdongbaek.tistory.com/56

 

동백 // Django 모델을 통한 조회 ( 기초 )Q

 

devdongbaek.tistory.com

https://devdongbaek.tistory.com/55

 

동백 // Django 모델을 통한 조회 ( 기초 )Q

 

devdongbaek.tistory.com

 

QuerySet -> 데이터베이스로부터 전달받은 데이터 객체 목록

https://docs.djangoproject.com/ko/4.0/ref/models/querysets/

 

QuerySet API reference | Django 문서 | Django

Django The web framework for perfectionists with deadlines. Overview Download Documentation News Community Code Issues About ♥ Donate

docs.djangoproject.com

 

쿼리셋은 RDBMS의 SELECT 문을 생각하면 된다.

 

 


디테일 페이지 만들기

 

https://devdongbaek.tistory.com/109

 

Django 포스팅 detail view 구현

출처 : https://www.inflearn.com/course/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%9E%A5%EA%B3%A0-%EC%9B%B9%EC%84%9C%EB%B9%84%EC%8A%A4/dashboard 장고(Django)를 배우기 시작한 입문자이시거나, 또는 배우고 싶은 생..

devdongbaek.tistory.com

 

Model을 작성할 때 primary key를 등록하지 않았다면, Django는 id 값이라는 pk를 스스로 만들어 primary key로 등록해준다.

  • id -> 객체가 데이터베이스에 post 된 순서

 

 

 

<a href="{% url 'url이름' 변수 %}">제목 : {{ i.title }}</a>
  • url  'url이름'  뒤 오는 변수는 주소 url의 함수에 인자로 넘길 값을 의미합니다.
<a href="{% url 'detail' i.id %}">제목 : {{ i.title }}</a>

 

 

프로세스 : 

# id는 매개 변수나 인자 값으로 사용되기에 원하는 이름으로 설정가능

!중요한 것은 views나 url에서 사용하는 id는 매개변수이기에 원하는 이름으로 지정가능('url의 <int>:변수'와

'views의 def detail(request, 변수)' 의 이름이 같다는 전제하에) 그러나 html에서는 반드시 {{ 변수.id }} id나 pk로 적어야 한다.

  

1. url에서 id값이 있는 detail 주소를 받고, detail 함수에 매핑시켜줌

path('detail/<int:id>' , views.detail, name='detail'),

 

2. detail 함수에서 id 값을 인자로 받고, id번째 글을 데이터베이스로부터 가져와서 detail.html으로 전달

def detail(request, id):
    # id 번째 블로그 글을 데이터베이스에서 가져와서 detail.html로 띄워주는 코드
    # get_object_or_404는 첫번째 인자로 특정 객체를 가져올 데이터베이스,
    # 두번째 인자로는 특정 객체의 pk값을 받습니다.
    blog_detail = get_object_or_404(Blog, pk = id)
    
    return render(request, 'detail.html', {'blog_detail':blog_detail} )
  • get_object_or_404()는 id값을 이용해 특정 모델 객체 하나만 가져온다. 

 

3. detail.html에서 해당 id 글의 출력

 

 


Media 파일 업로드

https://devdongbaek.tistory.com/54

 

동백 // Django media 파일을 다루는 방법

 

devdongbaek.tistory.com

 

MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

MEDIA_URL = '/media/'

1. MEDIA_URL = ""

  • 각 media 파일에 대한 URL Prefix  #필드명.url 속성에 의해서 참조되는 설정
  • 파일에 대한 url 접근시에 사용된다~!

 

2. MEDIA_ROOT = ""

  • 파일필드를 통한 저장 시에, 실제 파일을 저장할 ROOT 경로

 

Django에서 media 다루는 순서 요약하기

1. MEDIA_URL 설정

# settings.py
MEDIA_URL = "/media/"

2. MEDIA_ROOT 설정

# settings.py
MEDIA_ROOT = os.path.join(BASE_DIR, "media")

3. URL 지정

# settings.py 경로의 urls.py
from django.conf.urls.static import static
...
if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

4. pillow 패키지 설치

 

5. media 파일 저장하기

    photo = models.ImageField(blank=True, upload_to='instagram/post/%Y/%m/%d')

6. media 파일 사용하기

<img src={post.photo.url} style="width:100px; height:100px" />
<img src={post.photo.path} style="width:100px; height:100px" />
  • url은 Django에서 저장한 사진의 주소이고, path는 실제 사진이 저장된 서버 컴퓨터 내에서의 주소이기에 보안상으로 url 사용 권장

댓글 구현하기

https://devdongbaek.tistory.com/118

 

Django 댓글 구현하기

출처 : https://www.inflearn.com/course/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%9E%A5%EA%B3%A0-%EC%9B%B9%EC%84%9C%EB%B9%84%EC%8A%A4/dashboard 장고(Django)를 배우기 시작한 입문자이시거나, 또는 배우고 싶은 생..

devdongbaek.tistory.com

 

 

class Blog(models.Model):
    title = models.CharField(max_length=200)
    body = models.TextField()
    photo = models.ImageField(blank=True, null=True, upload_to='blog_photo')
    date = models.DateTimeField(auto_now_add=True)
    
    def __str__(self):
        return self.title
    
    
class Comment(models.Model):
    comment = models.CharField(max_length=200)
    date = models.DateTimeField(auto_now_add=True)
    # 1:N 관계에서는 N측에 1의 기본키를 가져와 외래키로 구성한다.
    post = models.ForeignKey(Blog, on_delete=models.CASCADE)
  • 외래키 같은 경우는 데이터베이스 수업 참조

 

 

 

commit=False를 쓰는 이유는?

예를 들어서 필드가 4개가 있는 모델을 모델 폼으로 작성을 받을 때

나는 그 중 1개의 필드만 폼으로 작성하고 싶다!!

이 경우에 view 함수에서 유효성 검사 이후 form.save()를 먼저 하면 나머지 필드들은 값을 받지

못했으므로 오류가 발생합니다!

 

그러기에 우리는 장고에게 잠시만 저장을 미뤄줘~~! 라는 의미로 commit=False를 적어준 다음

나머지 필드들의 값을 지정해주는겁니다!

 

{% for comment in blog_detail.comment_set.all %}
  • 1:N에서 1측의 특정 객체를 참조하고있는 N측의 집합을 모두 가져오고 싶을 때 필드_set.all 사용

 


로그인, 로그아웃 구현하기

 

https://devdongbaek.tistory.com/100

 

Django 로그인, 로그아웃 구현하기

출처 : https://www.inflearn.com/course/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%9E%A5%EA%B3%A0-%EC%9B%B9%EC%84%9C%EB%B9%84%EC%8A%A4/dashboard 장고(Django)를 배우기 시작한 입문자이시거나, 또는 배우고 싶은 생..

devdongbaek.tistory.com

 

<form action="{% url 'login' %}" method="POST">
    {% csrf_token %}
    username : <input type="text" name='username'>
    password : <input type="text" name='password'>
    <br />
    <input type="submit" value='로그인'>
</form>
  • 로그인 form
from django.shortcuts import redirect, render
from django.contrib import auth
from django.contrib.auth.models import User

# Create your views here.

def login(request):
    # POST 요청이 들어오면 로그인 처리를 해줌
    if request.method == 'POST':
        # 클라이언트가 입력한 값을 변수에 저장
        username = request.POST['username']
        password = request.POST['password']
        
        # auth.authenticate()은 사용자가 입력한 username과 password가 데이터에 저장되어 있는지 확인 후 
        # 저장되어 있으면 user 객체를 반환하고, 없다면 None을 반환하는 함수
        user = auth.authenticate(request, username=username, password=password)
        
        if user is not None:
            auth.login(request, user)
            return redirect('home')
        else:
            return render(request, 'home')
                
    # GET 요청이 들어오면 login form을 담고있는 login.html을 띄워주는 역할을 함
    else:
        return render(request, 'login.html')
  • 로그인 함수

 

def logout(request):
    auth.logout(request)
    return redirect('home')
  • 로그아웃 함수

 

LOGIN_REDIRECT_URL = "/"
  • 로그인 성공시 이동할 주소를 settings.py에서 설정 가능

 

 

 

GET과 POST의 차이를 잘 이해하자

  • GET은 유저에게 페이지를 보여줄 때 
  • POST는 유저가 입력한 값을 서버로 전송할 때

 

 

 

 

https://docs.djangoproject.com/en/4.0/ref/contrib/auth/

 

django.contrib.auth | Django documentation | Django

Django The web framework for perfectionists with deadlines. Overview Download Documentation News Community Code Issues About ♥ Donate

docs.djangoproject.com

 

 


익명 게시판

 

게시글 작성자가 익명으로 보이게 하는 것

    <div class="card-body">
        <div class="table-responsive">
            <table class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
                <thead>
                    <tr>
                        <th width='70%'>글 제목</th>
                        <th>작성 날짜</th>
                        <th>작성자</th>
                    </tr>
                </thead>
                <tbody>
                    {% for post in posts %}
                    <tr>
                        <td><a href="#">{{ post.title }}</a></td>
                        <td>{{ post.date }}</td>
                        <td>익명</td>
                    </tr>
                    {% endfor %}
                </tbody>
            </table>
        </div>
    </div>
  • 작성자를 표시 하지 않고 그냥 작성자라고 아예 명시함

 

 

글 제목을 누를 시 이동하게 하는 법

                <tbody>
                    {% for post in posts %}
                    <tr>
                        <td><a href="{% url 'detail' post.id %}">{{ post.title }}</a></td>
                        <td>{{ post.date }}</td>
                        <td>익명</td>
                    </tr>
                    {% endfor %}
                </tbody>

 

<tbody> 태그는 HTML 테이블에서 내용 콘텐츠(body content)들을 하나의 그룹으로 묶을 때 사용합니다.

 

<tbody> 요소는 테이블의 각 영역(header, body, footer)을 명시하기 위해 <thead>, <tfoot> 요소와 함께 사용됩니다.

브라우저는 이러한 요소들을 사용하여 테이블의 헤더나 푸터와는 독립적으로 테이블의 내용만 스크롤되게 할 수 있으며, 여러 페이지에 걸쳐 있는 큰 테이블을 인쇄할 때 각 페이지의 상단과 하단에 테이블의 헤더와 푸터가 모두 인쇄되도록 할 수도 있습니다.

 

<tbody> 요소는 <table> 요소의 자식 요소로써, 반드시 모든 <caption>, <colgroup>, <thead> 요소 다음에 위치해야 합니다. 또한, <tbody> 요소는 반드시 하나 이상의 <tr> 요소를 포함하고 있어야 합니다.

<thead>와 <tbody>, <tfoot> 요소는 기본적으로 웹 페이지의 레이아웃에 전혀 영향을 주지 않지만, 이 요소들의 스타일을 CSS를 사용하여 변경할 수는 있습니다.

 

  • 글들을 표현할 때는 table을 이용하여 목록을 구성

 

https://getbootstrap.com/docs/4.1/content/tables/

 

Tables

Documentation and examples for opt-in styling of tables (given their prevalent use in JavaScript plugins) with Bootstrap.

getbootstrap.com

 

 

댓글 입력받은 값을 데이터에 저장하기

# 댓글 저장
def new_comment(request, pk):
    filled_form = CommentForm(request.POST)
    if filled_form.is_valid():
        filled_form.save(commit=False)
        filled_form.post = get_object_or_404(Post, pk=pk)
        filled_form.save()
    return redirect('detail', pk)
  • commit=False를 통해서 Comment의 외래키를 해당 포스트의 글 pk로 지정  후 최종 저장한다. 

 

 

로그인 실패시 문구가 뜨게 하는 법

{% if form.errors %}
                    <p style="color:red;">아이디, 비밀번호가 일치하지 않습니다. 다시 시도해주세요.</p>
{% endif %}

 

 

클래스 뷰(CBV)를 사용해서 로그인, 로그아웃 구현

from django.contrib.auth.views import LoginView, LogoutView, logout_then_login


login = LoginView.as_view(template_name= "login.html")

#def logout(request):
    #return logout_then_login(request)

logout = LogoutView.as_view(next_page = "login")

 

Form들에 부트스트랩을 적용하기 위해서는 클래스를 적용해야 한다.

from django import forms
from .models import Post, Comment

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = '__all__'
    
    def __init__(self, *args, **kwargs):
        super(PostForm, self).__init__(*args, **kwargs)

        self.fields['title'].widget.attrs = {
            'class': 'form-control',
            'placeholder': "글 제목을 입력해주세요",
            'rows': 20
        }

        self.fields['body'].widget.attrs = {
            'class': 'form-control',
            'placeholder': "글 제목을 입력해주세요",
            'rows': 20,
            'cols' : 100
        }


class CommentForm(forms.ModelForm):
    class Meta:
        model = Comment
        fields = ['comment']

    def __init__(self, *args, **kwargs):
        super(CommentForm, self).__init__(*args, **kwargs)

        self.fields['comment'].widget.attrs = {
            'class': 'form-control',
            'placeholder': "댓글을 입력해주세요",
            'rows': 10
        }
  • widget.attrs은 widget에 클래스를 부여해주고, 아래 처럼 이 클래스를 통해 부트스트랩이나 css를 적용할 수 있습니다.
<input type="text" class="form-control bg-light border-0 small"
                                            placeholder="Search for..." aria-label="Search"
                                            aria-describedby="basic-addon2">

 

 

 

 

 

https://docs.djangoproject.com/en/4.0/ref/forms/widgets/

 

Widgets | Django documentation | Django

Django The web framework for perfectionists with deadlines. Overview Download Documentation News Community Code Issues About ♥ Donate

docs.djangoproject.com

 

 

https://ssungkang.tistory.com/entry/Django-widget-1-widget%EC%9D%98-%EC%9B%90%EB%A6%AC%EC%99%80-widget-%EB%A7%8C%EB%93%A4%EC%96%B4%EB%B3%B4%EA%B8%B0-%EC%8B%A4%EC%8B%9C%EA%B0%84-%EA%B8%80%EC%9E%90%EC%88%98-%ED%91%9C%EC%8B%9C-widget

 

[Django] widget (1) widget의 원리와 widget 만들어보기 - 실시간 글자수 표시 widget

input 태그는 type 속성에 따라 여러 모습을 보여줍니다. text 일 때는 글을 입력할 수 있도록, date 일 때는 날짜를 지정할 수 있도록, password 일 때는 비밀번호를 입력할 수 있도록 하는 등 다양한 type

ssungkang.tistory.com

 

 

자유 게시판 구현하기

from django.contrib.auth.models import User

# 자유 게시물 모델
class FreePost(models.Model):
    title = models.CharField(max_length=200)
    body = models.TextField()
    date = models.DateTimeField(auto_now_add=True)
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    
    def __str__(self):
        return self.title
  • User를 외래키로 삼아서 작성자를 저장

 

def freepostcreate(request):
    if request.method == 'POST' or request.method == 'FILES':
        # BlogForm 객체를 만들고 인자로 입력한 값들을 넘김
        form = FreePostForm(request.POST, request.FILES) 
        # 유효성 검사에 통과했다면
        if form.is_valid():           
            # DB에 최종 저장
            finished_form = form.save(commit=False)
            finished_form.author = request.user
            finished_form.save()
            return redirect('freehome')
        
    # 입력내용을 받을 수 있는 html 전달
    else:
        form = FreePostForm()
    
    # render()의 세번째 인자로 views.py의 데이터를 딕셔너리 자료형으로 html에 넘겨줄 수 있다.
    return render(request, 'free_post_form.html', {'form':form})
  • 현재 유저를 글 작성자로 지정한다.
            finished_form.author = request.user

 

 

회원가입 구현하기

https://devdongbaek.tistory.com/92

 

Django 회원가입, 로그인 구현하기!!!

출처 : https://www.inflearn.com/course/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%9E%A5%EA%B3%A0-%EC%9B%B9%EC%84%9C%EB%B9%84%EC%8A%A4/dashboard 장고(Django)를 배우기 시작한 입문자이시거나, 또는 배우고 싶은 생..

devdongbaek.tistory.com

 

from django.shortcuts import render, redirect
from django.contrib.auth.views import LoginView, LogoutView, logout_then_login
from django.contrib import auth
from django.contrib.auth.models import User

def signup(request):
    if request.method == "POST":
        # 만약 첫 비밀번호와 나중 비밀번호가 같다면
        if request.POST['password'] == request.POST['repeat']:
            # 회원가입
            new_user = User.objects.create_user(username=request.POST['username'], password=request.POST['password'])
            # 로그인
            auth.login(request, new_user)
            # 홈 리다이렉션
            return redirect('home')
    
    # 같지 않다면 다시 회원가입 페이지로       
    return render(request, 'register.html')
                            <form class="user" method="POST">
                                {% csrf_token %}
                                <div class="form-group row">
                                </div>
                                <div class="form-group">
                                    <input type="text" name="username" class="form-control form-control-user" id="exampleInputEmail"
                                        placeholder="Enter Your ID...">
                                </div>
                                <div class="form-group row">
                                    <div class="col-sm-6 mb-3 mb-sm-0">
                                        <input type="password" name="password" class="form-control form-control-user"
                                            id="exampleInputPassword" placeholder="Password">
                                    </div>
                                    <div class="col-sm-6">
                                        <input type="password" name="repeat" class="form-control form-control-user"
                                            id="exampleRepeatPassword" placeholder="Repeat Password">
                                    </div>
                                </div>
                                <br/>
                                <br/>
                                <br/>
                                <input type="submit" class="btn btn-success btn-user btn-block" value="회원가입">
                                <hr>
                            </form>
                            <div class="text-center">
                                <a class="small" href="{% url 'login' %}">Already have an account? Login!</a>
                            </div>

 

 


 

Pagination

https://ssungkang.tistory.com/entry/Django-11-Pagination-%EC%9D%84-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90

 

[Django] 11. Pagination 을 알아보자

안녕하세요 강민성입니다. 이번 시간에도 마찬가지로 blog project를 이어서 진행해보도록 하겠습니다. 글을 계속 추가할수록 글은 아래로 쌓이게 됩니다. 하지만 대부분의 웹사이트는 일정 수준

ssungkang.tistory.com

 

Pagination -> 객체들의 목록을 페이지 별로 나눠서 보여주도록 하는 것

 

from django.core.paginator import Paginator

def home(request):
    # 최신글 기준으로 order_by
    posts = Post.objects.filter().order_by('-date')
    # Paginator 함수의 첫 인자로는 페이징할 객체, 두번째 인자로는 몇 개씩 끉을 것인지
    paginator = Paginator(posts, 10)
    #  page에 해당되는 value를 가져오면 page의 번호를 리턴 받을 수 있습니다.
    pagenum = request.GET.get('page')
    # 해당 pagenum에 해당하는 posts 객체들을 저장
    posts = paginator.get_page(pagenum)
    return render(request, 'index.html', {'posts':posts} )

 

blog_list = Blog.objects.all()

그 전에도 사용한 적 있습니다.  Blog 클래스의 모든 객체들을 blog_list에 담아두게 됩니다.

 

paginator = Paginator(blog_list, 3)

위에서 import한 Paginator입니다.

 

2개의 인자를 받는데 첫 번째로 페이지로 분할될 객체, 두 번째로 한 페이지에 담길 객체의 수를 받습니다.

 

page = request.GET.get('page')

request는 사용자가 보낸 총체적인 정보를 담고 있습니다.

 

어디서 어떤 페이지로 정보를 보내는지 등 말이죠.

 

GET은 request 방식 중 하나 입니다.

 

GET 방식으로 정보를 받아오는 데이터를 가르키게 됩니다.

 

get 은 딕셔너리 자료형에서 key값으로 value를 찾을 때 사용됩니다.

 

그 말인 즉슨, request.GET 으로 받아온 값은 딕셔너리 자료형 이라는 의미죠.

 

ex) http://127.0.0.1:8000/title=first&body=hello

 

{'title':first, "body":hello} 

 

여기서 page에 해당되는 value를 가져오면 page의 번호를 리턴 받을 수 있습니다.

 

posts = paginator.get_page(page)

get_page 메소드는 페이지 번호를 받아 해당 페이지를 리턴하게 됩니다.

 

그 후 이 페이지를 다시 render를 통해 넘겨주게 됩니다.

 

 

 

https://velog.io/@kimkrh/Django-request.GET.get

 

[Django] request.GET.get()

request.get()과 request.GET.get()의 차이점

velog.io

 

 

-> page를 프론트엔드 단에 표시하는 방법

{% extends 'base.html' %}
{% load bootstrap4 %}
{% block content %}
<!-- Page Heading -->
<!-- DataTales Example -->
<div class="card shadow mb-4">
    <div class="card-header py-3">
        <h6 class="m-0 font-weight-bold text-success">익명게시판</h6>
    </div>
    <div class="card-body">
        <div class="table-responsive">
            <table class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
                <thead>
                    <tr>
                        <th width='70%'>글 제목</th>
                        <th>작성 날짜</th>
                        <th>작성자</th>
                    </tr>
                </thead>
                <tbody>
                    {% for post in posts %}
                    <tr>
                        <td><a href="{% url 'detail' post.id %}">{{ post.title }}</a></td>
                        <td>{{ post.date }}</td>
                        <td>익명</td>
                    </tr>
                    {% endfor %}
                </tbody>
            </table>
        </div>
    </div>
</div>
{% if user.is_authenticated %}
    <a href="{% url 'postcreate' %}" class="btn btn-success btn-icon-split">
        <span class="icon text-white-50">
            <i class="fas fa-flag"></i>
        </span>
        <span class="text">글쓰기</span>
    </a>
{% endif %}

<!-- 페이지네이션 -->


{% bootstrap_pagination posts size="small" justify_content="center" %}


<a href="?page=1">첫 페이지</a>
{% if posts.has_previous %}
    <a href="?page={{ posts.previous_page_number }}">이전 페이지</a>
{% endif %}

<span>현재 페이지 : {{ posts.number }}</span>
<span>/</span>
<span>{{ posts.paginator.num_pages }}</span>

{% if posts.has_next %}
    <a href="?page={{ posts.next_page_number }}">다음 페이지</a>
    <a href="?page={{ posts.paginator.num_pages }}">마지막 페이지</a>
{% endif %}

{% endblock %}

 

 


외부 DB 연동하기

mariaDB를 Django와 연동하기

 

1. mariaDB 설치

  • ! root 계정 비밀번호와 port번호가 3306인지 확인하기
  • mariaDB 설치시 자동으로 HeidiSQL이 설치되는데 이는 mariaDB안 테이블들을 쉽게 확인할 수 있는 SQL이다.

https://mariadb.com/kb/en/mariadb-server-10-6-8/

 

MariaDB Server 10.6.8

<div class="pdl-cta"> Thank you for downloading. Create your MariaDB account to receive download release notifications, product updates an...

mariadb.com

 

2. Django와 연동하기 위해서 mysqlclient를 설치해줘야함

pip install mysqlclient

https://victorydntmd.tistory.com/275

 

[Python3.6] mysqlclient 설치 에러 해결

윈도우 환경에서 python mysql을 사용하기 위해 pip install mysqlclient 으로 mysql을 설치하려고 하면, 아래와 같은 에러들이 발생하는 경우가 있습니다. 1. 이 메시지는 윈도우에서 Python module을 설..

victorydntmd.tistory.com

https://lemontia.tistory.com/756

 

[python-pip] mysqlclient 설치 중 에러날때(mysql.h)

pip3로 mysqlclient를 설치할때 다음의 에러가 발생할 수 있습니다. mysql.c _mysql.c(29): fatal error C1083: 포함 파일을 열 수 없습니다. 'mysql.h': No such file or directory error: command 'C:\\Program..

lemontia.tistory.com

 

3. settings.py에 DB 관련 설정

MYDATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'mysql',
        'USER': 'root',
        'PASSWORD': '루트계정암호',
        'HOST': '127.0.0.1',
        'PORT': '3306'
    }
}

 

! settings.py의 DB PASSWORD와 SECRET_KEY는 절대 외부에 노출되어서는 안된다.

그래서 보통 my_settings.py라는 파일을 만들고 그곳에 DB PASSWORD와 SECRET_KEY를 저장하고 import한다.

from .my_settings import MYSECRET_KEY, MYDATABASES

SECRET_KEY = MYSECRET_KEY
DATABASES = MYDATABASES

 

 


 

소셜 로그인

https://django-allauth.readthedocs.io/en/latest/installation.html

 

Installation — django-allauth 0.43.0 documentation

Post-Installation In your Django root execute the command below to create your database tables: Now start your server, visit your admin pages (e.g. http://localhost:8000/admin/) and follow these steps: Add a Site for your domain, matching settings.SITE_ID

django-allauth.readthedocs.io

1. 패키지를 설치

$ pip install django-allauth

 

2. settings.py에 등록


INSTALLED_APPS = [
    ...
    # The following apps are required:
    'django.contrib.auth',
    'django.contrib.messages',
    'django.contrib.sites',

    'allauth',
    'allauth.account',
    'allauth.socialaccount',
    # ... include the providers you want to enable:
    'allauth.socialaccount.providers.agave',
    'allauth.socialaccount.providers.amazon',
    'allauth.socialaccount.providers.amazon_cognito',
    'allauth.socialaccount.providers.angellist',
    'allauth.socialaccount.providers.apple',
    'allauth.socialaccount.providers.asana',
    'allauth.socialaccount.providers.auth0',
    'allauth.socialaccount.providers.authentiq',
    'allauth.socialaccount.providers.azure',
    'allauth.socialaccount.providers.baidu',
    'allauth.socialaccount.providers.basecamp',
    'allauth.socialaccount.providers.battlenet',
    'allauth.socialaccount.providers.bitbucket',
    'allauth.socialaccount.providers.bitbucket_oauth2',
    'allauth.socialaccount.providers.bitly',
    'allauth.socialaccount.providers.box',
    'allauth.socialaccount.providers.cern',
    'allauth.socialaccount.providers.cilogon',
    'allauth.socialaccount.providers.clever',
    'allauth.socialaccount.providers.coinbase',
    'allauth.socialaccount.providers.dataporten',
    'allauth.socialaccount.providers.daum',
    'allauth.socialaccount.providers.digitalocean',
    'allauth.socialaccount.providers.discord',
    'allauth.socialaccount.providers.disqus',
    'allauth.socialaccount.providers.douban',
    'allauth.socialaccount.providers.doximity',
    'allauth.socialaccount.providers.draugiem',
    'allauth.socialaccount.providers.drip',
    'allauth.socialaccount.providers.dropbox',
    'allauth.socialaccount.providers.dwolla',
    'allauth.socialaccount.providers.edmodo',
    'allauth.socialaccount.providers.edx',
    'allauth.socialaccount.providers.eventbrite',
    'allauth.socialaccount.providers.eveonline',
    'allauth.socialaccount.providers.evernote',
    'allauth.socialaccount.providers.exist',
    'allauth.socialaccount.providers.facebook',
    'allauth.socialaccount.providers.feedly',
    'allauth.socialaccount.providers.figma',
    'allauth.socialaccount.providers.fivehundredpx',
    'allauth.socialaccount.providers.flickr',
    'allauth.socialaccount.providers.foursquare',
    'allauth.socialaccount.providers.frontier',
    'allauth.socialaccount.providers.fxa',
    'allauth.socialaccount.providers.gitea',
    'allauth.socialaccount.providers.github',
    'allauth.socialaccount.providers.gitlab',
    'allauth.socialaccount.providers.globus',
    'allauth.socialaccount.providers.google',
    'allauth.socialaccount.providers.gumroad',
    'allauth.socialaccount.providers.hubic',
    'allauth.socialaccount.providers.instagram',
    'allauth.socialaccount.providers.jupyterhub',
    'allauth.socialaccount.providers.kakao',
    'allauth.socialaccount.providers.keycloak',
    'allauth.socialaccount.providers.lemonldap',
    'allauth.socialaccount.providers.line',
    'allauth.socialaccount.providers.linkedin',
    'allauth.socialaccount.providers.linkedin_oauth2',
    'allauth.socialaccount.providers.mailchimp',
    'allauth.socialaccount.providers.mailru',
    'allauth.socialaccount.providers.mediawiki',
    'allauth.socialaccount.providers.meetup',
    'allauth.socialaccount.providers.microsoft',
    'allauth.socialaccount.providers.naver',
    'allauth.socialaccount.providers.nextcloud',
    'allauth.socialaccount.providers.odnoklassniki',
    'allauth.socialaccount.providers.openid',
    'allauth.socialaccount.providers.openstreetmap',
    'allauth.socialaccount.providers.orcid',
    'allauth.socialaccount.providers.patreon',
    'allauth.socialaccount.providers.paypal',
    'allauth.socialaccount.providers.persona',
    'allauth.socialaccount.providers.pinterest',
    'allauth.socialaccount.providers.pocket',
    'allauth.socialaccount.providers.quickbooks',
    'allauth.socialaccount.providers.reddit',
    'allauth.socialaccount.providers.robinhood',
    'allauth.socialaccount.providers.salesforce',
    'allauth.socialaccount.providers.sharefile',
    'allauth.socialaccount.providers.shopify',
    'allauth.socialaccount.providers.slack',
    'allauth.socialaccount.providers.snapchat',
    'allauth.socialaccount.providers.soundcloud',
    'allauth.socialaccount.providers.spotify',
    'allauth.socialaccount.providers.stackexchange',
    'allauth.socialaccount.providers.steam',
    'allauth.socialaccount.providers.stocktwits',
    'allauth.socialaccount.providers.strava',
    'allauth.socialaccount.providers.stripe',
    'allauth.socialaccount.providers.telegram',
    'allauth.socialaccount.providers.trainingpeaks',
    'allauth.socialaccount.providers.trello',
    'allauth.socialaccount.providers.tumblr',
    'allauth.socialaccount.providers.twentythreeandme',
    'allauth.socialaccount.providers.twitch',
    'allauth.socialaccount.providers.twitter',
    'allauth.socialaccount.providers.untappd',
    'allauth.socialaccount.providers.vimeo',
    'allauth.socialaccount.providers.vimeo_oauth2',
    'allauth.socialaccount.providers.vk',
    'allauth.socialaccount.providers.weibo',
    'allauth.socialaccount.providers.weixin',
    'allauth.socialaccount.providers.windowslive',
    'allauth.socialaccount.providers.xing',
    'allauth.socialaccount.providers.yahoo',
    'allauth.socialaccount.providers.yandex',
    'allauth.socialaccount.providers.ynab',
    'allauth.socialaccount.providers.zoho',
    'allauth.socialaccount.providers.zoom',
    'allauth.socialaccount.providers.okta',
    'allauth.socialaccount.providers.feishu',
    ...
]

SITE_ID = 1

 

3. urls.py에 등록

urlpatterns = [
    ...
    path('accounts/', include('allauth.urls')),
    ...
]

 

4. migrate 진행

 

5. admin단 수정

 

https://console.cloud.google.com/welcome?project=northern-center-355407 

 

Google 클라우드 플랫폼

로그인 Google 클라우드 플랫폼으로 이동

accounts.google.com

  • client id와 Secret Key는 구글 콘솔 사이트에서 입력받음

1. 새 프로젝트 생성하기

2. API 및 서비스에 들어간 후 사용자 인증 정보 만들기 -> OAuth 클라이언트 ID 만들기 -> 사용자 인증정보 만들기 -> OAuth 클라이언트 ID 만들기 -> 어플리케이션 유형(웹 어플리케이션), 승인된 자바스크립트 원본(http://127.0.0.1:8000), 승인된 리다이렉션(http://127.0.0.1:8000/accounts/google/login/callback/)

3. 클라이언트 아이디와 비밀번호를 admin에 이어서 작성

 

 

6. settngs.py 이어서 작성

# 어떤 수단을 통해서 로그인 할 것인지에 대해서
AUTHENTICATION_BACKENDS = [
   
    # Needed to login by username in Django admin, regardless of `allauth`
    # 기존 장고의 내장되어있는 인증 기능
    'django.contrib.auth.backends.ModelBackend',

    # `allauth` specific authentication methods, such as login by e-mail
    # 소셜 로그인 기능
    'allauth.account.auth_backends.AuthenticationBackend',
    
]

LOGIN_REDIRECT_URL = '/'

 

7.템플릿단 구현

{% load socialaccount %}

<a href="{% provider_login_url 'google' %}" class="btn btn-google btn-user btn-block">
                                        <i class="fab fa-google fa-fw"></i> 구글 계정으로 가입 </a>

 


api 연동하기

https://developers.naver.com/docs/serviceapi/search/blog/blog.md#%EB%B8%94%EB%A1%9C%EA%B7%B8

 

블로그 - Search API

블로그 NAVER Developers - 검색 API 블로그 검색 개발가이드 검색 > 블로그 네이버 블로그 검색 결과를 출력해주는 REST API입니다. 비로그인 오픈 API이므로 GET으로 호출할 때 HTTP Header에 애플리케이션

developers.naver.com

 

!api 문서를 보는게 매우 중요


1.  API를 사용하기 위해서는 오픈 API 이용 신청을 하는게 중요 -> 클라이언트 ID와 클라이언트 Secret Key를 받기 위해서

 

2. API를 응답받을 URL을 지정 ex) https://openapi.naver.com/v1/search/movie.json

 

3. 요청 변수는 위 URL 뒤에 ?key='value' 에서 key이며 value를 가져온다. 

 

4. 필수 요청 변수는 반드시 적어주어야 한다. ex)query

 

API 호출 예시

curl "https://openapi.naver.com/v1/search/movie.xml?query=%EC%A3%BC%EC%8B%9D&display=10&start=1&genre=1" \
    -H "X-Naver-Client-Id: {애플리케이션 등록 시 발급받은 client id 값}" \
    -H "X-Naver-Client-Secret: {애플리케이션 등록 시 발급받은 client secret 값}" -v
  • 한글이 url에 표시될때는 %EC%로 표시된다.
  • &은 and를 의미한다.

 

API 요청 예시

> GET /v1/search/movie.xml?query=%EC%A3%BC%EC%8B%9D&display=10&start=1&genre=1 HTTP/1.1
> Host: openapi.naver.com
> User-Agent: curl/7.49.1
> Accept: */*
> X-Naver-Client-Id: {애플리케이션 등록 시 발급받은 client id 값}
> X-Naver-Client-Secret: {애플리케이션 등록 시 발급받은 client secret 값}

 

API 응답 예시

< HTTP/1.1 200 OK
< Server: nginx
< Date: Wed, 28 Sep 2016 07:40:17 GMT
< Content-Type: text/xml;charset=utf-8
< Transfer-Encoding: chunked
< Connection: keep-alive
< Keep-Alive: timeout=5
< Vary: Accept-Encoding
< X-Powered-By: Naver
< Cache-Control: no-cache, no-store, must-revalidate
< Pragma: no-cache
<
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>Naver Open API - movie ::'주식'</title>
        <link>http://search.naver.com</link>
        <description>Naver Search Result</description>
        <lastBuildDate>Wed, 28 Sep 2016 16:40:17 +0900</lastBuildDate>
        <total>2</total>
        <start>1</start>
        <display>2</display>
        <item>
            <title>주마등&lt;b&gt;주식&lt;/b&gt;회사</title>
            <link>http://openapi.naver.com/l?AAADWLQQvCIBzFP83f48h0zh08uK1B0S2IOm7mUEIts0F9+vQQPN77vQfv+dbxI2DXgyTQ9QV4B+2ATNSLMCk9gEjYjlkurFZXflp1rFRw/yXnbEspNk8vqypvPJBRhZsGMrSMY4ySwLSpN5RT3NSYIScO5nxhdzc18cjq0958w8LneKUy8fz6AdRxjD6YAAAA</link><image>http://imgmovie.naver.com/mdi/mit110/0968/96811_P01_142155.jpg</image>
            <subtitle>走馬&amp;amp;#28783;株式&amp;amp;#20250;社</subtitle>
            <pubDate>2012</pubDate>
            <director>미키 코이치로|</director>
            <actor>카시이 유우|쿠보타 마사타카|카지와라 히카리|치요 쇼타|요코야마 메구미|카시와바라 슈지|</actor>
            <userRating>4.50</userRating>
        </item>
        ...
    </channel>
</rss>

 


 

파이썬 API 호출 예제

# 네이버 검색 API예제는 블로그를 비롯 전문자료까지 호출방법이 동일하므로 blog검색만 대표로 예제를 올렸습니다.
# 네이버 검색 Open API 예제 - 블로그 검색
import os
import sys

# 특정 url에 request를 보낼 수 있도록 해주는 라이브러리
import urllib.request

# API 이용 신청을 통해 받은 id와 secret 입력
client_id = "YOUR_CLIENT_ID"
client_secret = "YOUR_CLIENT_SECRET"

# 검색할 단어를 입력받을 변수
# urllib.parse.quote()는 아스키코드 형식이 아닌 글자를 URL 인코딩 시켜줍니다.
encText = urllib.parse.quote("검색할 단어")

# 위에서 알아본 것처럼 query={value}에서 value에 위 encText 변수를 삽입
# encText라는 검색어를 담고있는 url 변수 작성
url = "https://openapi.naver.com/v1/search/blog?query=" + encText # json 결과
# url = "https://openapi.naver.com/v1/search/blog.xml?query=" + encText # xml 결과

# request.Request는 URL 요청의 추상화
request = urllib.request.Request(url)

# url을 요청할 때 보안을 위해 헤더에 추가적인 정보를 요청하는 API들이 있다.
request.add_header("X-Naver-Client-Id",client_id)
request.add_header("X-Naver-Client-Secret",client_secret)

# urlopen은 request를 보낼 인자를 받고, request를 보내 response를 반환한다.
response = urllib.request.urlopen(request)

# 응답 코드를 받는다.
rescode = response.getcode()

# 200(성공 코드)를 받는다면 response를 출력한다.
if(rescode==200):
    response_body = response.read()
    print(response_body.decode('utf-8'))
else:
    print("Error Code:" + rescode)

 

https://incomeplus.tistory.com/254

 

파이썬 urllib.request VS requests 차이점?

파이썬으로 웹사이트를 크롤링 할 때 가장 많이 사용되는 함수가 urllib.request와 requests다. 분명히 두 개함수가 차이가 있고, 필요한 상황에 맞춰 사용할 줄 알아야 할 것이다. 먼저 크롤링을 하기

incomeplus.tistory.com

 

https://developer.mozilla.org/ko/docs/Web/HTTP/Status

 

HTTP 상태 코드 - HTTP | MDN

HTTP 응답 상태 코드는 특정 HTTP 요청이 성공적으로 완료되었는지 알려줍니다. 응답은 5개의 그룹으로 나누어집니다: 정보를 제공하는 응답, 성공적인 응답, 리다이렉트, 클라이언트 에러, 그리고

developer.mozilla.org

 

response에 저장된 api 객체를 JSON 형식으로 저장하고 싶을 때

# 네이버 검색 API예제는 블로그를 비롯 전문자료까지 호출방법이 동일하므로 blog검색만 대표로 예제를 올렸습니다.
# 네이버 검색 Open API 예제 - 블로그 검색
import os
import sys

import json

# 특정 url에 request를 보낼 수 있도록 해주는 라이브러리
import urllib.request

# API 이용 신청을 통해 받은 id와 secret 입력
client_id = "YOUR_CLIENT_ID"
client_secret = "YOUR_CLIENT_SECRET"

# 검색할 단어를 입력받을 변수
# urllib.parse.quote()는 아스키코드 형식이 아닌 글자를 URL 인코딩 시켜줍니다.
encText = urllib.parse.quote("검색할 단어")

# 위에서 알아본 것처럼 query={value}에서 value에 위 encText 변수를 삽입
# encText라는 검색어를 담고있는 url 변수 작성
url = "https://openapi.naver.com/v1/search/blog?query=" + encText # json 결과
# url = "https://openapi.naver.com/v1/search/blog.xml?query=" + encText # xml 결과

# request.Request는 URL 요청의 추상화
request = urllib.request.Request(url)

# url을 요청할 때 보안을 위해 헤더에 추가적인 정보를 요청하는 API들이 있다.
request.add_header("X-Naver-Client-Id",client_id)
request.add_header("X-Naver-Client-Secret",client_secret)

# urlopen은 request를 보낼 인자를 받고, request를 보내 response를 반환한다.
response = urllib.request.urlopen(request)

# 응답 코드를 받는다.
rescode = response.getcode()

# 200(성공 코드)를 받는다면 response를 출력한다.
if(rescode==200):
    response_body = response.read()
    print(response_body.decode('utf-8'))
else:
    print("Error Code:" + rescode)

resjson = response_body.decode('utf-8')

with open('movie.json', 'w', encoading='UTF-8-sig') as file:
	file.write(json.dumps(resjson, ensure_ascii=False))

 

JSON 각 key에 접근하는 방법

pydata = json.loads(resdata)
data = pydata['items']

 

https://www.daleseo.com/python-json/

 

파이썬의 json 모듈로 JSON 데이터 다루기

Engineering Blog by Dale Seo

www.daleseo.com

 

 


검색 기능 구현하기

https://devdongbaek.tistory.com/57

 

동백 // QuerySet을 통한 간단 검색 구현

 

devdongbaek.tistory.com

https://ghqls0210.tistory.com/53

 

django / 검색 기능

이번에는 검색 기능을 만들어보겠습니다. 검색 기능 또한 직전 포스팅인 정렬과 같은 html에 만들면 좋지만 그러면 강제적으로 만들어 놓은 url을 수정해야되서 새로 url을 만들어서 하겠습니다. u

ghqls0210.tistory.com

https://velog.io/@mongle/Django-Web-Project-%EA%B0%9C%EB%B0%9C%EC%9D%BC%EC%A7%804-%EA%B2%80%EC%83%89-%EA%B8%B0%EB%8A%A5-%EC%B6%94%EA%B0%80

 

Django Web Project 개발일지4) 검색 기능 추가

djangoWeb searh 기능 추가

velog.io

 

 

https://devdongbaek.tistory.com/91

 

Django 빌트인 CBV를 통한 Form 처리

출처 : https://www.inflearn.com/course/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%9E%A5%EA%B3%A0-%EC%9B%B9%EC%84%9C%EB%B9%84%EC%8A%A4/dashboard 장고(Django)를 배우기 시작한 입문자이시거나, 또는 배우고 싶은 생..

devdongbaek.tistory.com

 

formview는 form을 보여주는 로직의 view

https://docs.djangoproject.com/en/3.0/ref/class-based-views/generic-editing/#django.views.generic.edit.FormView

 

Generic editing views | Django documentation | Django

Django The web framework for perfectionists with deadlines. Overview Download Documentation News Community Code Issues About ♥ Donate

docs.djangoproject.com

 

 

urls.py

path('search/', views.search.as_view(), name='search'),

 

forms.py

from django import forms

class SearchForm(forms.Form):
	search = forms.CharField(label='검색할 단어를 입력하세요', max_length=100)

 

views.py

from .forms import SearchForm

class search(Formview):
	form_class = SearchForm
    templates_name = 'blog/post_search.html'
	
    # form 입력이 성공했을 때
    def form_valid(self, form):
    	# form field 이름과 동일해야 함
    	searchWord = form.cleaned_data['search']
        post_list = Post.objects.filter(Q(title__icontains=searchWord) | Q(description__icontains=searchWord) | Q(content__icontains=searchWord)).distinct()
		
        # 딕셔너리 형식으로 값 저장
        """
        form = context.form
        searchWord = context.search_term
        post_list = context.object_list
        
        """
        context = {}
        context['form'] = form
        context['search_term'] = searchWord
        context['object_list'] = post_list

        return render(self.request, self.template_name, context)

. context라는 딕셔너리를 통해 템플릿에 인자를 전달한다.

 

templates

{% extends "base.html" %}

{% block title %}post_search.html{% endblock %}

{% block content %}
<h1>Blog Search</h1>
<br>

<form action="." method="post"> {% csrf_token %}
    {{ form.as_table }} <!-- form을 테이블 형식으로 표시, 여기서 form은 views에서 넘겨준 PostSearchForm 객체임-->
    <input type="submit" value="Submit" class="btn btn-primary btn-sm">
</form>

<br/><br/>

{% if object_list %}
    {% for post in object_list %}
        <h2><a href='{{ post.get_absolute_url }}'>{{ post.title }}</a></h2>
        {{ post.modify_date|date:"N d, Y" }}
        <p>{{ post.description }}</p>
    {% endfor %}
{% elif search_term %}<!-- 검색란이 공란인지 확인-->
    <b><i>Search Word({{ search_term }}) Not Found</i></b>
{% endif %}
{% endblock %}

 

https://velog.io/@jxxwon/Django-Q-%EA%B0%9D%EC%B2%B4

 

Django Q 객체

장고 Q객체

velog.io

 

CBV

https://wikidocs.net/9623

 

02) 클래스형 뷰 (CBV)

[TOC] # 클래스형 뷰 (CBV, Class-Based View) 클래스형 뷰는 상속과 믹스인 기능을 이용하여 코드 재사용하고 뷰를 체계적으로 구성할 수 있다. ## ...

wikidocs.net

 

반응형