반응형

회사에서 데이터 파이프라인 고도화 작업에 사용한다고 하여 공부하면서 정리해 볼 예정입니다..

 

빅쿼리란?(BigQuery)

  • 대용량 Dataset(최대 몇 십억 개의 행)를 대화식으로 분석할 수 있는 웹 서비스
  • 대규모 데이터 저장 및 분석 플랫폼으로, 일종의 데이터 웨어하우스
    • 데이터 웨어하우스 : 축적된 데이터를 모아 관리하는 곳
  • 확장 가능하고 사용이 간편한 BigQuery를 통해 개발자와 기업은 필요할 때 강력한 데이터 분석을 수행 할 수 있음

 

특징

  1. 클라우드 서비스로 설치/운영이 필요 없다.(NoOps)
    간단하게 클릭 몇 번으로 서비스 사용이 가능하고, 별도의 설정이나 운영이 필요 없다(PaaS 겸 IaaS)
  2. SQL 언어 사용
    기존 RDBMS에서 사용되는 SQL언어를 그대로 사용한다. 로그인 후 SQL만 수행하면 되므로 상대적으로 쉬움
  3. 클라우드 스케일의 인프라를 활용한 대용량 지원과 빠른 성능
  4. 데이터 복제를 통한 안정성
    3개의 복제본이 서로 다른 데이터 센터에 분산되어 저장되기 때문에 데이터에 대한 유실 위험이 적다.
  5. 배치와 스트리밍 모두 지원
    한꺼번에 데이터를 로딩하는 배치 외에, 실시간으로 데이터를 입력할 수 있는 스트리밍 기능을 제공
  6. 비용이 저렴하다.

 

구성

 

  • Project : 데이터를 담는 최상위 개념, 하나의 프로젝트에 여러개의 데이터셋이 들어갈 수 있음
    • 사용자와 데이터셋에 대한 권한 제어를 포함한 전반적인 리소스 관리 
  • Dataset : RDB에서 Database의 개념이며, 하나의 Dataset에 여러개의 테이블이 들어갈 수 있음
    • 빅쿼리에서 권한 제어를 할 수 있는 최소 단위
    • 데이터셋에 대한 권한부여를 통해 다른 사용자와 데이터 공유 가능 
  • Table : RDB에서 테이블과 동일한 개념
    • 스키마를 가지고 있음
    • 행(row) 단위 업데이트는 지원하지 않음
    • 테이블에 대한 권한제어는 따로 적용되지 않으며, 해당 테이블을 가지고 있는 부모 데이터셋으로부터 상속받음
  • Job : BigQuery에서 내리는 모든 명령
    • 쿼리 / 데이터 로딩 / 삭제 / 복사 / 내보내기 등의 명령을 수행하는 비동기식 연산 
    • Job(잡)의 구성요소
      • Reference - job ID : 유니크한 이름
      • Configuration - job Task : 수행하는 작업 종류
      • Status - job state : 오류와 경고를 포함한 상태
      • Statistics - job statistics : 수행 내역을 제공하는 통계
    • 잡의 구성요소는 향후 감사(audit)등의 목적을 위해서 로그를 남김
    • 실행 도중 취소 가능 , 하지만 취소해도 해당 명령에 대한 프로세싱은 이루어지기 때문에 비용 발생

 

데이터 타입

  • STRING : UTF-8인코딩. 최대 2MB
  • BYTES : base64로 인코딩 된 이진 데이터
  • INTEGER : 64 bit signed integer
  • FLOAT : Double precision, floating-point format
  • BOOLEAN : CSV format: true or false (case insensitive), or 1 or 0

                                 JSON format: true or false (case insensitive)

  • RECORD : A Collection of one or more field
    • 중첩 및 반복을 허용하고  JSON 과 같이 여러 개의 데이터 값을 가질 수 있다.
    • 불필요한 반복 및 중첩이 많아지면 쿼리를 포함한 여러 가지 작업들이 어려워지니 가급적 피하기 
  • TIMESTAMP

 

데이터 필드의 모드 

  • REQUIRED : NULL값을 허용하지 않음
  • NULLABLE : NULL값을 허용(기본값)
  • REPEATED : NULL값을 허용하고 1개 또는 배열 형태로 여러 개의 값을 가질 수 있으며 JSON, Avro포맷에서만 사용 가능

 

데이터 로딩 포맷

  • CSV
  • JSON(newline-delimited)
  • Avro
  • Cloud Datastore backups
  • Google sheets

## 스키마에 따라서 CSV, JSON , AVRO 모든  flat data 를 지원하지만,

      스키마에 중첩되거나 반복되는 필드를 가진 경우  JSON , Avro 만 지원

 

## 기본적으로 데이터에 줄바꿈(개행문자) 가 포함되어 있는 경우  JSON, Avro 가 빠름

 

 

데이터 로딩 지원도구(Tools)

  • Web UI : 웹 브라우저 기반으로 제공하는 빅쿼리 전용 웹 콘솔로 비전문가도 사용하기 쉽다.
  • Command-line interface (CLI) : 파이썬 기반의 명령어 도구가 제공된다.
  • REST API (POST) : POST요청으로 데이터 로딩을 할 수 있는 REST형태의 API를 제공하며, 재개 가능(Resumable)업로드, 다중 부분(Multipart) 업로드의 두가지 옵션을 제공한다.
  • Streaming API : 잡을 사용해서 대규모의 데이터를 로드하는 대신 한번에 하나의 레코드를 삽입할 수 있는 API를 제공한다.
  • Third-party ETL tools and connectors : 각종 3party의 ETL(Extract, Transform, Load) 툴 및 시각화, 개발환경 등과의 연동할 수 있도록 커넥터를 제공한다.

 



SQL의 사용 

  • BigQuery는 Standard SQL과 legacy SQL을 모두 제공
    • Legacy SQL은 초기부터 사용 된 문법이나, 2.0이후에는 Standard SQL이 지원
  • Standard SQL을 사용하는것이 필수는 아니지만 추천 됨
반응형

'Database Study > BigQuery' 카테고리의 다른 글

[BigQuery]bq - 데이터 로드  (0) 2020.09.04
OLTP, OLAP  (0) 2020.08.31
[BigQuery]표준 SQL 쿼리 구문  (0) 2020.06.14
[BigQuery] Datetime UTC 를 KST로  (0) 2020.05.26
[BigQuery]BigQuery Dremel  (0) 2020.05.26
반응형

 

이미 존재하는 깃허브 폴더에 또 풀을 땡기고 또 푸시를 하니까 생기는 에러인 것 같다. 

 

> 로컬 저장소에 있는 프로젝트를 깃허브 사이트를 통해 만든 저장소로 push 하는 경우에 이런 메세지가 뜨는 경우라고 합니다.

 

push 전에 먼저 pull을 해서 프로젝트를 병합해 주어야 한다.

 

 

pull 명령 실행시 이런 문구와 함께 진행되지 않는다면, 다음의 명령으로 실행한다.

 

$ git pull origin 브런치명 --allow-unrelated-histories

 

--allow-unrelated-histories  

=> 이 명령 옵션은 이미 존재하는 두 프로젝트의 기록(history)을 저장하는 드문 상황에 사용된다고 한다.

즉, git에서는 서로 관련 기록이 없는 이질적인 두 프로젝트를 병합할 때 기본적으로 거부하는데, 이것을 허용해 주는 것

 

 

 

자알 된다아

반응형

'Web > Git' 카테고리의 다른 글

Github 포토폴리오 만들기(2)  (0) 2020.03.06
Github 포토폴리오 만들기(1)  (0) 2020.03.06
Git 동작 원리  (0) 2020.03.05
Git 사용법(Git Bash)  (0) 2020.03.05
반응형
  • 권한 문제 해결하기
    • 다른 사람으로 로그인 시, 삭제 및 수정 하지 못하게 함 
    • html기준
    • views 조정으로 해결하기
  • 댓글 기능 구현하기
    • disqus를 활용한 댓글 구현

 

 <권한문제 해결하기>

  • 분기를 통해 간단하게 화면에서 나오지 않도록 만들기
  • 단, 새로운 유저 생성 이후 /delete/2 이런식으로 주소접근 해보면 새로운 유저가 기존 유저의 사진을 수정, 삭제가 가능 
      -> views.py로 해결 가능
  • user와 작성자가 같으면 수정하기와 삭제하기를 보여주도록 설정
  • 새로운 유저 생성 이후에 다시 로그인을 해보면,  다른 유저가 올린 사진에 수정하기와 삭제하기가 사라짐

- photo/templates/photo/photo_list.html

                <div class="card-body">
                    <a href="{% url 'photo:detail' object.id %}" class="card-link">댓글달기</a>
                    {% if user == object.author %}
                    <a href="{% url 'photo:update' object.id %}" class="card-link">수정하기</a>
                    <a href="{% url 'photo:delete' object.id %}" class="card-link">삭제하기</a>
                    {% endif%}
                </div>

 

 

views로 해결 ( /photo/views.py)

- def dispatch를 활용하여 get형태 및 post형태 2가지 모두 커버하기

 

1. PhotoUpdate class 수정 

  • 사용자 접속 시,  get이냐, post이냐를 결정하고 분기를 자동으로 해줌
  • 만약에 작성자와 요청자가 다르면 수정할 권한이 없다는 메시지를 주고 메인페이지로 이동
  • 그렇지 않으면 super을 써줘서 원래 Updateview가 실행되도록 해주며 super을 쓰게 되면 실행시 absolute_url로 자동적으로 이동한다. (단 success url이 설정되어 있으면 우선시 된다.)
from django.http import HttpResponseRedirect
from django.contrib import messages 

class PhotoUpdate(UpdateView):
    model = Photo
    fields = ['text', 'image']
    template_name_suffix = '_update'
    success_url = '/'

    def dispatch(self, request, *args, **kwargs):
        object = self.get_object()

        if object.author != request.user:
            messages.warning(request, '수정할 권한이 없습니다.')

            return HttpResponseRedirect('/')
            #삭제페이지에 권한이 없다고 띄우거나
            #detail 페이지 들어가서 삭제에 실패했다고 띄우거나

        else:
            return super(PhotoUpdate, self).dispatch(request, *args, **kwargs)

 

 

2. PhotoDelete class 수정

class PhotoDelete(DeleteView):
    model = Photo
    template_name_suffix = '_delete'
    success_url = '/'

    def dispatch(self, request, *args, **kwargs):
        object = self.get_object()

        if object.author != request.user:
            messages.warning(request, '삭제할 권한이 없습니다.')
            return HttpResponseRedirect('/')

        return super(PhotoDelete, self).dispatch(request, *args, **kwargs)

 

 

3. layout/base.html

  • {if message}를 통해 만약에 message가 있으면 message를 띄우도록 구현
  • 위치 : {block content}위에 위치하여 화면의 가장 위에 구현
  <div class ="container">
    {% if messages %}
    <div class = "row">
      <div class = "col"></div>
      <div class = "col-6">
        {% for message in messages%}
          <div class = "alert alert-{{message.tag}}">{{message}}</div>
        {% endfor %}
      </div>
      <div class = "col"></div>
    </div>
  </div>
    {% endif %}

  {% block content %}
  {% endblock %}

 

<확인하기>

  • 새로운 유저 생성 이후 다시 delete/2로 접속
  • 메인페이지에서 삭제할 권한이 없습니다 .확인

 

 

 <댓글 구현하기 - 소셜로그인>

- disqus 가입 후 -> create a new site -> Basic -> universal code -> configure -> complete setup

 

- 프로젝트로 돌아와 터미널에 

$pip install django.disqus #물론, 가상환경이 켜져 있는 상태에서 진행
$pip freeze > requirements.txt #환경업그레이드 되었으니, 다시한번 요구 사항에 적어줌 

 

- config/settings.py -> installed app 에 django.contrib.sites . disqus 추가

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'photo.apps.PhotoConfig',
    'accounts.apps.AccountsConfig',
    'django.contrib.sites',
    'disqus',
]


#맨 밑에 추가
DISQUS_WEBSITE_SHORTNAME = 'expinstagram'
SITE_ID = 1

 

$python manage.py migrate 는 필수!

 


잠깐, 여기서 

from six.moves.urllib_parse import urlencode ModuleNotFoundError: No module named 'six'

에러가 발생했다. 확인해보니 내가 낮은 버전의 장고와 pip를 사용하고 있었기 때문에 에러가 난 것 같은데, 

 

터미널에 

$pip install six

를 입력해주면 해결 가능 하다..

 

그리고 오류가 난 저부분 경로를 타고 들어가서 disqus/__init__.py를 살펴보고

urlopen과 urlencode 부분을 저렇게 바꿔주면 오류가 발생하지 않는다.

from six.moves.urllib_parse import urlencode
from six.moves.urllib_request import urlopen

<화면에 보이는 위치 설정해주기>

- photo/templates/photo/photo_detail.html

  1. 수정일 =>  { load disqus_tags }
  2. 보일 위치에 { disqus_show_comments } 
        <div class="card" >
            <img src=" {{ object.image.url }}" class="card-img-top" alt="...">
            <div class="card-body">
                <h5 class="card-title">{{ object.author }}</h5>
                <p class="card-text"> {{ object.text }} </p>
            </div>
            <ul class = "list-group list-group-flush">
                <li class = "list-group-item">생성일 : {{ object.created }}</li>
                <li class = "list-group-item">
                    {% load disqus_tags %}
                    <div class = "card-body">
                        {% disqus_show_comments %}
                    </div>

                </li>
            </ul>

 

 

<확인해보기>

- python manage.py runserver 후 

- 게시글 detail 에 들어가서 댓글달기를 아래와 같이 성공하면 완성!

 

 


기다리시는 분들이 있으시네요 ㅜㅜ 하지만... 회사 업무에 치여.. 장고관련 프로젝트는 중단된 상태입니다 ㅜㅜ 

반응형

'Web > Django' 카테고리의 다른 글

[Django] Instagram 클론 코딩(4)  (0) 2020.03.25
[Django] Instagram 클론 코딩(3)  (0) 2020.03.20
[Django] Instagram 클론 코딩(2)  (0) 2020.03.19
[Django_심화]4 - (1)pagination  (0) 2020.03.18
[Django] Instagram 클론 코딩(1)  (0) 2020.03.17
반응형
  • success url을 get_absolute_url 로 연동시켜보기
  • account 앱 만들기
    • 로그인/ 로그아웃 기능 구현하기
    • 템플릿에 로그인/ 로그아웃 보이도록 하기
    • 로그아웃 되었을 때는 create 및 로그아웃이 안 보이도록 구현하기 (분기)

 

<success url을 get_absolute_url로 연동시켜보기>

  1. models.py에 absolute url 입력해주기
  2. 상세페이지로 이동하도록 absolute_url 설정 (나중에 사용)
  3. 이후에 views에서 return super가 나오게 되면 자동적으로 absolute_url 이 실행

/photo/models.py 

from django.db import models
from django.contrib.auth.models import User
from django.urls import reverse

# Create your models here.

class Photo(models.Model):
    author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='user')
    #장고에서 구현하는 user 불러와 photo를 fk로 연결
    text = models.TextField(blank=True)
    image = models.ImageField(upload_to = 'timeline_photo/%Y/%m/%d')
    #timeline_photo 폴더에 연, 월, 일 만들어 사진 저장
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

    def __str__(self):
        return "text : " + self.text
        #admin 사이트 화면 표시 구현 

    class Meta:
        ordering = ['-created']
        #ordering 정렬

    def get_absolute_url(self):
        return reverse('photo:detail', args = [self.id])
        #상세페이지로 이동하도록 absolute_url 설정, views 에서 return super가 나오게되면 absolute가 실행

 

 

<account 앱 만들기>

  1. python manage.py startapp accounts
  2. setting.py에 install_app에 accounts 추가
    1. 추가해주지 않으면 템플릿 및 models을 찾을 수가 없다.
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'photo.apps.PhotoConfig',
    'accounts.apps.AccountsConfig'
]

 

- 로그인, 로그아웃 기능 만들기

  1. 장고에는 login view 와 logoutview를 기본적으로 제공
    • 기본적으로 loginview나 logoutview는 registration/login.html, logout.html을 자동 렌더링 하게 되어 있다
  2. 고로, urls.py 에서 login.html과 logout.html를 구현해 바로 전달해줄 계획
  3. 로그인 성공했을시 접속할 링크 구현

/account/urls.py

from django.urls import path
from django.contrib.auth.views import LoginView, LogoutView

app_name = "accounts"

urlpatterns = [
    path('login/', LoginView.as_view(template_name = 'accounts/login.html'), name = 'login'),
    path('logout/', LogoutView.as_view(template_name = 'accounts/logout.html'), name = 'logout'),
]

 

 

  4. main url (config /urls.py) 에 accounts url 연결

    - /config/urls.py 

from django.contrib import admin
from django.urls import path, include


urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('photo.urls')),
    path('accounts/', include('accounts.urls'))
]

 

 

  5. templates 구현하기 

     - login.html (accounts/templates/accounts/login.html)

{% extends 'base.html' %}

{% block title %}
{% endblock %}

{% block content %}
<div class="row">
    <div class="col"></div>
    <div class="'col-6">
        <div class="alert alert-primary" role="alert">
            Please enter your login information. </div>
        <form action="" method="post">
            {% csrf_token %}
            {{form.as_p}}
            <input type="submit" value="login" class = 'btn btn-outline-primary'>
        </form>
    </div>
    <div class="col"></div>

</div>
{% endblock %}

 

 

     - logout.html (accounts/templates/accounts/logout.html)

{% extends 'base.html' %}

{% block title %}
{% endblock %}

{% block content %}
<div class="row">
    <div class="col"></div>
    <div class="'col-6">
        <div class="alert alert-primary" role="alert">
            로그아웃 되었습니다. </div>
        <a href="{% url 'accounts:login'%}" class="btn btn-outline-success">login 하기</a>
    </div>
    <div class="col"></div>

</div>
{% endblock %}

 

6. 로그인 성공 시, 접속할 링크 구현해주기 

- config/settings.py 에 리다이렉트 url 추가

LOGIN_REDIRECT_URL ='/' 

 

여기까지 해서 python manage.py runserver 해보고 accounts/login , accounts/logout 들어가보고 확인해보기!

 

 

<템플릿에 로그인 / 로그아웃 보이도록 하기>

  1. base.html의 navbar를 추가하여 로그인 / 로그아웃 구현
  2. url 링크 연동 
  3. 로그인 했을 때만, 사진 업로드 및 logout 보이게 하기(분기 사용)

-layout/base.html

    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav mr-auto">
        <li class="nav-item active">
          <a class="nav-link" href="{% url 'photo:index' %}">Home<span class="sr-only">(current)</span></a>
        </li>
        {% if user.is_authenticated %}
        <li class="nav-item">
          <a class="nav-link" href="{% url 'photo:create'%}">사진 올리기</a>
        </li>
        <li class="nav-item">
          <a class="nav-link" href="{% url 'accounts:logout'%}">Logout</a>
        </li>
        {% else %}
        <li class="nav-item">
          <a class="nav-link" href="{% url 'accounts:login'%}">Login</a>
        </li>
        {% endif %}

 

 

runserver 해보면 유저가 인증받은 상태일 때만 사진올리기와 Logout이 가능하며, 

유저가 인증받지 않았다면 navbar 부분에 Login만 보이게 해 놓았다.

 

이런식으로 나오면 login / logout 기능 구현 완료 

 

반응형

'Web > Django' 카테고리의 다른 글

[Django] Instagram 클론 코딩(5)  (0) 2020.03.26
[Django] Instagram 클론 코딩(3)  (0) 2020.03.20
[Django] Instagram 클론 코딩(2)  (0) 2020.03.19
[Django_심화]4 - (1)pagination  (0) 2020.03.18
[Django] Instagram 클론 코딩(1)  (0) 2020.03.17
반응형

python 의 대표적 시각화 모듈인 matplotlib 모듈을 활용한 다양한 시각화 기법

 

  1. matplotlib 모듈 기초 문법
    • 2차원 그래프 주로 그리는 패키지
    • 동작하는 OS 제한 없음
    • MATLAB과 유사한 인터페이스
    • 그림 요소 상세한 서식 지정 
    • 다양한 출력 양식으로 저장 가능 


 


 











 

이 외에도 다양한 차트 작성 가능  / 다른 차트 종류는 www.matplotlib.org 에 !

반응형
반응형
  • Template 만들기
  • 사진이 화면에 업로드 할 수 있도록 만들기

<Template 만들기>

1. 템플릿 만들기

  • photo/templates/photo 폴더 생성

    • 그 아래에 html 파일들 생성해주기

    • 많은 앱들 생성 시, 겹치는 문제 해결 위해 photo 폴더를 다시 생성해주고 html 파일 생성

  • photo/layout 폴더 생성

    • 그 아래에 layout.html 파일 생성

    • 모든 html 파일 안에 들어갈 base가 될 layout.html 생성

    •  setting에 templates 경로에 layout 경로 찾을 수 있도록 지정 

layout/base.html 경로

/instagram/config/settings.py

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'layout')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

 

 

<Bootstrap 사용>

  1. Bootstrap · The most popular HTML, CSS, and JS library in the world. 에 접속합니다.

  2. base.html 파일에 부트스트랩을 사용하기 위해 필요한 css 및 javascript 링크를 넣습니다.

    1. css 파일은 head의 마지막에 위치

    2. javascript 파일은 body의 마지막에 위치

#css 
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> 

#javascript

<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>

  3. 마음에 드는 포맷을 찾으면 해당 포맷을 적용할 html에 넣어줍니다.

  • 지금은 base.html에 넣어줍니다.(body tag내 위치)

  • 파일들을 적절하게 수정하고 클릭시 연결할 링크를 연결해줍니다.

    1. <a href = " " > 내 위치

    2. { url 'photo:index' } 등과 같이 연결

  • block content를 만들어서 다른 템플릿에서 추가할 수 있도록 구성합니다.

  • Home / 사진 올리기

  • 아래쪽에 { block content } { endblock } 을 구성

  • Dstagram 제목 설정 및 Home, 사진 올리기 url 연결하기

/photo/layout/base.html

<!DOCTYPE html>
<html lang = "en">


<head>
    <meta charset="UTF-8">
    <title>{% block title %} {% endblock %}</title>

    #css 
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> 

</head>

<body>

#navbar
<nav class="navbar navbar-expand-lg navbar-light bg-light">
    <a class="navbar-brand" href="#">Dstagram</a>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>
  
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav mr-auto">
        <li class="nav-item active">
          <a class="nav-link" href="{% url 'photo:index' %}">Home<span class="sr-only">(current)</span></a>
        </li>
        <li class="nav-item">
          <a class="nav-link" href="{% url 'photo:create'%}">사진 올리기</a>
        </li>
      </ul>
      <form class="form-inline my-2 my-lg-0">
        <input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search">
        <button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
      </form>
    </div>
  </nav>

  {%block content%}

  {% endblock %}

  #javascript bootstrap
  <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
  <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>

</body>



</html>

 

<Photo 템플릿 수정>

/photo/templates/photo/photo_list.html

  • for문으로 models를 통해 생성된 object의 list를 하나씩 

  • img의 경로는 image.url 을 통해 연결

{% extends 'base.html' %}
{% block title %}
{% endblock %}

{% block content %}

<div class='container'>
    <div class="row">
        <div class="col"></div>
        <div class="col-5">
            {% for object in object_list %}

            <div class="card">
                <div class="card-header">
                    {{ object.author }}
                </div>
                <img src=" {{ object.image.url }}" class="card-img-top" alt="...">
                <div class="card-body">
                    <p class="card-text"> {{ object.text }} </p>
                </div>
                <ul class="list-group list-group-flush">
                    <li class="list-group-item">생성일 : {{ object.created }}</li>
                    <li class="list-group-item">수정일 : {{ object.updated }}</li>
                </ul>
                <div class="card-body">
                    <a href="{% url 'photo:detail' object.id %}" class="card-link">댓글달기</a>
                    <a href="{% url 'photo:update' object.id %}" class="card-link">수정하기</a>
                    <a href="{% url 'photo:delete' object.id %}" class="card-link">삭제하기</a>

                </div>

            </div>
            {% endfor %}
        </div>

        <div class = "col"></div>
    </div>
</div>
{% endblock %}

 

/photo/templates/photo/photo_create.html

  • form을 활용하여 입력창 생성

  • post형식으로 전송

  • { csrf_token }을 작성하여 보안문제 해결

  • {{ form.as_p }} 를 적어줌으로서 한줄씩 띄우면서 create의 field에 등록된 것을 가지고 옴

- html의 폼형태에서 입력할 때는 모두 text 파일로 인식 ->  따라서 form에 image 파일이라는 것을 인식시켜줌!

 

{% extends 'base.html' %}
{% block content %}
<div class="row">
    <div class="col"></div>
    <div class="col-12 col-xl-8 col-md-8">
        <div class="alert alert-primary" role="alert">
            모든 부분을 다 채워넣어주세요!!
        </div>
        <form action="" method="post" enctype='multipart/form-data'>
            {% csrf_token %}

            {{form.as_p}}
            <input type="submit" value="Upload" class="btn btn-primary">
        </form>
    </div>
    <div class="col"></div>
</div>
{% endblock %}

 

 

/photo/templates/photo/photo_detail.html

  • 해당 컨텐츠의 작성자 / text를 보여준다.

  • 수정과 삭제를 할 수 있도록 링크 연결

{% extends 'base.html' %}
{% block title %}
{% endblock %}

{% block content %}
<div class="row">
    <div class="col"></div>
    <div class="col-12 col-xl-4 col-md-4">
        <div class="card" style="">
            <img src=" {{ object.image.url }}" class="card-img-top" alt="...">
            <div class="card-body">

                <h5 class="card-title">{{ object.author }}</h5>
                <p class="card-text"> {{ object.text }} </p>
            </div>
            <div class="card-body">
                <a href="{% url 'photo:update' object.id %}" class="card-link">수정하기</a>
                <a href="{% url 'photo:delete' object.id %}" class="card-link">삭제하기</a>
            </div>
        </div>
    </div>

    <div class="col"></div>
</div>
{% endblock %}

 

 

/photo/templates/photo/photo_delete.html

  • form 형식으로 구현

  • 한번 더 실제로 지울지를 확인하는 페이지로 구성

  • form 을 다시 보여줄 필요는 없다

{% extends 'base.html' %}
{% block content %}

<div class="alert alert-primary" role="alert">
  진짜로 {{object}}를 지우실 건가요?
</div>

<form action="" method ="post">
    {% csrf_token%}
    <input type="submit" value="Delete OK">
</form>
{% endblock %}

 

 

/photo/templates/photo/photo_update.html

  • create와 비슷하게 구현

  • 폼 형태로 입력할 수 있도록 구현

{% extends 'base.html' %}
{% block content %}
<div class="row">
    <div class="col"></div>
    <div class="col-12 col-xl-8 col-md-8">
        <form action="" method="post">
            {% csrf_token %}
            {{form.as_p}}
            <input type="submit" value="Create">
        </form>
    </div>
    <div class="col"></div>
</div>
{% endblock %}

 

 

 

<화면에 사진 업로드 할 수 있도록 만들기>

- photo을 생성하는데 user_id가 확인되어야 하므로 views.py 수정

/photo/views.py

class PhotoCreate(CreateView):
    model = Photo
    fields = ['text', 'image']
    template_name_suffix = '_create'
    success_url = '/'


    def form_valid(self, form):
        form.instance.author_id = self.request.user.id
        if form.is_valid():
            form.instance.save()
            return redirect('/')
        else:
            # 올바르지 않다면
            return self.render_to_response({'form': form})

 

- 메인 화면에 사진이 오류가 뜸

  1. static으로 url 지정
  2. photo/urls.py 수정
urlpatterns += static(settings.MEDIA_URL, document_root = settings.MEDIA_ROOT)

 

 

아래와 같이 나온다면 성공!

 

반응형

'Web > Django' 카테고리의 다른 글

[Django] Instagram 클론 코딩(5)  (0) 2020.03.26
[Django] Instagram 클론 코딩(4)  (0) 2020.03.25
[Django] Instagram 클론 코딩(2)  (0) 2020.03.19
[Django_심화]4 - (1)pagination  (0) 2020.03.18
[Django] Instagram 클론 코딩(1)  (0) 2020.03.17
반응형
  • views 설계하기
  • URL 연결하기

<Views 설계>

  1. class형 뷰의 generic view를 이용하여 구현

  2. ListView/CreateView/UpdateView/DeleteView/DetailView 구현

(instagram/photo/views.py)

from django.shortcuts import render
from django.views.generic.list import ListView
from django.views.generic.edit import UpdateView, CreateView, DeleteView
from django.views.generic.detail import DetailView
from .models import Photo

# class형 뷰의 generic view를 이용하여 구현
# ListView/CreateView/UpdateView/DeleteView/DetailView 구현

class PhotoList(ListView):
    model = Photo
    template_name_suffix = '_list'
#PhotoList:가장 메인에서 보여줄 로직 / 모델을 불러와서 데이터를 활용할 것이라고 기제
class PhotoCreate(CreateView):
    model = Photo
    fields = ['text', 'image']
    template_name_suffix = '_create'
    success_url = '/'

# PhotoCreate:
# 모델을 활용하는데생성할 때 채워야 할 필드 확인이후 연결될 템플릿 이름은 Photo_create 일 것이다.
#성공하면 메인 페이지로 돌아가도록 연결(이후 url로 연결)

class PhotoUpdate(UpdateView):
    model = Photo
    fields = ['text', 'image']
    template_name_suffix = '_update'
    success_url = '/'

#photoUpdate : Create와 동일

class PhotoDelete(DeleteView):
    model = Photo
    template_name_suffix = '_delete'
    success_url = '/'

class PhotoDetail(DetailView):
    model = Photo
    template_name_suffix = '_detail'

#PhotoDelete와 PhotoDetail
#삭제와 상세페이지는 특별한 로직이 필요하지 않음
#템플릿과 연결을 잘 시킬 수 있도록

 

 

<URL 연결하기>

 

- Photo URL 연결하기

  1. photo의 urls.py 생성 ( instagram/photo/urls.py)

  2. URL 작성하기

from django.urls import path
from .views import PhotoList, PhotoDelete, PhotoDetail, PhotoUpdate, PhotoCreate

app_name = "photo"
#app_name 설정을 통해 namespace(이름공간)확보
#다른 앱들과 url pattern 이름이 겹치는 것을 방지하기 위해 사용한다.

urlpatterns = [
    path("create/", PhotoCreate.as_view(), name = 'create'),
    path("delete/<int:pk>/", PhotoDelete.as_view(), name = 'delete'),
    path("update/<int:pk>/", PhotoUpdate.as_view(), name = 'update'),
    path("detail/<int:pk>/", PhotoDetail.as_view(), name = 'detail'),
    path("", PhotoList.as_view(), name = 'index'),
]

 

 

- Config URL 연결하기

  1. config의 url.py에 작성(instagram/config/urls.py)

  2. 2차 URL을 설정한 이후에 가장 기본이 되는 URL과 연결시켜준다.

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('photo.urls')),
]

include를 통해서 해당 주소로 연결시켜준다.

  1. 가장 기본 주소가 들어오면 photo의 url로 연결

반응형

'Web > Django' 카테고리의 다른 글

[Django] Instagram 클론 코딩(4)  (0) 2020.03.25
[Django] Instagram 클론 코딩(3)  (0) 2020.03.20
[Django_심화]4 - (1)pagination  (0) 2020.03.18
[Django] Instagram 클론 코딩(1)  (0) 2020.03.17
[Django_심화]3 - viewset & router  (2) 2019.11.03
반응형

API 서버에서 Pagination 

-> 사용자가 게시판의 전체 글 목록 요청 시 /
    수 많은 데이터들을 한번에 전달 한다면 데이터를 받는 사용자는 많은 데이터를 감당할 수 없을 것 

 

API 서버에서 Pagination을 사용하는 이유

- > 하나의 Request 만으로 처리하기 어려운 레코드들을 여러 Request로 나누어 전송하기 위해 사용

 

DRF에서의 Pagination

  1. PageNumberPagination 
  2. LimitOffsetPagination 
  3. CursorPagination
  4. CustomizedPagination

1, 2, 3 = Django restFramework 에서 제공하는 Pagination class

4 = 직접 Pagination class를 직접 커스터마이징 해서 사용하는 방법 

 

흔히, PageNumberPagination 을 Default로 설정하고

추가적인 페이지 설정 부분만 CustomizedPagination 을 사용

 

 

프로젝트 기본 세팅

1. Django Project 생성

2. 경로 이동 및 앱 생성

3. settings.py에 앱 등록

4. models.py 작성 및 admin.py 연결

5. makemigrations 및 migrate 진행

6. serializer.py 생성 및 작성

from .models import Post
from rest_framework import serializers


class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = "__all__"

 

7. views.py 작성

from .models import Post
from .serializer import PostSerializer
from rest_framework import viewsets


class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

 

 

8. urls.py 작성

- 앱 내부의 urls.py

from rest_framework.routers import DefaultRouter
from django.urls import include, path
from .views import PostViewSet

router = DefaultRouter()
router.register('post', PostViewSet)

urlpatterns = [
    path('', include(router.urls))
]

 

- 프로젝트 폴더의 urls.py 

from django.contrib import admin
from django.urls import path, include
import post.urls

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include(post.urls)),
]

 

DRF로 Pagination 구현하기

DEFAULT_PAGINATION_CLASS는 None이며 PAGE_SIZE 또한 NONE이다.

 

settings.py에 Pagination관련 설정하기

- DEFAULT_PAGINATION_CLASS에 Pagination 클래스를 설정하고, PAGE_SIZE에 한 번에 가져올 레코드의 수 설정

 

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS':
        'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 10,
}

 

 

Pagination 적용 후 테스트

아래와 같이 한 번에 10개씩의 레코드만 가져오는 것을 확인할 수 있다.
총 데이터의 개수인 count와 이전 10개의 레코드 주소인 previous
다음 10개의 레코드를 위한 next또한 RESPONSE에 생겼다.

 

 

 

유의 사항

Pagination을 사용할 때는 반드시 레코드를 정렬해 사용해야 한다.
=> views.py를 아래와 같이 id로 정렬하도록 수정해준다.

from .models import Post
from .serializer import PostSerializer
from rest_framework import viewsets


class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.all().order_by('id')
    serializer_class = PostSerializer

 

CustomizedPagination 클래스 작성하기

PageNumberPagination을 상속받는 클래스를 선언하고
클래스 내부에 page_size변수에 원하는 레코드의 수를 지정한 후
적용할 모델의 ViewSet의 pagination_class에 클래스를 지정하면 된다.

 

...
from rest_framework.pagination import PageNumberPagination

class MyPagination(PageNumberPagination):
    page_size = 6

class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.all().order_by('id')
    serializer_class = PostSerializer
    pagination_class = MyPagination

지정해준 page 수 대로 잘 정리되는 것을 볼 수 있다

 

코드 클린

1. pagination.py 생성 및 클래스 옮기기

from rest_framework.pagination import PageNumberPagination


class MyPagination(PageNumberPagination):
    page_size = 6

 

2. views.py 에 추가, 사용

from .models import Post
from .serializer import PostSerializer
from .pagination import MyPagination
from rest_framework import viewsets


class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.all().order_by('id')
    serializer_class = PostSerializer
    pagination_class = MyPagination
반응형

'Web > Django' 카테고리의 다른 글

[Django] Instagram 클론 코딩(3)  (0) 2020.03.20
[Django] Instagram 클론 코딩(2)  (0) 2020.03.19
[Django] Instagram 클론 코딩(1)  (0) 2020.03.17
[Django_심화]3 - viewset & router  (2) 2019.11.03
[Django_심화]2 - Rest, Serializer  (0) 2019.11.03

+ Recent posts