반응형

JavaScript xml

 

HTML 형태의 코드를 js 코드로 변환하기 위한 규칙 

- jsx에서는 태그를 닫지 않으면 오류가 발생 , 꼭 태그를 열었으면 닫아주어야 합니다.

 

- 두 개 이상의 엘리먼트는 반드시 하나의 엘리먼트로 감싸야 한다.
   div 태그로 감싸는 방법도 있지만, 스타일 중복이나 table 관련 태그가 번거롭기 때문에 React v16.2에 도입된 Fragment을 이용

   Fragement를 사용하게 되면 실제 DOM 자체는 생기지 않는다. (논리적 엘리먼트이기 때문에)

import React, { Component } from "react";
 
class App extends Component {
  render() {
    return (
      <React.Fragment>
        <h1>My name is</h1>
        <b>jisu</b>
      </React.Fragment>
    );
  }
}
 
export default App;

- jsx 내부에서 javascript의 변수나 함수를 사용하기 위해  {}를 사용

import React, { Component } from "react";
 
class App extends Component {
  render() {
    const color = "black";
 
    return <h1>Color is {color}</h1>;
  }
}
 
export default App;

-jsx 내부에서 조건문 사용시 , 삼항연산자(조건? 참 : 조건) 이나 AND 연산자(조건 &&참) 이용

 IF문은 기본적으로 사용할 수 없고, 사용하려면 IIFE(즉시 실행 함수 표현)을 사용

import React, { Component } from "react";
 
class App extends Component {
  render() {
    const lib = "react";
 
    return (
      <React.Fragment>
        {/* IIFE(즉시 실행 함수) 사용 */}
        {(() => {
          if (lib === "angular") return <h1>Angular</h1>;
          if (lib === "react") return <h1>React</h1>;
          if (lib === "vue") return <h1>Vue</h1>;
        })()}
      </React.Fragment>
    );
  }
}
 
export default App;

 

import React, { Component } from "react";
 
class App extends Component {
  render() {
    const color = "white";
    const lib = "react";
 
    return (
      <React.Fragment>
        {color === "black" ? <h1>검정색</h1> : <h1>다른색</h1>}
        {lib === "react" && <h2>리액트</h2>}
      </React.Fragment>
    );
  }
}
 
export default App;

- JSX에서 style과 className을 사용할 때 

  #styel변수로 사용할 경우 객체 형태로 생성

  #속성이름은 하이펀(-)형태로 camelcase로 바꾸어야한다
    (background-color ==> backgroundColor) 
  #또한 class 대신 className 을 사용

 

// App.css 수정
.App {
  background-color: black;
  color: magenta;
  font-size: 24px;
  font-weight: bold;
  padding: 15px;
}

import React, { Component } from "react";
import "./App.css";
 
class App extends Component {
  render() {
    const style = {
      backgroundColor: "cyan",
      color: "white",
      textDecoration: "underline"
    };
 
    return (
      <div className="App">
        <span>My Name is </span>
        <span style={style}>Kani</span>
      </div>
    );
  }
}
 
export default App;

반응형

'Language Study > React' 카테고리의 다른 글

4. Life Cycle API  (0) 2019.11.26
3. props와 state  (0) 2019.11.26
1. 준비사항  (0) 2019.11.26
반응형

정의 : 컴포넌트를 여러가지 파일로 분리하여 저장하고 JS가 아닌 JSX 라는 문법을 사용

 

- Webpack : 여러가지 분리된 파일 하나로 합쳐주는 라이브러리

- Babel : jsx를 비롯한 새로운 js 문법을 사용하기 위한 라이브러리

- Node.js : Webpack과 Babel 등 여러 라이브러리를 사용하기 위한 javascript 런타임

- Yarn : 캐싱 시스템 등 조금 더 개선된 npm(node package manager)

- Visual Studio Code : 실제 개발을 위해 필요한 에디터 (공짜)

 

 

컴포넌트 App.js 코드 분석

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

//파일에서 jsx문법 사용하려면 React 꼭 import
//Webpack을 사용하기에 logo.svg와 App.css를 이런식으로 불러옴

 

class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <p>
            Edit <code>src/App.js</code> and save to reload.
          </p>
          <a
            className="App-link"
            href="https://reactjs.org"
            target="_blank"
            rel="noopener noreferrer"
          >
            Learn React
          </a>
        </header>
      </div>
    );
  }
}

//컴포넌트 만드는 방법 중 하나인 클래스 이용하는 방법
//클래스는 꼭 render()이 있어야하고 그 내부에서 jsx를 반환 
//return 뒤의 html 같은 코드 = jsx
	
export default App;
//작성된 컴포넌트를 다른 곳에서 불러 사용하게 할 수 있는 코드

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
//APP 컴포넌트 사용하기 위해 import
import * as serviceWorker from './serviceWorker';
 
ReactDOM.render(<App />, document.getElementById('root'));
//브라우저 상 React컴포넌트를 보여주기 위해 ReacDOM.render(렌더링 결과물, 그릴 DOM ) 함수 사용
//public/index.html 파일에 있는 id가 'root'인 DOM을 찾아 app컴포넌트를 렌더링
serviceWorker.unregister();
반응형

'Language Study > React' 카테고리의 다른 글

4. Life Cycle API  (0) 2019.11.26
3. props와 state  (0) 2019.11.26
2. JSX  (0) 2019.11.26
반응형

1. Views of DRF

모델 기반의 viewset을 작성했었다.

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

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

ViewSet

-> View(CRUD)를 설계하는 가장 간단한 방법

 

 

2. APIView

         -> 장점 : 자신의 마음대로 코드를 작성할 수 있다.

         -> 단점 : CBV의 장점인 중복되는 코드 제거를 하지 못한다.

         

         -> Response : 클라이언트가 요청한 형태로 콘텐츠를 렌더링 해준다.

         -> Status : Http 에서 제공하는 상태 코드는 숫자이지만, REST 프레임워크에서는 
              문자 형태의 식별자를 제공하여 상태를 좀더 잘 구분할 수 있도록 한다.

 

         -> APIView를 상속해서 view를 설계할 때는 
             status 와 Response를 import 하여 Response과정을 작성

         -> APIView를 상속해서 만든 CBV의 내부 함수들은 필요로 하는  HTTP Method 이름으로 갖는다.

         -> 어떠한 status를 받고 response를 전달할지 직접 정하는 것이 apiview를 상속받은 cbv

class SnippetDetail(APIView):
    def get(...):
        ...

    def put(...):
        ...

    def delete(...):
        ...
        
#SnippetDetail 클래스에 필요한 method는 GET, PUT, DELETE

##실습해보기

  • django-admin startproject <project-name>
  • cd <project-name>
  • python manage.py startapp <app-name>
  • settings.py에 App, rest_framework추가
  • models.py에 모델 작성 및 migrate
class Post(models.Model):
    title = models.CharField(max_length=100)
    body = models.TextField()
  • serializer.py 생성 및 작성
from .models import Post
from rest_framework import serializers


class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = "__all__"
  • views.py 작성
    1) 필요한 모듈 추가(apiview를 상속받은 cbv를 작성하기 위해 아래 모듈 추가)
from CBV.models import Post
from CBV.serializer import PostSerializer
from django.http import Http404
from rest_framework.response import Response
from rest_framework import status
from rest_framework.views import APIView

           2)객체들 목록 가져오는 PostList 작성

             - post 메서드는 다수의 post 객체를 반환하는 메서드 

             - 다수의 객체를 직렬화 하기위해서는 PostSerializer 에 many = True를 넘겨야 한다

            - post 메서드는 새로운 객세 생성하는 함수

           - 저장 성공시 상태코드 201(created) / 반환 실패시 400(bad request)반환  

class PostList(APIView):
    def get(self, request):
        posts = Post.objects.all()
        serializer = PostSerializer(posts, many=True)

        return Response(serializer.data)

    def post(self, request):
        serializer = PostSerializer(data=request.data)

        if serializer.is_valid():
            serializer.save()

            return Response(serializer.data, status=status.HTTP_201_CREATED)

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

          3) 객체의 상세 정보를 받는 PostDetail 작성

              - 각각의 함수에 pk 값을 인자로 받는다

              - get_object 함수 : get_object_or_404 함수와 동일한 기능을 한다

              - PostList 에서 작성한 함수들과 비슷한 방식으로 작동

class PostDetail(APIView):
    def get_object(self, pk):
        try:
            return Post.objects.get(pk=pk)
        except Post.DoesNotExist:
            return Http404

    def get(self, request, pk, format=None):
        post = self.get_object(pk)
        serializer = PostSerializer(post)

        return Response(serializer.data)

    def put(self, request, pk, format=None):
        post = self.get_object(pk)
        serializer = PostSerializer(post, data=request.data)

        if serializer.is_valid():
            serializer.save()

            return Response(serializer.data)

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk, format=None):
        post = self.get_object(pk)
        post.delete()

        return Response(status=status.HTTP_204_NO_CONTENT)
  • urls.py 작성하기
    1) App 내부의 urls.py 
        - rest_framework.urlpatterns 의 format_suffix_patterns 모듈 추가 
        - views.py 가져와 <ClassName>.as_view() 형식으로 작성
from django.urls import path, include
from rest_framework.urlpatterns import format_suffix_patterns
from . import views

urlpatterns = [
    path("post", views.PostList.as_view()),
    path("post/<int:pk>", views.PostDetail.as_view()),
]

urlpatterns = format_suffix_patterns(urlpatterns)

       2) 프로젝트 폴더의 urls.py 
           - include 를 사용해 앱의 urls.py 를 가져와 PATH 설정

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

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

실행하면 -> http://127.0.0.1:8000/post 로 젒고하면 결과가 잘 보인다.

 


3. Mixins

  • APIView를 상속해 CBV 구현시 문제점
    • 여러가지 모델 사용 시 비슷한 논리 코드 반복
      -> 불필요한 반복을 제거하기 위해 mixins 사용 
      -> 상속을 통해 불필요한 반복을 제거 

##실습해보기

1. views.py 작성하기 전

  • django-admin startproject <project-name>
  • cd <project-name>
  • python manage.py startapp <app-name>
  • settings.py에 App, rest_framework추가
  • models.py에 모델 작성 및 migrate

2. views.py 작성

   1) 필요한 모듈 추가하기 

from .models import Post
from serializer import PostSerializer
from rest_framework import generics
from rest_framework import mixins

  2) PostList 클래스 작성

  • apiview를 상속해 작성한 PostList 클래스와 동일한 기능
  • mixins 의 ListModelMixin 과 CreateModelMixin , generics의 GenericAPIView 상속
  • 사용할 모델의 쿼리셋과 Serializer 클래스 등록
  • 객체 목록을 가져오는 GET 메서드는 ListModelMixin 에 구현된 list 함수 사용
  • 객체 생성하는 POST 메서드는 CreateModelMixin 에 구현된 create 함수 사용
class PostList(mixins.ListModelMixin,
               mixins.CreateModelMixin,
               generics.GenericAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

   3) PostDetail 클래스 작성하기  

  • PostList를 작성한 것과 동일하게 작성
  • 각 메서드에 필요한 mixins 모듈 상속하고 , serializer, 쿼리셋 등록 후 각 HTTP Method 함수 작성
class PostDetail(mixins.RetrieveModelMixin,
                 mixins.UpdateModelMixin,
                 mixins.DestroyModelMixin,
                 generics.GenericAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

3. urls.py 작성

- 앱 내부에 urls.py 생성 및 작성

from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from . import views

urlpatterns = [
    path("post", views.PostList.as_view()),
    path("post/<int:pk>", views.PostDetail.as_view()),
]

urlpatterns = format_suffix_patterns(urlpatterns)

- 프로젝트 폴더 내부의 urls.py 에 include 

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

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

실행하고 http://127.0.0.1:8000/post 에 접속하면 정상 작동한다

 

4. Generic CBV

<분류>

  • 기반 뷰(Base View): 뷰 클래스를 생성하고 다른, 제너릭 뷰의 부모 클래스가 되는 기본 제너릭 뷰
    • view : 최상위 부모 제너릭 뷰 클래스
    • TemplateView : 주어진 템플릿으로 렌더링
    • RedirectView : 주어진 URL로 리다이렉트
  • 제너릭 보기 뷰(Generic Display View): 객체의 목록 또는 하나의 객체 상세 정보를 보여주는 뷰
    • DetailView : 조건에 맞는 하나의 객체 출력
    • ListView : 조건에 맞는 객체 목록 출력
  • 제너릭 수정 뷰(Generic Edit View): 폼을 통해 객체를 생성, 수정, 삭제하는 기능을 제공하는 뷰
    • FormView : 폼이 주어지면 해당 폼 출력
    • CreateView : 객체 생성하는 폼 출력
    • UpdateView : 기존 객체 수정하는 폼 출력
    • DeleteView : 기존 객체 삭제하는 폼 출력
  • 제너릭 날짜 뷰(Generic Date View): 날짜 기반 객체의 연/월/일 페이지로 구분해 보여주는 뷰
    • YearArchiveView : 주어진 연도에 해당하는 객체 출력
    • MonthArchiveView : 주어진 월에 해당하는 객체 출력
    • DayArchiveView : 주어진 날짜에 해당하는 객체 출력
    • TodayArchiveVIew : 오늘 날짜에 해당하는 객체 출력
    • DateDetailView : 주어진 연, 월, 일 PK(OR 슬러그)에 해당하는 객체 출력

<제너릭 뷰 오버라이딩>

  • 속성 변수 오버라이딩
    • model : 기본 뷰(View, Template, RedirectView) 3개를 제외하고 모든 제너릭 뷰에서 사용한다.
    • queryset : 기본 뷰(View, Template, RedirectView) 3개를 제외하고 모든 제너릭 뷰에서 사용한다. queryset을 사용하면 model 속성은 무시된다.
    • template_name : TemplateView를 포함한 모든 제너릭 뷰에서 사용한다. 템플릿 파일명을 문자열로 지정한다.
    • context_object_name : 뷰에서 템플릿 파일에 전달하는 컨텍스트 변수명을 지정한다.
    • paginate_by  : ListView와 날짜 기반 뷰(예, YearArchiveView)에서 사용한다. 페이징 기능이 활성화 된 경우 페이지당 출력 항목 수를 정수로 지정한다.
    • date_field : 날짜 기반 뷰(예, YearArchiveView)에서 사용한다. 이 필드의 타입은 DateField 또는 DateTimeField이다.
    • form_class : FormView, CreateView, UpdateView에서 폼을 만드는데 사용할 클래스를 지정한다.
    • success_url : FormView, CreateView, UpdateView, DeleteView에서 폼에 대한 처리가 성공한 후 리디이렉트할 URL 주소이다.
  • 메소드 오버라이딩
    • def get_queryset() : 기본 뷰(View, Template, RedirectView) 3개를 제외하고 모든 제너릭 뷰에서 사용한다. 디폴트는 queryset 속성을 반환한다. queryset 속성이 지정되지 않은 경우 모델 매니저 클래스의 all() 메소드를 호출해 QuerySet 객체를 생성해 반환한다.
    • def get_context_data(**kwargs) : 뷰에서 템플릿 파일에 넘겨주는 컨텍스트 데이터를 추가하거나 변경하는 목적으로 오버라이딩한다.
    • def form_valid(form)

<모델을 지정하는 방법 3가지>

  1. model 속성 변수 지정
  2. queryset 속성 변수 지정
  3. def get_queryset() 메소드 오버라이딩

##실습

1. views.py 작성하기 전

  1. django-admin startproject <project-name>
  2. cd <project-name>
  3. python manage.py startapp <app-name>
  4. settings.py에 App, rest_framework추가
  5. models.py에 모델 작성 및 migrate
  6. serializer.py생성 및 작성

2. views.py 작성하기

   1) 필요한 모듈 추가하기

       - 사용할 모델, serializer , rest_framework 의 generics 추가

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

    2) PostList 클래스 작성하기

     -  generics의 ListCreateAPIView 는 mixins에서 사용한 list 함수와 create 함수를 가지고 있는 class 이다.

class PostList(generics.ListCreateAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

    3) PostDetail 클래스 작성하기

     - genercis의 RetrieveUpdateDestoryAPIView 또한 mixins에서 사용한 함수들과 동일한 기능

class PostDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

3. urls.py 작성하기

- 앱 내부에 urls.py 생성 및 작성

from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from . import views

urlpatterns = [
    path("post", views.PostList.as_view()),
    path("post/<int:pk>", views.PostDetail.as_view()),
]

urlpatterns = format_suffix_patterns(urlpatterns)

- 프로젝트 폴더 내부의 urls.py 에 include 

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

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

-> 실행 결과 : http://127.0.0.1:8000/post로 접속하면 잘 작동하는 것을 확인할 수 있다.

 

5. ViewSet

rest_framework의 viewsets.py내부에는 4개의 클래스가 존재한다.

  • ViewSet
  • GenericViewSet
  • ReadOnlyModelViewSet
  • ModelViewSet

모두 간단하게 구성되어 mixins와 GenricViewSet, APIView등을 묶는 역할을 한다.

 

ReadOnlyModelViewSet

- 상속 받은 클래스

  • mixins.RetrieveModelMixin
  • mixins.ListModelMixin
  • GenericViewSet

상속받은 클래스로 보아 해당 viewset 역할은 retrieve 함수와 list 함수 기능을 가지고 있다

- 기능 : 특정 객체 가져오는 역할(retrieve)  / 객체 목록을 가져오는 역할(list)

 

ReadOnly를 수행하는 viewSet 이다

 

아래와 같이 작성하면

기존 postlist, postDetail 클래스의 기능을 따로 작성하지 않아도 ReadOnlyModelViewSet을 상속받아 사용할 수 있다.

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

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

ModelViewSet

모델과 Serializer 을 사용할 건지 등록한다

permission_classes는 action을 수행할 수 있는 권한을 설정하는 변수

  • IsAuthenticatedOrReadOnly : 권한이 없을 경우 ReadOnly
  • IsOwnerOrReadOnly : 소유자가 아닐 경우 ReadOnly
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response

class SnippetViewSet(viewsets.ModelViewSet):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer
    permission_classes = [
        permission.IsAuthenticatedOrReadOnly,
        IsOwnerOrReadOnly,
    ]

    @action(detail=True, renderer_classes=[renderers.StaticHTMLRenderer])
    def highlight(self, request, *args, **kwargs):
        snippet = self.get_object()
        return Response(snippet.highlighted)

    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)

-> customAPI 함수는 default로 GET 메서드로 처리된다. / 다른 메서드로 처리하고 싶다면 action의 format 인자로 지정 가능

 

%action decorator

@는 decorator장식자라고 부른다.
ViewSet을 이용하면 CRUD는 간단하게 구현되나 다른 논리들을 구현할 때
@action를 사용한다. 개발자가 임의로 View를 설계할 수 있도록 해준다.

 

%renderer_classes

Response를 어떤 형식으로 Rendering시킬 것인가를 정의

  • renderer.JSONRenderer
  • renderer.BrowsableAPIRenderer
  • renderer.StaticHTMLRenderer
  • TemplateHTMLRenderer

등 여러가지가 있다. 기본 설정은 JSONRenderer와 BrowsableAPIRenderer다.

 

##실습해보기

viewSet 적용하기

1. views.py 작성하기 전

  • django-admin startproject <project-name>
  • cd <project-name>
  • python manage.py startapp <app-name>
  • settings.py에 App, rest_framework추가
  • models.py에 모델 작성 및 migrate
  • serializer.py생성 및 작성

2. views.py 작성하기

    1) 필요한 모듈 추가

from .models import Post
from .serializer import PostSerializer
from rest_framework import viewsets
from rest_framework import renderers
from rest_framework.decorators import action
from django.http import HttpResponse

    2) ReadOnlyModelViewSet을 상속해 클래스 작성하기

      - viewsets의 ReadOnlyModelViewSet을 상속받아 작성한다. ReadOnlyModelViewSet을 상속받았으므로 읽기만 가능하다.

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

3. urls.py 작성하기

- post 앱 내부의 urls.py

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

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

urlpatterns = [
    path('post', 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)),
]

>>실행결과 

-http://127.0.0.1:8000/로 접속하면 아래와 같은 결과를 확인할 수 있다.
DefaultRouter를 사용했기 때문에 PATH가 ""일때도 화면이 나온다.

 

-http://127.0.0.1:8000/post로 접속하면 사용가능한 HTTP Method가
ReadOnlyModelViewSet을 상속받았으므로 GET밖에 없다.

 

4. ModelViewSet 상속해 클래스 작성하기

viewsets의 ModelViewset 상속받은 PostViewSet 클래스 작성

사용할 모델과 serializer을 등록하는 것은 기존과 동일

추가적으로 @action 데코레이터를 이용해 custom api 작성

get 방식으로 @action Custom API Test를 띄우는 highlight 함수 작성

 

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

    @action(detail=True, renderer_classes=[renderers.StaticHTMLRenderer])
    def highlight(self, request, *args, **kwargs):
        return HttpResponse("@action Custom API Test")

post로 처리하고 싶다면 @action(method=['post']) 와 같이 작성

 

>>실행결과

http://127.0.0.1:8000/post로 접속하면 사용가능한 HTTP Method가 GET외에도 POST가 추가된 것을 확인

 

Custom API 테스트

>http://127.0.0.1:8000/post/2/와 같이 Detail페이지로 접속하면 Extra Actions 스크롤 메뉴가 생긴 것을 확인할 수 있다.

스크롤 메뉴에서 highlight 버튼 클릭 시 아래와 같은 결과가 생성된다

6. Router

viewSet 복습

- ReadOnlyModelViewSet

  • retrieve()와 list()의 기능

- ModelViewSet

  • list(), create(), retrieve(), update()
    partial_update(), destroy()의 기능

path 

viewSet을 하나의 path 함수로 처리할 수 있을까?

하나의 path로 ListView, DetailView 의 CRUD가 모두 처리 가능할까??

 

===> 불가능하다 / PATH를 하나로 묶어줄 방법이 필요하다

 

as_view

as_view({'http_method' : '처리할 함수'})

예시

path = PostViewSet.as_view({
    'get': 'retrieve',
    'put': 'update',
    'patch': 'partial_update',
    'delete': 'destroy'
})

urlpatterns = [
    path('', path)
]

router : 관례적인 url 맵핑 단계를 자동으로 구현해주는 방식

 

<사용 방법>

1. rest_framework의 router에서 DefaultRouter 추가

from rest_framework.router import DefaultRouter

2. DefaultRouter 객체 생성 및 등록

-> URL Prefix : 127.0.0.1:8000/post와 같은 URL에서 post부분

router = DefaultRouter()
router.register(<URL Prefix>, <View Logic>)

3.  urlpatterns에 router의 urls 추가

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

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

[Django_심화]4 - (1)pagination  (0) 2020.03.18
[Django] Instagram 클론 코딩(1)  (0) 2020.03.17
[Django_심화]2 - Rest, Serializer  (0) 2019.11.03
[Django_심화]1 - 준비운동  (0) 2019.11.02
[6주차]Form  (0) 2019.03.06
반응형

1.Rest Architecture 

-REST란? : Representational State Transfer  / “자원을 이름으로 구분하여 상태를 전송하는 방법”

 

-Rest 설계 필요 충분 조건

   1. SERVER – CLIENT 

   2. STATELESS #클라이언트의 이전 상태를 기록하지 않는 연결방식을 취함 

   3. CACHE #캐쉬 처리기능 

   4. UNIFORM INTERFACE #일관된 인터페이스 

  • Resource가 URI로 식별이 되어야한다.
  • CRUD를 HTTP메세지로 수행 가능해야한다.
  • 메세지는 자체로 모든 것이 설명 가능해야한다.
  • 어플리케이션의 상태는 하이퍼링크를 이용해 전이되야한다.(HATEOAS)

   5. LAYERED SYSTEM #다칭적인 구성

   6. CODE-ON-DEMAND #JS처럼 서버와 같이 원격지에서 보낸 코드로 메소드를 실행시킬 수 있는 것

 

-API란? Application Program Interface / Request, Response로 오가는 구조화된 데이터

-Rest API  : REST의 설계방식을 잘 따르는 API / 대부분의 API는 rest를 잘 따르지 않는다 / Json은 모든 태그가 만들어져 있지않고, 만든 이가 정의하기 나름이기 때문에

 

    -> 필요한 이유?

         : 백엔드, 프론트엔드 분리 / 독립적인 네트워크 향상 가능 / 클라이언트 플랫폼 다양화 지원 가능

 

Django VS Django Rest Framework

  Django Djnago Rest Framework
만들어지는 것 웹 어플리케이션 RESTful API
전달하는 데이터 HTML Form(HTML, CSS, JS) JSON 데이터
데이터를 담는 곳  Form / ModelForm Serializer/ ModelSerialier
model로 부터 Field 읽어온다
유효성 검사

 

Form VS Serializer

 

 

Serializer vs ModelSerialier

Form과 ModelForm이 존재하듯이 Model만 작성할 줄 알면
더 간편하게 Serializer를 사용할 수 있게하는 ModelSerialier에 대해서 공부한다.
ModelSerialier는 쿼리셋과 모델 직렬화를 알아서 해준다.

 

2.Serializer 실습 

가상환경 생성

$python -m venv myvenv

 

필요 패키지 설치

$pip install djangorestframework

$pip install django

 

django 프로젝트 생성

$django-admin startproject rest

-> 생성 후 cd rest 로 폴더 이동

 

django app 생성

$ python manage.py startapp post

 

settings.py에 앱 추가(장고 프레임워크도 등록)

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'post',
]

url 관리

   -> 앱 폴더 내부에 urls.py 생성(계층적 url 관리 위해 분할) ##아니 나는 urls.py를 완성하지 못하면 마이그레이트가 안된다.. 이거먼저!

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

router = DefaultRouter() #ROUTER 객체 생성
router.register('post', views.PostViewSet) #ROUTER 객체의 REGISTER 함수(첫번째 인자 = URL, 함수)

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

#rest_framework는 ROUTER을 사용해 URL을 결정한다
#CRUD 기반으로 URL을 설정한다

  

    -> 프로젝트 폴더 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)),
]

앱 폴더의 models.py 에 모델 클래스 정의 (post/models.py)

from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=100)
    body = models.TextField()
# Create your models here.

앱 폴더 내부에 serializer.py 생성 및 작성 (어떤 클래스의 어떤 필드를 사용할 것인지)

from .models import Post
from rest_framework import serializers

class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = ['title', 'body'] 
        # 모든 필드를 사용할 경우 '__all__' 사용 가능

views.py 작성하기

-> rest_framework 의 viewsets 모듈과 작성한 모델serializer 추가

 

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

class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer
    
Django Rest Framework는 Class base View 이기에 
view로서 class를 작성한다
viewsets의 ModelViewSet을 상속받는다.

 

##ROUTER

▪ ViewSet에는 여러가지 메소드(함수)들이 있다 

      ex) retrieve() , destroy(), …

▪ 이 모든 것을 하나의 path()로 처리해 주는 것은 불가능하다.

▪ Path함수를 묶어준다 == path함수의 두번째 인자로 오는 함수를 묶는다 

     -> Path(요청을 처리할 url, 요청을 인자로 받아 처리할 함수, namespace)

 

#실행 결과(api 서버 보기)

1. api root : urls.py 에 작성된 DefaultRouter 에 의해 만들어진 기본적인 환경 / 처음 보여지는 화면은 GET 요청을 할 수 있는 URL이 보여진다.

 

2.POST API

-request : 127.0.0.1/8000/post

 

-response

 

post 후 post/1로 들어가면 allow 부분에 put이 들어간 것을 볼 수 있음

각 객체에 pk 값 부여하기 

-> serializer.py 파일의 PostSerializer 을 수정한다

class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = ['id', 'title', 'body'] 

새로 고침 후 보면 id 값을 직접 확인할 수 있는 것을 볼 수 있다. 

id 값 : 각 인스턴스에 붙는 고유 번호

Read Only 설정하기

 -> serializer.py 파일의 PostSerializer을 수정한다

 -> read_only_fields 에 read_only 속성을 적용할 필드를 tuple에 추가한다

 -> 수정 시 read_only 부분이 true로 바뀌는 것을 볼 수 있다.

 -> write_only부분도 같은 방법으로 수정하면된다!

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

반응형

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

[Django] Instagram 클론 코딩(1)  (0) 2020.03.17
[Django_심화]3 - viewset & router  (2) 2019.11.03
[Django_심화]1 - 준비운동  (0) 2019.11.02
[6주차]Form  (0) 2019.03.06
[5.5주차]Faker  (0) 2019.03.05
반응형

1. JSON & Http Resquest & Response & Httpie

  • JSON 
    • Java Script Object Notation 의 약자
    • 데이터 송수신을 자바스크립트의 문법으로 표현된 객체로 수행할 수 있게 만든 가벼운 문자열 데이터 표현식
    • 자바스크립트를 사용하지 않아도 사용 가능하다!
    •  웹에서는 Server와 Client 사이에는 여러 항목을 주고 받음. /구조를 나타내는 HTML /스타일을 표현하는 CSS /논리를 구현하는 Javascript
    • 앞으로의 강의들은 데이터인 JSON 만 주고 받음!
  • JSON 문법 형식
    • Python 의 딕셔너리와 비슷한 구조
    • name : value
    • 숫자, 문자(열), Boolean, 배열, 객체 등으로 구성 가능 
    • JSON  데이터를 보낼 때에는 직렬화(Serialization) 과정을 거쳐 JSON 데이터를 문자열로 바꾸어 상대방에게 보낸다.
    • 데이터를 보낼 때에는 JSON  -> 문자열로 / 데이터를 받을 때에는 문자열 -> JSON으로 바꾸어 받는다.
json_example = { 
	"string_name": "something",
    "number_name": 3,
    "null_name": null,
    "bool_name": true
}
  • Python에서 JSON 사용하기
    • Python 딕셔너리 자료형을 반환해보자
import json //Json을 처리하는 라이브러리를 제공
diary = {
	"id": 3,
    "title": “멋쟁이사자처럼”,
    "body“: “2학기도 파이팅”
}

print(diary)
//{‘id’: 3, 'title': ‘멋쟁이사자처럼’, ‘body’: ‘2학기도 파이팅’}

print(type(diary))
//<class ‘dict’>
  • Dict to JSON 
    • JSON의 dumps 함수를 이용해 변환 진행
    • 데이터를 송신할 때 사용할 수 있다.
    • dic 이였던 자료형이 str로 변환
diary_s = json.dumps(diary)
print(diary_s) 
//{‘id’: 3, 'title': ‘멋쟁이사자처럼’, 'body': ‘2학기도 파이팅’}
print(type(diary_s)) 
//<class ‘str’>
  • JSON to Dict
    • JSON 의 load 함수를 이용해 변환을 진행
    • 데이터를 수신할 때 사용할 수 있다
    • Str 자료형이 dict로 다시 변환되었음을 볼 수 있다.
diary_b = json.loads(diary)
print(diary_b) 
//{‘id’: 3, 'title': ‘멋쟁이사자처럼’, 'body': ‘2학기도 파이팅’}
print(type(diary_b)) 
//<class ‘dict’>

Http Requsest & Response

  • 정의
    • Http란?
      • 웹 상에서 통신을 주고 받을 수 있는 주체, Client와 Server 그 중, Server의 역할은 Request 를 가공해서 처리해주는 역할
      • Web 상에서 지원하는 통신 규약을 Http
      • 한마디로 정의한다면?
        • HttpRequest : 요청에 대한 메타 정보를 가지고 있는 객체 
        • HttpResponse : 요청에 대한 메타 정보를 가지고 있는 객체
    • Django 에서 Http Method
GET POST
빈 입력공간 가져와 입력한 내용 처리
URL에 데이터 정보 포함 URL에 데이터 정보 포함 안함

        ▪ Django 에서 Rest FrameWork에서 Http Method

Method 기능
GET 요청받은 URL의 정보 검색하여 응답
POST 요청된 자원을 생성(CREATE)
DELETE 요청된 자원 삭제
PATCH 요청된 자원 일부 수정
OPTION 웹 서버에 지원된 메소드 종류 확인

 

 

 Django Rest FrameWork?

-> 장고 안에서 RESTful API 서버를 쉽게 구축할 수 있도록 도와주는 오픈 소스라이브러리

 

-Rest?

    -> HTTP 의 URL과 HTTP Method(GET, POST, PUT, DELETE)를 사용해서

         API 사용 가독성을 높인 구조화된 시스템 아키텍쳐(Framework)

 

-RESTful 아키텍쳐를 HTTP Method와 함께 사용해서 웹, 데스크탑 앱, 어플리케이션 까지 구동할 수 있는 서버를 만든다고 생각!

 

  • Django Rest Framework API Server에서는 JSON을 주고 받음
  • 서버 Response 종류 
    • 1XX (정보) -> 요청을 받았으며 프로세스를 계속한다.
    • 2XX(성공) -> 요청을 성공적으로 받았으며 인식했고 수용했다. 
    • 3XX(리다이렉션) -> 요청을 완료하기 위해 추가 작업 조치가 필요하다.
    • 4XX(클라이언트 오류) -> 요청의 문법이 잘못되었거나 요청을 처리할 수 없다.
    • 5XX(서버 오류) -> 서버가 명백히 유효한 요청에 대해 충족에 실패했다.

Httpie

- 요청을 Command Line으로 동작하는 파이썬 기반 프로그램 (=http client)

- 다른 http Client 보다 가볍고 설치하기 편하기 떄문에 사용

 

-httpie 설치

>>pip install --upgrade httpie

 

-httpie 명령어

>>http [flags] [METHOD] URL [ITEM[ITEM]]

 

flags : 옵션

▪ METHOD : HTTP METHOD

▪ URL : 요청이나 응답의 대상 URL

▪ ITEM : 처리할 값 (인자)

▪ POST / PUT : =로 표현

▪ GET : ==로 표현

 

▪ Httpie 명령어 사용 예시 

     ▪ GET

          http(GET) 대상주소 인자 ==값

 http (GET) example.org a==1 b==2
 http example.org a==1 b==2 


      ▪ PUT

           http(PUT) 대상주소 GET인자 ==값 PUT인자=값

 http PUT example.com a==1 b==2


▪ Get 인자가 있으면 쓰고 없으면 안쓴다. ▪ Put 인자가 있으면 쓰고 없으면 안쓴다.

 

2. CBV

-장고의 view

    - 호출 가능한 객체(callable object) 로 정의

        -> 함수 / 클래스

    - 함수와 클래스의 차이 : 함수 (상속x) / 클래스 (상속o)

    - 상속을 사용하는 이유는 ? 중복의 제거 / 코드의 재사용

 

따라서 CBV를 사용하는 이유는 상속을 사용해 코드를 더 간단하게 작성하기 위해 사용

 

-FBV(Function Based View)  : 함수 기반 뷰 

 

##실습하면서 익혀봅시다

순서 : 프로젝트 생성 -> 앱 생성 -> 템플릿 추가

 

가상환경 생성 및 활성화

$python -m venv myvenv

$ source myvenv/Scripts/activate

 

프로젝트 생성

$ django-admin startproject CBV

$ cd CBV

 

앱 생성 

$python manage.py startapp classBaseCrud

 

settings.py 에 앱 추가

INSTALLED_APPS = [
    'classBaseCrud',
]

 models.py 작성하기

- 클래스 생성

from django.db import models

# 클래스를 생성하여 제목, 생성일, 수정일, 본문 필드 작성

class ClassBlog(models.Model):
    title = models.CharField(max_length=100)
    created_at = models.DateTimeField(auto_now_add=True)
    #auto_now_add 옵션 : model이 최초 저장시에만 현재 날짜 적용
    updated_at = models.DateTimeField(auto_now=True)
    #auto_now 옵션 : 데이터가 저장 될 때마다 현재 날짜로 갱신
    body = models.TextField()

    def __str__(self):
        return self.title

-admin.py에 생성한 클래스 등록

from django.contrib import admin
from .models import ClassBlog

admin.site.register(ClassBlog)

-마이그레이션

$ python manage.py makemigrations
$ python manage.py migrate

 

views.py 작성하기

1) 사용할 모듈 추가 - CRUD

게시글 목록 ListView
새로운 게시글 생성 CreateView
게시글 상세 내용 DetailView
게시글 수정 UpdateView
게시글 삭제 DeleteView

    -Generic Views : 웹 개발 시 자주 사용하는 기능을 장고에서 미리 제공 / 외부 모듈 적용이 필요하다

 

      #사용할 모듈 추가 CRUD

from django.utils import timezone

from django.urls import reverse_lazy

from django.views.generic.list import ListView
#게시글 목록 보여줌

from django.views.generic.detail import DetailView
#게시글의 상세 내용을 보여줌

from django.views.generic.edit import CreateView, UpdateView, DeleteView
#게시글 생성, 수정, 삭제 

from .models import ClassBlog

2)모듈을 상속해 클래스 작성(classBaseCrud/views.py)

-CREATE

#CREATE
class BlogCreate(CreateView):
    model = ClassBlog
    fields = ["title", "body"]
    success_url = reverse_lazy('list')
    #reverse_lazy 
    #- redirect 와 같은 기능으로 , generic view에서 주로 사용
    #- reverse_lazy() 함수 사용 인자로 URL 이름을 넣어줌
    #- 작업이 성공적으로 완료되었을 경우 해당 URL로 이동

-READ

#READ
class BlogView(ListView):
    model = ClassBlog
    template_name = 'classBaseCrud/list.html'
    context_object_name = 'blog_list'
    # List Views
    #- Generi view 중 특정 모델의 리스트를 출력해주는 뷰
    #- 모델에 대한 쿼리가 필요하고(queryset)
    #- 어느 템플릿에 (template_name)
    #- 어떤 파라미터명으로 전달할지 (context_object_name) 를 정의
    #- 약속되어있는 html 이름을 사용하지 않고 작성하기 위함
    #- 해당 html을 사용할 class에 template_name을 설정하면 된다
    
    #context_object_name : 임의로 설정하여 기본적으로 설정되어있는 객체를 가져옴
class BlogRead(DetailView):
    context_object_name = 'blog'
    model = ClassBlog
    #DetailView
    #- 특정 모델의 특정 오브젝트에 대한 자세한 정보를 출력해주는 뷰
    #- 어떤 모델의 어떤 오브젝으를 어떤 템플릿에 전달할지 정의

-UPDATE

#UPDATE
class BlogUpdate(UpdateView):
    model = ClassBlog
    fields = ["title", "body"]
    success_url = reverse_lazy("list")

-DELETE

#DELETE
class BlogDelete(DeleteView):
    model = ClassBlog
    success_url = reverse_lazy("list")

앱 내부에 urls.py 생성 및 작성

- 작성할 클래스에 맞는 url path 작성

- (urls.py / classBaseCurd)

from django.urls import path
from .import views

#작성할 클래스에 맞는 url path 작성
urlpatterns = [
    path("", views.BlogView.as_view(), name='list'),
    #as_view : view를 하나 만들어서 이를 return
    #함수가 하나 만들어지는 것과 같음
    #클래스를 정의한 후 이에 해당하는 함수 만들어 사용하는 방식

    path("create/", views.BlogCreate.as_view(), name="create"),
    path("read/<int:pk>/", views.BlogRead.as_view(), name="read"),
    path("update/<int:pk>/", views.BlogUpdate.as_view(), name="update"),
    path("delete/<int:pk>/", views.BlogDelete.as_view(), name="delete"),
]

프로젝트 urls.py 에 앱 urls.py 추가

- (urls.py / CBV)

from django.contrib import admin
from django.urls import path, include
#django.urls 의 include 모듈 추가
#앱의 urls.py 파일을 프로젝트 urls.py에 추가
import classBaseCrud.urls
import classBaseCrud.views

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

templates 생성하기

- 수행 결과를 보여주는 HTML 템플릿이 존재해야하고 
ListView 는 블로그 리스트 HTML / CreateView, UpdateView 는 입력공간(form)을 갖는 HTML

DetailView는 상세 페이지 HTML / DeleteView 는 삭제 작업을 확인하는 HTML 필요

 

- Generic View 에서는 기본적으로 설정된 이름이 존재

- 다른 이름으로 HTML 을 작성하기 위해서는 추가 설정이 필요

 

View 이름 HTML 이름
ListView (소문자 모델)_list.html
CreateView (소문자 모델)_form.html
DetailView (소문자 모델)_detail.html
UpdateView (소문자 모델)_form.html
DeleteView (소문자 모델)_confirm_delete.html

 

##template폴더 구성할 때 template 안에 HTML 파일을 작성하는 게 아니라 App 이름의 폴더를 작성하고 그 안에 작성

앱 안에 templates라는 폴더를 만들고 그 안에 앱 이름으로 폴더를 만들어 그 안에 html를 만들어야함

#classblog_list.html

<!DOCTYPE html>
<html lang="ko" dir="ltr">
    <head>
        <meta charset="utf-8">
        <title>Class Base View</title>
    </head>
    <body>
        <a href="{% url 'create' %}"> 추가 </a>
        
        <br>
        
        {% for blog in blog_list %}
        <h2> {{ blog.title }} </h2>
        <h2> {{ blog.created_at }} </h2>
        <p> {{ blog.body }} </p>
        
        <a href="{% url 'read' blog.id %}"> 자세히보기 </a><br>
        <a href="{% url 'update' blog.id %}"> 수정 </a><br>
        <a href="{% url 'delete' blog.id %}"> 삭제 </a><br>
        {% endfor %}
    </body>
</html>

#classblog_form.html

<form method="POST">
    {% csrf_token %}
    {{ form.as_p }}
    <input type="submit" value="추가하기">
</form>

//HTTP POST, PUT, DELETE 🡪 {% csrf token %} 사용
//{{ form.as_p}} : view에서 전달한 form 객체를 템플릿 변수로 사용 / 폼의 각 필드를 p태그 안에 배치

#classblog_detail.html

<h1> 본문 자세히 보기 페이지 </h1>

<hr>

<h2> 제목 : {{ blog.title }} </h2>
<h4> 날짜 : {{ blog.created_at }} </h4>
본문 : {{ blog.body }}

#classblog_confirm_delete.html

<form method="POST">
    {% csrf_token %}
    <h2>진짜 지울거야?</h2>
    <br>
    <br>
    <input type="submit" value="삭제">
</form>

##결과

CREATE
DETAIL
UPDATE(수정)
DELETE
생성 후 리스트 목록

반응형

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

[Django_심화]3 - viewset & router  (2) 2019.11.03
[Django_심화]2 - Rest, Serializer  (0) 2019.11.03
[6주차]Form  (0) 2019.03.06
[5.5주차]Faker  (0) 2019.03.05
[5주차]2.Pagination  (0) 2019.03.05
반응형

1. Aggregation(집계)

  • 집계 연산 : group values from multiple documents together, and perform a variety of operations on the grouped data to return a single result
  • 집계 작업은 데이터 레코드를 처리하고 계산 된 결과를 반환
  • 집계 작업은 여러 문서의 값을 함께 그룹화하고 그룹화 된 데이터에 대해 다양한 작업을 수행하여 단일 결과를 반환 할 수 있다.
  • 3가지 유형의 집계 연산 기능 제공
    • 집계 파이프라인
    • 맵-리듀스 함수
    • 단순 목적 집계 메소드

Aggregation 프레임워크

  • 집계 파이프라인
    • 여러 단계를 거쳐 최종 결과 산출
      • 각 단계 마다 다양한 파이프라인 작업 가능
        • ex) $project , $group , $match, $limit, $skip, $sort , 등 10개 연산자
      • 각 단계의 출력 결과가 다음 단계의 입력으로 제공
      • 문서를 여러 단계의 파이프 라인으로 처리해, 데이터를 처리/집계 한다고 이해
    • 집계 프레임워크에서 사용 문법
      • $project : 컬렉션에서 특정 필드를 선택하는데 사용
      • $match : 필터링 작업 / 다음 단계에 입력으로 제공되는 문서의 양 줄임
      • $group : 위에서 설명한 실제 집계 수행
      • $sort : 문서 정렬
      • $skip : 주어진 양의 문서에 대한 문서 목록에서 건너 뛸 수 있음
      • $limit : 현재 위치에서 시작하여 주어진 숫자로 볼 문서의 양 제한
      • $unwind : 배열을 사용하는 문서 되 감는데 사용 / 배열을 사용할 때는 데이터가 미리 결합 되어 있으며 , 이를 통해 작업을 취소하여 개별 문서를 다시 가질 수 있음 
        -> 다음 단계의 문서 양을 늘림
         
      • 정의 
db.컬렉션명.aggregate([<작업1>, . . . <작업n>])

db.mycol.aggregate([{$match:..}, {$group:..}, {$sort:..}])
//<작업n>은 문서 형식 {키 : 값} 으로 명세

mycol 컬렉션 -> $match 작업 -> $group 작업 -> $sort 작업 -> 출력 문서

 

1. 

2.

 

3. 최종

 

> db.mycol.aggregate([{$match:{by_user:“Neo4j”}}, { $unwind : “$tags”}, {$project:{“_id:0”, “title”:1, “tags”:1}} ])
{“title”:“Neo4j Overview”, “tags”:“neo4j”}
{“title”:“Neo4j Overview”, “tags”:“database”}
{“title”:“Neo4j Overview”, “tags”:“NoSQL”}

  • SQL과의 비교(SQL count(*) 에서 group by 는 mongodb 집계와 동일)
select $project, $group 함수($sum, $min, $avg 등)
from  aggregate()
join $unwind
where $match
group by $group
having $match

 

  • aggregation 표현식 목록
> db.mycol.aggregate([ {$group : { _id : "$by_user“ , num_tutorial : {$sum : 1} }} ])
//출력될 문서의 형식 { _id : 값1, num_tutorial : 값2 }
//$가 붙은 문자열 필드 값은 일반 문자열 값이 아닌 입력 문서의 특정 필드를 지정

> db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : 1}}}])
{
    "result" : [
        
{
        
"_id" : "tutorials point",
        
"num_tutorial" : 2
        
},
       
{ "_id" : "Neo4j",
         "num_tutorial" : 1
         }
    
], "ok" : 1
}//각 사용자가 작성한 자습서 수 나타내는 목록
  • $sum 
    -> 각 그룹에 대해 그 그룹에 속하는 문서가 있을 때 마다 num_tutorial 필드 값을 1씩 증가
  • $avg
    -> 컬렉션의 모든 문서에서 제공된 모든 값의 평균 계산
  • $min 
    -> 컬렉션의 모든 문서에서 해당 값의 최소값 가져옴
  • $max
    -> 컬렉션의 모든 문서에서 해당 값의 최대값 가져옴
  • $push 
    -> 결과 문서의 배열에 값 삽입
  • $addToSet
    -> 결과 문서의 배열에 값을 삽입하지만 중복은 생성하지 않는다
  • $first
    -> 그룹화에 따라 소스 문서에서 첫 번째 문서를 가져옴 / 이전에 적용된 일부 "$sort" 단계와 만 의미 O
  • $last
    -> 그룹화에 따라 소스 문서에서 마지막 문서를 가져옴 / 이전에 적용된 일부 "$sort" 단계와 만 의미 O
반응형
반응형

쿼리문 예시 보기 좋은 사이트 입니다. 참고 ~

https://docs.mongodb.com/manual/tutorial/query-documents/
https://docs.mongodb.com/manual/reference/sql-comparison/

1. 중첩된 문서를 포함하는 문서에 대한 질의(embedded document 추가 예시)

> db.myCol.insert( { “name”: “kim”, “address”:{ “city”:“seoul”, “gu”:“nowon” }
  • 중첩된 문서 전체를 비교하는 질의
> db.myCol.find( {“address”:{“city”:“seoul”, “gu”:“nowon”} })
  • 중첩된 문서의 특정 필드 값 비교 질의
    • 필드 구분자로 '.' 을 사용
    • 반드시 ""로 묶어야 함
// partial match (중첩된 문서의 일부 필드만 일치 여부 비교)
> db.myCol.find( {“address.city”:“seoul”})
> db.myCol.find( {“address.gu”:”nowon”, “address.city”:“seoul”})
> db.myCol.find( {“address.gu”:”nowon”})
  • 배열을 포함하는 문서에 대한 질의1
> db.myCol.insertMany( [
{“name”: “cho”, “hobby”:[“football”, “swimming”]},
{“name”: “kim”, “hobby”:[“game”, “swimming”, “football”]}
])
  • 배열 전체 비교 질의

          - 완전 일치 

// exact match (배열 요소의 값, 개수, 순서까지 일치)
> db.myCol.find( {“hobby”:[“football”, “swimming”] })

       - 부분 일치(주어진 값들의 순서 상관 없이 배열 요소로 존재하는지 질의)

// partial match
> db.myCol.find( {“hobby”:{ $all:[“football”, “swimming”] } })
  • 배열 포함하는 문서에 대한 질의2
> db.myCol.insertMany( [
{ “name”: “joo”,
“hobby”:[“football”, “swimming”],
“points”:[25, 28, 30, 34] },
{“name”: “park”,
“hobby”:[“game”, “football”, “swimming”],
“points”:[27, 30, 40] }
])
  • 배열의 특정 요소(들)에 대한 질의
// 배열 hobby의 요소 값 중에 “swimming”을 내용으로 가지는 문서 검색
> db.myCol.find( {“hobby”:“swimming” })

// 배열 points의 요소 값 중에 하나라도 34보다 큰 값을 가지는 문서 검색
> db.myCol.find( {“points”:{$gt:34} })

// 배열 hobby의 두 번째 요소 값을 “swimming”으로 가지는 문서 검색
> db.myCol.find( {“hobby.1”:“swimming” })

// 크기가 3인 배열 hobby를 포함하는 문서 검색
> db.myCol.find( {“hobby”:{$size:3} })
  • 배열을 포함하는 문서에 대한 질의3
> db.myCol.insertMany( [
{ “name”: “joo”, “points”:[25, 27] },
{ “name”: “min”, “points”:[14, 21] },
{ “name”: “yoo”, “points”:[19] }
])
  • 배열 요소(들)에 대한 여러 개의 조건 가지는 질의
// 배열 points의 요소들 중에 (15보다 큰 조건만을 만족하는 요소도 있으면서 20보다 작은 조건을 만족하는 또 다른 요소도 있는 문서) 또는 (한 요소가 15보다 크면서 20보다 작은 조건을 모두 만족)하는 문서 검색
> db.myCol.find( {“points”:{$gt:15, $lt:20} })

// 배열 points의 요소들 중에 적어도 하나라도 (21보다 크고 26보다 작은 조건을 모두 만족)하는 요소를 가지는 문서 검색
> db.myCol.find( {“points”:{$elemMatch:{$gt:21, $lt:26}} })
  • 배열을 포함하는 문서에 대한 질의 4
    • 배열의 요소 값이 문서인 경우 질의 (심화 질의)
> db.myCol.insertMany( [
{ “name”:“cho”,“friends”:[{“name”:”ko”},{“name”:”joo”,“age”:24}] },
{ “name”:“yoo”,“friends”:[{“name”:”jon”, “age”:20},{“name”:”je”}] } ] )

Q1) 이름이 joo이고 나이가 24인 친구를 가지고 있는 사람의 이름은?
Q2) 나이가 22살 이하인 친구를 가지고 있는 사람의 이름은?
Q3) 첫 번째 친구의 나이가 20인 친구를 가지고 있는 사람의 이름은?

2. Indexing(인덱스)

  • 몽고 디비 쿼리에서 효율적인 실행을 지원
    -> 인덱싱이 없이 몽고디비는 콜렉션 스캔을 실행하여야 한다.
    -> ex) 쿼리문에 일치하는 도큐먼트 선별하기 위해 컬렉션 내의 모든 도큐먼트를 스캔해야한다
  • 인덱스 : 콜렉션의 데이터셋의 작은 일부를 저장하고 있는 특별한 데이터 구조
    -> 인덱스는 특정 필드 또는 필드 세트의 값을 인덱스에 지정된 필드 값 순서로 저장
    ->
    컬렉션내의 임의의 문서를 빠르게 접근하도록 하는 자료구조

    ->인덱스는 데이터 파일과는 별도의 파일에 저장됨
    ->검색 속도를 향상시키나 인덱스 저장 공갂 필요
    -> 삽입, 삭제, 수정 연산의 속도 저하
  • 인덱스의 구조
    -> 엔트리 구조 : < 탐색 키 필드, 문서에 대핚 포인터 >

    ->엔트리들은 탐색 키 필드 값의 오름차순으로 정렬되어 있음

 

  • 다단계 인덱스 : B, B+ 트리 사용

  1) ensureIndex() 메서드 

db.COLLECTION_NAME.ensureIndex({KEY : 1})

-> key 는 색인을 작성하려는 필드의 이름 / 1은 오름차순 / 내림차순으로 인덱스를 만들려면 -1

-> 여러 필드에 인덱스를 만들려면 여러 필드를 전달할 수 있다.

>db.mycol.ensureIndex({"title" : 1})
>db.mycol.ensureIndex({"title":1,"description":-1}) //여러 필드 전달
  • ensureIndex() 메소드는 옵션 목록도 허용합니다.
PARAMETER TYPE DESCRIPTION
background boolean 인덱스를 빌드해도 다른 DB 활동을 차단하지 않도록 백그라운드에서 인덱스를 빌드한다. 백그라운드에서 빌드하려면 TRUE / 기본값은 FALSE
unique boolean 인덱스 키가 인덱스의 기존 값과 일치하는 문서 삽입을 허용하지 않도록 고유 인덱스를 생성/ 고유 인덱스를 작성하려면 TRUE 지정 / 기본값은 FALSE
name string 인덱스의 이름/ 지정되지 않은 경우 mongodb는 인덱스화 된 필드의 이름과 정렬 순서를 연결하여 인덱스 이름을 생성
dropDups boolean 중복될 수 있는 필드에 고유 인덱스를 작성 / mongodb는 키의 첫번째 항목만 인덱스화하고 해당 키의 후속 항목이 포함된 콜렉션에서 모든 문서를 제거 / 고유 인덱스를 작성하려면 true를 지정/ 기본값은 false
sparse boolean true인 경우 인덱스는 지정된 필드가 있는 문서만 참조 / 이 인덱스는 공간을 덜 사용하지만 일부 상황(정렬..) 에서 다르게 동작 / 기본값 false
expireAfterSeconds Integer mongodb가 콜렉션에서 문서를 보유하는 기간을 제어하기 위해 TTL 값을 초 단위로 지정
v Index Version 색인 버전 번호 / 기본 인덱스 버전은 인덱스 작성시 실행중인 mongodb 버전에 따라 다름
weights Document 가중치는 1~99,999 범위의 숫자, 점수를 기준으로 다른 색인화 된 필드에 대한 필드의 중요성 나타냄
default_language string 텍스트 인덱스의 경우 중지 단어 목록과 형태소 분석기 및 토크 나이저에 대한 규칙을 결정하는 언어 / 기본값 english
language_override string 텍스트 인덱스의 경우 기본 언어를 대체 할 언어를 포함하는 문서의 필드 이름 지정 / 기본값은 언어

 

2. 커서(5주차 추가 메뉴얼)

  • 커서의 활용
    • find() 메소드 : 검색된 문서 집합에 대한 커서 반환
  • var 타입의 번수 사용하지 않는 경우
// mongo 쉘에서는 반환 결과를 변수에 할당하지 않으면
// 커서가 자동으로 20번 반복되어 문서 20개를 자동 출력 후 대기
// “it” 입력하면 커서 반복
> db.myCol.find()
  • var 타입의 변수 사용하는 경우
    • 반환된 문서 집합에 대해 커서를 수동으로 반복하여 개별적 문서 접근
> var myCursor = db.myCol.find()
> myCursor // 커서 20번 반복 -> 20개 문서 출력 후 대기
> var myCursor = db.myCol.find()
> while (myCursor.hasNext()) {
           print(tojson(myCursor.next()));
           // printjson(myCursor.next());
}
  • 커서 활용
    • 검색 결과의 문서 개수 출력
> db.myCol.find.count()
> var res = db.myCol.find.count()
> res

3. Index 메소드

인덱스 생성 메소드 //createIndex()

db.컬렉션명.createIndex(<키> , <옵션>)
// <키>는 문서 형식 {키:1 or -1, ... }으로 명세 (1:오름, -1:내림)
// <옵션>도 문서 형식 {키:값, ... }으로 명세
  • 단일 / 복합 인덱스 (single / compound index) 생성
db.mycol.createIndex({"age" : 1})
//단일 키 인덱스(하나의 필드만 사용)

db.mycol.createIndex({"deptname" : 1, "year" : -1})
//복합 키 인덱스 (2개 이상의 필드 사용)
//키의 순서 중요
  • 다중키 인덱스(multikey index)
    • 필드의 값이 배열(예, 취미)인 경우 생성되는 인덱스 (별도 지정 불필요)
    • 배열의 각 요소 마다 인덱스 엔트리 유지
      • 같은 배열에서 생성된 여러 엔트리들을 동일한 문서 참조(포인트)
  • 인덱스 생성시 인덱스명 작명
    • 시스템이 기본적으로 주는 기본 이름 대신 의미 있는 이름으로 작명
    • 기본 이름 : 인덱스 키 (필드) 와 정렬 기준(1 / -1) 값을 조합하여 작명
db.mycol.createIndex({username : 1} , {name : "userinfo_idx"})

 

  • 고유 인덱스 (unique index)
    • 컬렉션 내 동일한 인덱스 필드 값을 갖는 문서 중복 불가
    • _id 필드에 대한 고유 인덱스는 자동으로 생성
    • 문서 삽입 이전에 인덱스를 생성하는 것 좋음
db.mycol.createIndex({username : 1}, {unique : true})

 

  • 희소 인덱스(sparse index)
    • 인덱스 키 필드를 포함하는 문서에 대해서만 인덱스 엔트리를 유지
    • oop, 밀집 인덱스(dense index)
    • 모든 문서에 포함되지 않는 필드에 인덱스를 생성할 경우 유용
db.mycol.createIndex({fax : 1}, {sparse : true, unique : true})

 

인덱스 정보 확인 메소드 //getIndexes()

  • 한 컬렉션 내에 생성된 모든 인덱스 정보를 문서 배열로 반환
db.컬렉션명.getIndexes() 또는 db.컬렉션명.getIndexSpecs()

> db.mycol.getIndexes() [
  {
                “v”:1, // 인덱스 버전 번호 (미활용)
               “key”:{“username”:1}, // 인덱스 키 정보
               “ns”:“mycol.username”, // 네임 스페이스
               “name”:“username_1” // 인덱스명
   },
  { . . . }
]

 

인덱스 제거 메소드 //dropIndex()

  • 컬렉션 내의 특정 인덱스 제거
    • <인덱스_정보>  : 문서 형식으로 명세 : {"인덱스명" : 1 또는 -1}
db.컬렉션명.dropIndex("인덱스명" 또는 <인덱스_정보>)
> db.mycol.dropIndex(“stdinfo_idx”)
> db.mycol.dropIndex(“username_1”)
> db.mycol.dropIndex({“username”:1})

 

  • 여러 개의 인덱스 제거 메소드
    • 해당 컬렉션에 대한 모든 인덱스 제거 (단, _id 인덱스는 제외)
db.컬렉션명.dropIndexes()
반응형
반응형

Mongodb - Limit Records

-find() 메소드를 더욱 더 활용에 필요한 sort(), limit() , skip() 메소드

-find() 메소드를 사용했을 시  cursor 형태의 결과값을 반환, 이 객체가 가지고 있는 limit() , skip()메소드를 통해 보이는 출력물의 갯수를 제한, sort() 메소드를 사용하여 데이터를 순서대로 나열

 

1. limit() //db.콜렉션명.find().limit(number)

이 메소드는 출력할 데이터 갯수를 제한할 때 사용. value 파라미터는 출력 할 갯수 값

>db.mycol.find({}, {"title" : 1, _id:0}).limit(2)
  { "title" : "Mongodb"}
  { "title" : "Nosql"}

 

2. Skip() //db.콜렉션명.find().limit(number).skip(number)

출력 할 데이터의 시작부분을 설정할 때 사용.  value 값 갯수의 데이터를 생략하고 그 다음부터 출력

>db.mycol.find({}, {"title" : 1, _id : 0}).limit(1).skip(1)
  { "title" : "Mongodb"}

3.Sort() //db.콜렉션명.find().sort({key : 1})

이 메소드는 데이터를 정렬할 때 사용. 매개변수로는 어떤 KEY 를 사용하여 정렬 할 지 알려주는 document 를 전달

KEY 는 데이터의 field 이름이고, value 의 값은 1 혹은 -1 . 이 값을 1로 설정하면 오름차순으로, -1로 하면 내림차순으로 정렬

또한 여러 KEY를 입력 할 수 있고 먼저 입력한 KEY가 우선권을 가짐.

>db.mycol.find({}, "title" : 1, _id:0}).sort({"title" : -1}) //역순 출력

Mongodb 메뉴얼 (튜토리얼에는 없지만 중요하다)

Embbedded Document(내장형문서) 조회

 

MongoDB에서는 JSON객체 형태의 문서로 데이터를 저장하는데 문서안에 다시 문서의 내장된 문서의 형태로도 데이터를 저장할 수 있습니다. 그러한 내장형 문서를 조회를 해보겠습니다. 

 

연습을 위해 아래의 데이터를 Insert 합니다.

db.inventory.insertMany( [     
{ item: "journal", qty: 25, size: { h: 14, w: 21, uom: "cm" }, status: "A" } ,
{ item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status: "A" } ,
{ item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status: "D" } ,
{ item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status: "D" } ,
{ item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A" }
]);

 

아래는 위에서 실습한 기본적인 형태의 동등 조건의 조회식

 

size가 h 14이며 w 가 21 uom 이 cm 인 값을 찾습니다.

db.inventory.find( { size: { h: 14, w: 21, uom: "cm" } } )

이러한 형태는 하위 문서와 완전히 똑같은 문서만 찾을 수 있습니다.

 

 

아래는  하위문서와 특정 필드에 조건을 걸어 구하는 식.

 

-size 아래 문서 중 uom 값이 cm 인 값을 구하는 식.

db.inventory.find( { "size.uom": "cm" } )

-size 값 중 h값이 10보다 작은 값. 

db.inventory.find( { "size.h": {$lt : 10} } )


-size 값 중 h값이 10보다 작고 status 가 A 인 문서.

db.inventory.find( { "size.h": {$lt : 10}, status : "A"} )

 

배열값 조회

MongoDB는 배열형태의 값을 저장할 수 있습니다. 

 

연습을 위해 아래의 데이터를 저장합니다.

db.inventory.insertMany([
{ item: "journal", qty: 25, tags: ["blank", "red"], dim_cm: [ 14, 21 ] } ,
{ item: "notebook", qty: 50, tags: ["red", "blank"], dim_cm: [ 14, 21 ] } ,
{ item: "paper", qty: 100, tags: ["red", "blank", "plain"], dim_cm: [ 14, 21 ] },
{ item: "planner", qty: 75, tags: ["blank", "red"], dim_cm: [ 22.85, 30 ] } ,
{ item: "postcard", qty: 45, tags: ["blue"], dim_cm: [ 10, 15.25 ] }
]);


tags 필드에 오직 red값과 blank값만 배열에 존재하는 문서를 조회.

db.inventory.find( { tags: ["red", "blank"] } )
//위 경우는 red와 blank 만 포함된 경우만 출력합니다. 다른 것이 포함된 경우는 출력되지 않습니다.

 

 

 tags값에 red와 blank 값이 모두 존재하는 문서를 조회.

db.inventory.find( { tags: { $all: ["red", "blank"] } } )
//이 경우는  red와 blank를 포함하고 다른 것들이 포함된 경우도 함께 출력됩니다.

 

 

배열이 포함된 필드에 배열 조회가 아닌 단일 값만 조회 값으로 입력했을때의 결과.

db.inventory.find( { tags: "red"} )
//위 결과는 red가 배열에 포함된 모든 문서를 출력합니다.

 

 dim_cm 필드에 25이상 값이 포함된 문서를 출력.

db.inventory.find( { dim_cm: {$gte : 25}} )


 dim_cm필드에서 1번째 위치 즉 0번 인덱스에 있는 값 중 14이상인 값.

db.inventory.find( { "dim_cm.0": {$gte : 14}} )

 

tags 배열의 크기가 2인 문서를 조회합니다.

db.inventory.find( { tags : {$size : 2}} )

 

Embbedded Document내에서 배열값 조회

 

연습을 위해 아래 데이터를 입력.

db.inventory.insertMany( [     
{ item: "journal", instock: [ { warehouse: "A", qty: 5 } ,
{ warehouse: "C", qty: 15 } ] }
, { item: "notebook", instock: [ { warehouse: "C", qty: 5 } ] } ,
{ item: "paper", instock: [ { warehouse: "A", qty: 60 }, { warehouse: "B", qty: 15 } ] } ,
{ item: "planner", instock: [ { warehouse: "A", qty: 40 }, { warehouse: "B", qty: 5 } ] } ,
{ item: "postcard", instock: [ { warehouse: "B", qty: 15 }, { warehouse: "C", qty: 35 } ] }
]);

instock 필드에서 { warehouse : "A", qty : 5 } 값을 가지는  document를 조회.

db.inventory.find( { "instock": { warehouse: "A", qty: 5 } } )

그러면 아래의 식은 조회가 될까요?

db.inventory.find( { "instock": { qty: 5, warehouse: "A" } } )

 문서(객체)를 동등 비교하는 경우 순서 포함하여 모든 값이 일치해야합니다. -> 조회 안됨

 

 

instock 1번째 배열의 qty 값이 40 이상인 값을 조회.

db.inventory.find( { 'instock.0.qty': { $gte: 40 } } )


 instock 필드에서 qty가 5이고 warehouse 값이 A인 문서를 조회.  순서는 상관 없습니다.

db.inventory.find( { "instock": { $elemMatch: { qty: 5, warehouse: "A" } } } )


조회 결과에서 필드 표시 여부

 

find 명령어 두번째 파라미터에서 field 를 표시할지 여부를 지정.

기본적으로 파라미터가 없으면 모두 표시.

 

document 에  기본적으로 포함된 _id필드를 제외하고 나머지 필드의 경우 '표시할 필드만 입력' 하거나 '표시하지 않을 필드를 표시' 해야 함. 

 

아래의 식은 에러가 발생합니다.

db.inventory.find( { status: "A" }, { item: 1, status: 0 } )


아래의 식은 item 필드와, status 필드 그리고 _id필드가 표시

db.inventory.find( { status: "A" }, { item: 1, status: 1 } )

 

아래의 식은 item필드와 status필드를 제외한 필드가 표시.

db.inventory.find( { status: "A" }, { item: 0, status: 0 } )

 

 Embedded(내장) 된 문서의 필드 표시는 아래와 같습니다.

db.inventory.find( { status: "A" }, { item: 1, status: 1, "size.uom": 1 } )

 

Null 값과 필드 존재여부 조회

 

아래의 식은 item 필드가 null 인 값도 조회 하지만, item 필드가 존재하지 않는 문서도 조회가 됩니다.

db.inventory.find( { item: null } )

item 필드가 존재하지 않는 문서를 조회합니다.

db.inventory.find( { item : { $exists: false } } )

 

이름설명

$exists 특정 필드를 가지고 있으면 반환
$type 특정 타입의 필드가 있으면 반환

Cursor 반복문

find 명령어는 cursor형태의 객체로 반환이 됩니다.

변수를 할당하고 아래와 같이 javascript 반복문을 활용할 수 있습니다.

var myCursor = db.users.find( { type: 2 } ); while (myCursor.hasNext()) {     printjson(myCursor.next()); }

아래와 같이도 가능 합니다.

var myCursor = db.users.find( { type: 2 } ); myCursor.forEach(printjson);

cursor객체를 array(배열) 로 변환할 수 있습니다.

var myCursor = db.inventory.find( { type: 2 } ); var documentArray = myCursor.toArray(); var myDocument = documentArray[3];
출처: https://cionman.tistory.com/48 [Suwoni블로그]
반응형

+ Recent posts