Django에서 makemigrations와 migrate의 차이는 무엇일까?


Django 홈페이지

Django Migrations에 보면 간략하게 소개가 되어있다.


차이

장고 공식 홈페이지에 들어가면 아래처럼 간략하게 설명이 나와있다.

Django


  • makemigrations: models.py에서 적용한 변경사항이나 추가된 혹은 삭제된 사항들을 감지하여 파일로 생성
  • migrate: 적용되지 않은 migrations들을(설정값들을) 적용시키는 역할


즉, makimigrations는 장고에서 제공하는 모델의 변경사항들을 감지하고 기록하는 역할을 하며 migrate는 그러한 기록된 파일들과 설정값들을 읽어서 그 변경사항을 db에 저장하는 역할을 한다.

Django에서 테스트를 하기위해 RunPython을 이용해서 Database에 값을 넣어보자


소개

Django에서 fixture사용하기에서 fixture를 이용해 django에서 DB에 자료를 추가하는 방법을 보았는데 이번에는 RunPython을 이용해 보겠습니다.


방법

방법은 비교적 간단하며 아래와 같습니다.

  1. [app_name]/models.py에 모델을 정의합니다.
  2. [app_name]/migrations/에 해당 모델에 대한 py를 추가합니다(하단 참조)
  3. ./manage.py makemigrations./manage.py migrate 실행


모델 작성

필요한 모델을 작성합니다. 예시를 위해서 RunPythonExample이라는 모델을 만들었습니다.

[app_name]/.models.py

from django.db import models


[...]


class RunPythonExample(models.Model):
    test_name = models.TextField()
    test_text = models.TextField()


모델에 대한 .py를 migrations 폴더에 추가

모델을 추가하였으니 해달 모델에 대한 값들을 추가하는 코드를 아래처럼 작성해서 migrations의 하위에 추가합니다. 2개의 tuple을 추가해보겠습니다.

run_python_test.py

# -*- coding: utf-8 -*-

from __future__ import unicode_literals
from django.db import migrations


def forwards_func(apps, schema_editor):

    # (app_name, 해당 앱에서 model class명)
    RunPythonExample = apps.get_model("moim", "RunPythonExample")
    db_alias = schema_editor.connection.alias
    RunPythonExample.objects.using(db_alias).bulk_create([
        RunPythonExample(test_name = "test_name_one", test_text = "test_text_one"),
        RunPythonExample(test_name = "test_name_two", test_text = "test_text_two"),
    ])


def reverse_func(apps, schema_editor):

    RunPythonExample = apps.get_model("moim", "RunPythonExample")
    db_alias = schema_editor.connection.alias
    RunPythonExample.objects.using(db_alias).filter(test_name="test_name_one").delete()
    RunPythonExample.objects.using(db_alias).filter(test_name="test_name_two").delete()


class Migration(migrations.Migration):

    dependencies = [
    ]

    operations = [
        migrations.RunPython(forwards_func, reverse_func),
    ]


명령어 실행 및 확인

명령어를 실행해서 이제 확인을 해봅시다.

python ./manage.py makemigrations
python ./manage.py migrate
python ./manage.py runserver

Git에서 rebase를 통해 변경 내역은 남겨두고 rebase를 통해 commit을 합치는 방법을 알아보자


소개

Git에서 작업을 하다보면 오류를 고치거나 테스트를 하기 위해서 commit을 여러번하거나 불필요한 commit을 하게되는 경우가 있다. 이때 코드는 그대로 남겨두고(변경이력은 그대로) commit을 합치는 방법을 rebase를 통해서 알아보자


방법

특정 python 파일을 계속해서 한줄씩 추가해서 5번 commit을 하고 5번째 commit의 코드 내용은 그대로 유지하면서 commit은 4번째 commit으로 남기는 실습을 진행해 보겠습니다.

  1. rebase_test.py를 만든다
  2. print(“First Commit”)을 추가한다.
  3. “Fisrt Commit”이라는 메시지로 commit한다.
  4. 2~3을 “Fifth Commit”까지 반복한다.


예제 코드

위에 나온대로 하면 아래와 같이 나옵니다.

rebase_test.py

print("Fisrt Commit")
print("Second Commit")
print("Third Commit")
print("Fourth Commit")
print("Fifth Commit")


커밋 내역은 다음처럼 나오면 정상입니다. 추가적으로 나중에 remote에 push하는 과정까지 진행해볼 예정이기에 remote에 추가하였습니다.

Commits


이제 현재 코드를 그대로 “Fifth Commit”의 커밋 메시지를 삭제해보겠습니다. 자 터미널이나 쉘에서 다음과 같은 명령어를 칩니다.

git rebase -i HEAD~2
#HEAD를 포함한 이전 2개까지의 커밋을 보여줍니다.


그러면 아래와 같은 텍스트들이 출력이 됩니다.

Rebase Command Result


이제 커맨드들이 나왔으니 커맨드를 이용해줍시다. 우리는 squash를 통해서 현재의 커밋은 유지하고 이전 커밋으로 녹아드는(내역은 유지하고 메시지는 삭제) 작업을 진행하겠습니다.

아래 내용처럼 picksquash로 변경해주고 저장합니다.

Change to squash


그러면 아래와 같은 화면이 나오는데 여기서 삭제하시고 싶은 Commit을 삭제하시면 됩니다. 우리는 “Fifth Commit”을 삭제하겠습니다. 삭제해주시고 저장하시면 됩니다.

After Save


그러면 아래와 같이 코드는 그대로인데 Commit은 “Fifth Commit”은 사라진 것을 확인 할 수 있습니다.

Git Log Result
Code After Rebase


Remote 저장소에 push 하기

이제 remote에 push를 해보도록 하자

git push origin master


그런데 오류가 생긴다… Updates were rejected because the tip of your current branch is behind라고 현재 브랜치가 remote보다 뒤에 있다고 해서 그렇다고 한다..

그래서 이렇게 rebase를 통해 commit을 합친 경우에는 force push를 해줘야한다.

git push origin master -f


정리

  1. git rebase -i HEAD~2를 명령어 창에 입력
  2. 합치고자 하는 commit에서 pick -> squash로 변경 후 저장
  3. 지우고자 하는 commit 메시지를 지우고 저장
  4. force push로 remote 저장소에 저장


주의사항

force push로 remote에 업로드 하기 때문에 master 혹은 develop 브랜치는 피하는게 좋습니다.

Django 이전에 포스팅을 했던 fixture에 대해서 간단하게 포스팅해보도록 하겠습니다.


환경 및 선수조건


소개

Django에서 fixture를 이용해서 테스팅을 할 때 DB에 값들을 넣는 방법을 알아보겠습니다.

  1. Model을 만든다.
  2. Model을 dump 떠서 혹은 입력을 통해 json형태로 저장한다.
  3. 명령어를 통해서 테스팅 전에 불러온다.


Model을 만들기

기본적으로 Django에서는 [app_name]/models.py의 파일에 작성해주시면 됩니다. 작성해주시고 아래와 같은 migrate 명령어를 주셔야 적용이됩니다.

models.py

from django.db import models


class Meeting(models.Model):
    maker = models.TextField()
    name = models.TextField()
    place = models.TextField()
    start_time = models.DateTimeField()
    image_path = models.ImageField(blank=True)
    distance_near_univ = models.TextField()
    price_range = models.TextField()

$ python manage.py migrate


Model을 dump 떠서 혹은 입력을 통해 json형태로 저장한다.

2가지 방법(Model을 dump 혹은 직접 입력)으로 json을 만들 수 있습니다.


방법1) DB에 있는 model들을 dump 뜨기

우선 현재 db에 있는 data들을 json형식으로 dump를 뜨고 싶다면 아래의 명령어를 통해서 할 수 있다.

$ python manage.py dumpdata [app_name].[model.name] --indent [INDENT] > [fixture_name].json

실제 사용 코드는 아래와 같습니다.

$ python manage.py dumpdata moim.meeting --indent 2 > meeting-data.json


방법2) 직접 json 파일을 작성하기

위에 처럼 현재 db가 아니라 아래의 형태처럼 직접 db를 json형식으로 입력해도 됩니다. json형식에 대한 부분은 구글에 자료가 많으니 따로 작성하지 않겠습니다.

[
    {
    "model": "moim.meeting",
    "pk": 1,
    "fields": {
        "maker": "test maker",
        "name": "test name",
        "place": "test place",
        "start_time": "2017-02-19T08:09:29Z",
        "image_path": "",
        "distance_near_univ": "test univ",
        "price_range": "233000~335000"
        }
    }
]


만든 json파일을 [app_name]/fixtures 폴더로 이동

결론적으로 위의 형태를 만족하는 json형태의 파일을 생성하고 [app_name]폴더 아래 fixtures라고 폴더를 만들어주시고 넣어주시면 됩니다.


명령어를 통해 테스팅 전에 불러오기

이제 테스팅을 위해서 json파일들을 db에 넣어보겠습니다. 아래의 명령어를 통해서 실행합니다.

$ python manage.py migrate
$ python manage.py loaddata [app_name]/fixtures/[fixture_name].json
$ python manage.py runserver

제가 실제 사용한 명령어는 아래와 같습니다.

$ python manage.py migrate
$ python manage.py loaddata moim/fixtures/meeting-data.json
$ python manage.py runserver

Django admin을 통해서 자료가 들어가 있음을 확인 할 수 있습니다.

pytest 혹은 테스팅을 할 때 fixture란게 어떤 개념이고 그에 대한 간략한 예시를 알아보기로 한다.


환경 및 선수조건

  • Python
  • Pytest가 무엇인지
  • Django에 대한 기본 사용 및 이해
  • Selenium


소개

pytest에 대한 공부를 하면서 계속 fixture라는게 나오고 mocking 혹은 mock object라는 것이 계속 나오다가 이해를 하지 못하였는데 이 기회에 정리하면서 이해를 해보고자 정리합니다.

살펴 볼 fixture라는게 어떤 개념인지부터 시작해 현재 프로젝트에 적용해본 pytest fixture의 예시를 보도록 하겠습니다.


Fixture가 무엇인가요?

우선 fixture의 사전적 정의에 대해서 살펴보고 넘어가보도록 하겠습니다. 검색은 네이버에서의 결과입니다.

naver fixture meaning

사전적인 의미로는 경기 혹은 고정 세간이라도 나와있지만… 아직은 무슨 의미인지 감이 안옵니다…(그게 정상) 그러면 Wikipedia에 한번 영어로 검색을 해보겠습니다.

wiki fixture search

검색을 해보니 첫번째에 Test fixture라는 부분이 있습니다. 의미는 “used to control and automate testing”으로 테스트를 자동화 하거나 조절하기위해 사용된다고 합니다.

링크를 들어가 보시면 아래와 같이 소프트웨어 섹션이 있습니다.

wiki fixture software

간략하게 요약을 하면 fixture란 테스팅을 하는데 있어서 필요한 부분들을 혹은 조건들을 미리 준비해놓은 리소스 혹은 코드들이라고 보면됩니다. DB가 필요해서 어떤 내용들을 테스트 할 때만 DB에 넣어서 확인을 한다던가 특정 파일을 테스팅 할 때 필요하다면 특정 파일들이 그 fixture라고 볼 수 있습니다.


pytest에서 fixture 사용하기

이제 fixture에 대해서 개념을 알았으니 실제로 간단한 코드를 보도록 하겠습니다. pytest를 통해서 테스팅을 할 때 특정 browser를 만들고 그 브라우저를 계속 사용하기위한 코드입니다. 즉 여기서는 browser가 fixture가 되겠죠?


homepage_test.py

@pytest.fixture(scope="module")
def browser():
    # ...
    # 브라우저를 받아오는 부분
    # driver는 webDriver이다.
    # ...
    yield driver
    driver.close()

def test_first_page_card_title(browser):
    browser.get("http://localhost:8000")
    assert '규카츠 먹을래?' in browser.page_source

browser함수에서 driver를 만들고 그 driver를 test_first_page_card_title이라는 함수가 사용을 합니다.

위에 @pytest.fixture(scope="module")을 통해서 현재 py파일 안에서 계속 사용할 것임을 명시 하였기에 현재 py파일에서 실행이 되면 계속 driver를 사용 할 수 있게됩니다.

추가적으로 아래처럼 계속 browser를 사용해도 하나의 브라우저를 사용할 수 있으며 yield예약어가 있기 때문에 다 끝났을 때 browser의 driver.close()가 실행됩니다.


def test_1(browser):
    browser.get("http://localhost:8000")
    assert True

def test_2(browser):
    browser.get("http://localhost:8000")
    assert True

def test_f3(browser):
    browser.get("http://localhost:8000")
    assert True        

다시 마지막으로 정리하면 fixture란 테스팅에서 쓰이는 값이나 리소스에 대한 부분으로 미리 준비해두는 준비 도구 및 재료입니다. 그 값은 어떤 객체가 될수도 있고 환경도 될수도 있고 DB가 될수도 있습니다.

다음에는 fixture 혹은 RunPython을 이용해서 pytest에서 테스팅 하기위한 DB를 어떻게 설정하는지를 확인해보는 코드를 만들어보겠습니다.