Wagtail에서 Ajax를 하는 방법에 대해서 기술하려고 한다. 

form 태그와 route 기능을 통해서 서버와 간단한 통신을 할 수 있지만 데이터가 많아지면 한계가 생긴다. 

그래서 route와 Ajax를 이용하여 서버와 클라이언트 간에 데이터 송수신 방법을 소개하려고 한다. 

 

1. JS 작성 

card.js

const card_completion = () => {
    const data = {
        card : card_page_title,
        card_id : card_page_id,
    }

    const url = card_page_url + 'card_completion/';
    const option = {
        method: "POST",
        headers: {
            'X-CSRFToken' : card_page_token,
            "Content-Type": "application/json",
        },
        body: JSON.stringify(data),
    }
    fetch(url, option)
    .then(res => {
        if(res.status === 200){
            res.text()
            .then(text => {
                console.log(text);
            }) 
        }
        else {
            console.log('오류 발생');
        }
    });
}

학습을 완료하고 학습 완료 버튼을 누르면 card_completion 함수가 실행되도록 하려 한다. 

    const data = {
        card : card_page_title,
        card_id : card_page_id,
    }

data에는 서버로 보내고 싶은 데이터를 Json 형식으로 작성해준다. 

    const url = card_page_url + 'card_completion/';
    const option = {
        method: "POST",
        headers: {
            'X-CSRFToken' : card_page_token,
            "Content-Type": "application/json",
        },
        body: JSON.stringify(data),
    }

-url은 서버의 route 주소를 적어준다. Django에서는 꼭 마지막에 '/'을 붙여줘야한다. 

-method는 어떤 방식으로 통신할 것인지를 정해준다. 대표적으로 'GET', 'POST'등이 있다. 

-headers에는 기본 정보가 포함되어 있는데 csrf 토큰과, 전송할 data type을 명시해준다. 

-body에는 위에서 작성한 전송할 데이터를 json 형식 문자열로 바꿔서 작성해준다. 

 

    fetch(url, option)
    .then(res => {
        if(res.status === 200){
            res.text()
            .then(text => {
                console.log(text);
            }) 
        }
        else {
            console.log('오류 발생');
        }
    });

원래는 Jquery Ajax를 사용했는데 요즘은 fetch를 사용한다고 해서 fetch를 사용해보았다. 

res.status가 200이면 정상적으로 통신이 되었다는 의미이므로 통신이 정상적으로 이루어졌을 때에 할 작업들을 작성해주면 된다. 정산적으로 통신이 되었으면 res.text().then을 통해 서버에서 보내온 데이터를 수신한다. 

 

  • response.text() – 응답을 읽고 텍스트를 반환합니다,
  • response.json() – 응답을 JSON 형태로 파싱합니다,
  • response.formData() – 응답을 FormData 객체 형태로 반환합니다. FormData에 대한 자세한 내용은 다음 챕터에서 다루겠습니다.
  • response.blob() – 응답을 Blob(타입이 있는 바이너리 데이터) 형태로 반환합니다.
  • response.arrayBuffer() – 응답을 ArrayBuffer(바이너리 데이터를 로우 레벨 형식으로 표현한 것) 형태로 반환합니다.
  • 이 외에도 response.body가 있는데, ReadableStream 객체인 response.body를 사용하면 응답 본문을 청크 단위로 읽을 수 있습니다. 자세한 용례는 곧 살펴보겠습니다.

https://ko.javascript.info/fetch

 

2. models.py 작성

course/models.py

    @route(r'^card_completion/$', name='card_completion')
    def card_completion(self, request):
        jsonObject = json.loads(request.body)
        card_title = jsonObject.get('card')
        card_id = jsonObject.get('card_id')


        return HttpResponse(json.dumps({'status':'success'}), content_type="application/json")

서버에는 코드가 어떻게 작성되어 있는지를 확인해보자. 기본적인 wagtail의 route랑 같다. 다만 데이터가 json 형식으로 오기 때문에 json.loads를 통해 데이터를 파싱해준다. 데이터는 js에서 보낸 body에 담겨 있다. 

 

그리고 return은 json.dumps를 통해 인코딩을 하고 마찬가지로 content_type을 통해서 전달해준다. 

 

 

3. JQUERY AJAX

 

iamport 기능 구현

let csrftoken = '{{ csrf_token }}'
        IMP.init('imp');
        IMP.request_pay({
            pg: 'html5_inicis',
            pay_method: 'card',
            merchant_uid: 'merchant_' + new Date().getTime(),
            name: '강의 1',
            amount: 100,
            buyer_email: im_email,
            buyer_name: im_name,
            buyer_tel: im_phonenumber,
            buyer_addr: '서울특별시 강남구 삼성동',
            buyer_postcode: '123-456'
        }, function (rsp) {
            if (rsp.success) {
                jQuery.ajax({
                    url: "/api/payment/", 
                    headers:{'X-CSRFToken':csrftoken},
                    type: 'POST',
                    dataType: 'json',
                    contentType: "application/json; charset=utf-8",
                    data: JSON.stringify({
                        imp_uid: rsp.imp_uid,
                        price: 100
                    }),
                }).done(function (data) {
                    alert('성공했습니다.')
                }).fail(function (data) {
                    // 여기에 결제 취소 endpoint를 입력
                    alert(data.message);
                });
            } else {
                var msg = '결제에 실패하였습니다.';
                msg += '에러내용 : ' + rsp.error_msg;
                alert(msg);
            }
        });

 

추가)

HttpResponse, render와 redirect의 차이점에 대해서 알아보자.

 

1. HttpResponse

HttpResponse(data, content_type)

 HttpResponse 는 HttpRequest와 짝을 이루며, response를 반환하는 가장 기본적인 함수

Json이나 html을 반환할 수도 있음.

 

2. render

render(request, template_name, context=None, content_type=None, status=None, using=None)

템플릿과 같이 쓰는 함수이다. context로 정보를 넘겨주면 된다. request 와 template_name 은 필수적으로 필요!

 

3. redirect

redirect(to, permanent=False, *args, **kwargs)

to 에는 어느 URL 로 이동할지를 정하게 됨. 이 때 상대 URL, 절대 URL 모두 가능하며 urls.py 에 name 을 정의하고 이를 많이 사용. 단지 URL로 이동하는 것이기 때문에 render 처럼 context 값을 넘기지는 못함.

 

핵심 : render 는 템플릿을 불러오고, redirect 는 URL로 이동

 

https://valuefactory.tistory.com/605

'Back-End > Wagtail, Django' 카테고리의 다른 글

Wagtail Admin Page 한글 설정  (0) 2021.11.04
Wagtail Form  (0) 2021.11.02
Wagtail Custom Admin Menu  (0) 2021.10.28
Wagtail StreamField Queryset  (0) 2021.10.27
알아두면 좋을 것들  (0) 2021.10.22

https://learnwagtail.com/tutorials/how-register-django-model-wagtails-modeladmin/

 

How to Register a Django Model with Wagtails ModelAdmin

How to Register a Django Model with Wagtails ModelAdmin

learnwagtail.com

https://www.revsys.com/tidbits/how-add-django-models-wagtail-admin/

 

Admin Page 메뉴에 모델을 추가해보려고 한다. 

 

1. 모델 작성

user_management/models.py

# 공지사항
class Notice(models.Model):

    type = models.CharField(blank=False, null=True, verbose_name="공지사항 종류", max_length=20)
    title = models.CharField(blank=False, null=True, verbose_name="공지사항 제목", max_length=100)
    content = RichTextField(blank=False, verbose_name="공지사항 내용")

    def __str__(self):
        return self.title

    class Meta:
        verbose_name_plural = "공지사항"

공지사항 모델을 Admin Page 메뉴에 추가 시켜보자. 

 

2. admin.py 작성

user_management/admin.py

from wagtail.contrib.modeladmin.options import (
    ModelAdmin,
    modeladmin_register
)
from .models import Notice

# Register your models here.
class NoticeAdmin(ModelAdmin):

    model = Notice
    menu_label = "공지사항"
    menu_icon = "placeholder"
    menu_order = 290
    add_to_settings_menu = False
    exclude_from_explorer = False
    list_display = ("type", "title")
    search_fields = ("type", "title")

modeladmin_register(NoticeAdmin)

 

Wagtail Document

from wagtail.contrib.modeladmin.options import (
    ModelAdmin, modeladmin_register)
from .models import Book


class BookAdmin(ModelAdmin):
    model = Book
    menu_label = 'Book'  # ditch this to use verbose_name_plural from model
    menu_icon = 'pilcrow'  # change as required
    menu_order = 200  # will put in 3rd place (000 being 1st, 100 2nd)
    add_to_settings_menu = False  # or True to add your model to the Settings sub-menu
    exclude_from_explorer = False # or True to exclude pages of this type from Wagtail's explorer view
    list_display = ('title', 'author')
    list_filter = ('author',)
    search_fields = ('title', 'author')

# Now you just need to register your customised ModelAdmin class with Wagtail
modeladmin_register(BookAdmin)

위에 코드는 직접 작성한 ModelAdmin이고 아래는 Wagtail 공식문서이다. 관련 기능은 옆에 설명이 되어있다.

 

 

 

3. settings.py 등록

settings/base.py

INSTALLED_APPS = [
    'wagtail.contrib.modeladmin',
]

model admin을 등록시켜주면 완료이다. 

 

적용 모습

정상적으로 적용되었다. 

'Back-End > Wagtail, Django' 카테고리의 다른 글

Wagtail Form  (0) 2021.11.02
Wagtail Ajax  (0) 2021.10.29
Wagtail StreamField Queryset  (0) 2021.10.27
알아두면 좋을 것들  (0) 2021.10.22
Wagtail StyleGuide + 추가 모듈  (0) 2021.10.18

Wagtail StreamField는 기존 Django Field와는 다르게 JSON 형식으로 이루어져 있다. 

그래서 Django Field 처럼 query문으로 create, add 등을 쓸 수가 없다. StreamField는 Query set을 어떻게 사용해야하는지 알아보자.

 

참고 사이트

https://stackoverflow.com/questions/40612658/add-modify-blocks-value-in-wagtail-streamfield-from-shell

https://docs.wagtail.io/en/stable/topics/streamfield.html#streamfield

1. Models

user_management/models.py

@register_snippet
class UserPyCoin(index.Indexed, ClusterableModel):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    py_coin_count = models.IntegerField(default=0, verbose_name="파이코인 총갯수")
    py_coin = StreamField(UserPyCoinBlock(), verbose_name="파이코인", blank=True)

    panels = [
        FieldPanel('user'),
        FieldPanel('py_coin_count', widget=CountWidget()),
        StreamFieldPanel('py_coin'),
    ]

일단 사용할 모델에 대해서 살펴보자. user 별로 streamfield인 py_coin을 추가해주고 py_coin의 갯수를 py_coin_count에 추가해주려고 한다. 그렇다면 streamfield인 py_coin이 어떤 구조인지 살펴보자.

 

user_management/blocks.py

class PyCoinBlock(StructBlock):
    coin_explanation = CharBlock(required=False, label="코인 설명")
    coin = CoinBlock(required=False, label='코인 개수', default=0)
    date = DateBlock(required=True, label='획득 날짜')

coin_explanation은 어떻게 코인을 사용했고 얻었는지 설명을 적어준다.

coin은 얻은 코인의 갯수이다.

date는 coin을 얻은 날짜이다. 

 

2. Ajax 통신

model을 보면 알 수 있듯이 user에게 코인을 적립해주는 기능이다. user가 quiz를 맞추면 ajax통신을 통해 quiz 정보와 갯수를 저장해주려고 한다. 

 

js/card.js

jQuery.ajax({ 
    url: card_page_url + "answer_check/", // 클라이언트가 HTTP 요청을 보낼 서버의 URL 주소 
    headers:{'X-CSRFToken':card_page_token},
    data: { 
    title : card_page_title,
    py_coin : 1,
  }, // HTTP 요청과 함께 서버로 보낼 데이터 
    method: "POST", // HTTP 요청 메소드(GET, POST 등) 
    dataType: "json" // 서버에서 보내줄 데이터의 타입 
  }).done(function (data) {
    if (data.status=='success') {
    alert('성공입니다. 파이 코인이 적립되었습니다.');
  }
    else {
    alert("서버 오류입니다. 새로고침 후 다시 실행해주세요.");
  }
    }).fail(function () {
    alert("서버 오류입니다. 새로고침 후 다시 실행해주세요.");

  }).always(function () {

});

Jquery Ajax를 통해 작성하였다. 요즘은 fetch를 사용하기 때문에 추후에 바꿔서 수정할 계획이다. 

퀴즈를 맞추게 되면 Ajax의 POST를 통해서 서버와 통신하려고 한다. 

 

3. StreamField Query Set

card/models.py

    @route(r'^answer_check/$', name='answer_check')
    def answer_check(self, request):
        if request.method == 'POST' and request.is_ajax():
            page_title = request.POST.get('title')
            py_coin = request.POST.get('py_coin')

            add_pycoin = [
                ('py_coin',{
                'coin_explanation' : page_title,
                'date' : timezone.localdate(),
                'coin' : py_coin
                },)
            ]

            instance_user = UserPyCoin.objects.get(user=request.user)
            instance_user.py_coin_count += int(py_coin)
            instance_user.py_coin.append(add_pycoin[0])
            instance_user.save()

            return HttpResponse(json.dumps({'status':'success'}), content_type="application/json")

Wagtail이라서 route를 통해서 Ajax 통신을 구현하였다. 앞서 얘기 했던 것 처럼 StreamField는 JSON 형태의 데이터이다. 먼저 어떻게 데이터가 저장되어 있는지 데이터베이스를 통해서 확인해보자. 

 

데이터베이스 구조

StreamField는 JSON 형식인 것을 확인 할 수 있다. 

구조를 보자면

type, value, id 값으로 나누어져 있고 type에는 StreamField의 변수명 (py_coin), value는 각 변수의 값이 JSON 형식으로 저장되어 있고 id 값은 block의 고유 값이다. 

 

우리는 type과 value만 추가해주면 된다. (아이디 값을 wagtail에서 자동으로 추가)

 

Python에서 저장해줄 때는 append를 통해 추가해주면 된다. 그리고 JSON형식은 튜플과 딕셔너리를 통해서 작성해주면 된다. 

add_pycoin = [
  ('py_coin',{
  'coin_explanation' : page_title,
  'date' : timezone.localdate(),
  'coin' : py_coin
  },)
]

type을 정의해주기 위해 'py_coin'을 작성하였다. 그리고 실제 데이터에는 JSON을 형식에 맞게 딕셔너리로 작성하였다. 

그리고 add_pycoin은 리스트 형식이기 때문에 첫번 째 데이터를 append 한다는 의미로 [0]을 붙여주면 된다.

instance_user.py_coin.append(add_pycoin[0])

이제 데이터베이스에 가서 확인하면 정상적으로 저장되는 것을 확인할 수 있다. 

 

추가)

삭제

del instance_user.py_coin[-1]

삭제를 하고 싶다면 위와 같이 작성을 해주면 된다. JSON형식의 데이터로 List안에 저장되므로 파이썬 List와 같게 데이터를 제어하면 될 것이다. 

 

수정

 

instance_user.py_coin[0] = ('heading', "My story")

코드 수정은 직접 리스트 번호로 접근해서 수정해야한다. 

 

instance_user.py_coin[0]를 출력해보면 아래와 같이 나타난다. 

<dl>
    <dt>coin_explanation</dt>
    <dd>코인입니다.</dd>
    <dt>coin</dt>
    <dd>1</dd>
    <dt>date</dt>
    <dd>2021-10-26</dd>
</dl>

그렇다면 데이터를 접근하려면 어떻게 해야할까?

 

page.body[0].block_type and page.body[0].value

위와 같이 접근할 수 있다. 다만 데이터의 직접 접근은 불가능하다고 한다. (tuple값이라서 바꿀 수가 없음)

'Back-End > Wagtail, Django' 카테고리의 다른 글

Wagtail Ajax  (0) 2021.10.29
Wagtail Custom Admin Menu  (0) 2021.10.28
알아두면 좋을 것들  (0) 2021.10.22
Wagtail StyleGuide + 추가 모듈  (0) 2021.10.18
Wagtail Iamport 결제, 결제 페이지  (0) 2021.10.14

https://velog.io/@ifyouseeksoomi/10.DRF-Level-Three-Django-REST-Framework-1.-Signal

 

Django에서는 로그인, 로그아웃이나 모델 생성등 어떤 기능을 작동할 때, singal을 발생 시킨다. 

그래서 signal을 통해 기능을 추가할 수 있다. (회원가입 했을때 축하 메일을 보내는 것 처럼)

 

Django를 다룰 때 필수적인 기능인 signal에 대해서 정리해보려고 한다. 

 

이번에 할 기능은 회원 가입시 개발 중인 Page에서 자동으로 관련 모델을 생성해주는 기능을 적어보려고 한다. 

 

1. Signals.py

user_management/signals.py

from user.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
from user_management.models import UserCourseRelationship, UserPyCoin


@receiver(post_save, sender=User)
def create_user_account(sender, instance, created, **kwargs):
    if created:
        UserCourseRelationship.objects.create(user=instance)
        UserPyCoin.objects.create(user=instance)

회원 가입이 되었을 때, 자동으로 UserCourseRelationship 모델과 UserPyCoin 모델을 생성해주려고 한다. 

 

먼저 나는 User 모델이 user.models에 AbstractUser로 재정의 되어 있으니 user.models.py에서 가져왔다.

그리고 django signals을 가져오면 되는데 회원가입, 즉 새로운 User 모델이 생성이 되는 것이므로 post_save(모델이 생성될 때 사용하는 signal)을 사용한다. 

signal에 대한 문서는 

https://docs.djangoproject.com/en/3.2/ref/signals/#django.db.models.signals.post_save

윗 링크를 참고하자 (장고 공식 문서) 이외에도 다양한 signals이 존재한다.

 

@receiver를 통해 어떤 기능을 수행하는지 정의 해 준다. 

@receiver(post_save, sender=User)

위 코드의 뜻은 User 모델에 post_save signal을 보내겠다는 의미이다. 

함수명은 직관적이게 create_user_account로 해주었다.

 

그리고 새로운 User 모델이 생성되었을 때(회원 가입 시), 기본 모델을 생성해주는 것이기 때문에 created를 사용한다. (created를 사용하기 위해 post_save로 해주었다고 생각하면 된다. created가 없으면 User모델이 동작할 때 마다 이벤트가 발생된다. 즉 로그인만 해도 자동으로 기본 모델이 생성되어 버린다.)

 

def create_user_account(sender, instance, created, **kwargs):
    if created:
        UserCourseRelationship.objects.create(user=instance)
        UserPyCoin.objects.create(user=instance)

sender는 User 모델을 의미하고 instance에는 현재 만들어진 인스턴스(회원 가입이므로 방금 회원가입한 사람)가 저장된다. 정리하자면 sender는 모델 그 자체이고 instance는 방금 생성된 instance를 뜻한다.

 

 2. apps.py에 작성

user_management/apps.py

class AccountConfig(AppConfig):
    name = 'user_management'

    def ready(self):
        import user_management.signals

다음은 apps.py에 우리가 만든 signals.py를 등록해줘야한다. 여기서 name에는 signals.py가 있는 app이름을 적어야 한다. 

 

3. __init__.py 작성 (settings.py에 작성)

user_management/__init__.py

default_app_config = "user_management.apps.AccountConfig"

init 파일에 AccountConfig를 실행해주라는 코드를 작성한다. (하지만 Django에서는 installed_app에 적는 것을 권장하고 있다.)

 

settings.py

INSTALLED_APPS = [
	#'user_management', 대신에
    'user_management.apps.AccountConfig', # 로 적으면 된다.
]

위와 같이 바꾸어 주자.

 

이제 회원가입할 때 마다 자동으로 모델이 생성되어지는 것을 확인할 수 있다. signals를 사용하면 정말 다양한 기능을 구현할 수 있을 것으로 생각된다. 다음에는 회원가입을 했을 때, 자동으로 축하 메세지를 보내는 기능을 구현해보고 싶다.

 

추가)

user_management/apps.py

from django.apps import AppConfig


class UserManagementConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'user_management'

    def ready(self):
        import user_management.signals

단순하게 기존 apps.py에 def ready만 추가하고 signals을 받으면 된다.

 

settings.py

INSTALLED_APPS = [
	'user_management', # 그냥 기존과 같이 적으면 된다.
]

 

정리하자면 INSTALLED_APPS에 app이름을 등록을 해주면 일단 __init__.py을 실행한다. (기본적으로 아무 것도 정의되어 있지 않음) 그러면 자동으로 apps.py에 가서 AppConfig를 실행해주게 된다.

 

참고로 AutoField는 Django ORM에서 자동적으로 생성되는 Auto increment Primary key를 말한다.

출처: https://www.morenice.kr/263 [morenice's blog]

'Back-End > Django' 카테고리의 다른 글

Django Request, Requests  (0) 2021.12.16
Wagtail Django_extensions, ipython  (0) 2021.11.19
Django ORM  (0) 2021.10.21
Django iamport 적용하기  (0) 2021.10.06
Django Bulma 적용 방법  (0) 2021.08.24

dev_front와 dev_backend를 main에다 merge 하면서 전체적으로 코드를 검토하게 되었다. 그동안 기능 만들기에 집중하여 애매하게 넘어갔던 부분이나 잘 몰랐던 부분을 검토하면서 정리하려고 한다. 

 

1. @Property

    @property
    def cards_of_course(self):
        cards = [ n.card for n in self.course_card_relationship.all()]
        return cards

Page에서 특정 필드 값을 Template에 다양한 방법으로 표현하고 싶을 때, 사용하는 방법이다. get_context로도 보낼 수 있지만 @property가 모듈화 되어 있어서 사용하기에는 더 좋아보인다.

context와 property를 사용하는 가장 큰 차이점!context는 request를 사용하고 싶을 때, property는 request가 필요 없을 때! property 메소드 함수에 request를 쓰면 오류가 난다. 

(Template에서는 메소드 명으로 가져와야한다.)

 

https://www.youtube.com/watch?v=5OyFch8_4fk

https://dojang.io/mod/page/view.php?id=2476 

@property에 대한 설명은 위에 잘 나와있다. 이해한대로 정리를 하자면 class에서 값을 가져오는 getter와 값을 설정해주는 setter가 있는데 @property는 getter, @함수명.setter는 setter를 뜻한다. 클래스의 변수명은 직접 변수명으로 접근해도 되지만 getter와 setter를 통해서 접근하면 원하는 조건을 입력해줄 수 있다. 

 

예를들어 어떤 경우에는 나이가 10살보다 적은 사람에만 적용되는 기능을 만들고 싶을 때, getter, setter 메소드를 통해서 기능을 적용할 수가 있다는 뜻이다. setter에서 조건에 맞게 기능을 추가해주고 getter로 가져오면 된다. 

 

(@property를 안적어도 기능 수행에는 문제가 없는 것을 확인할 수 있다. 하지만 getter 메소드이고 나중에 setter 메소드를 사용할 수도 있으므로 @property를 적어주자.)

 

2. git 명령어

git branch --delete 브랜치 이름 : git branch 삭제

git push origin --delete 브랜치 이름 : 원격 저장소 git branch 삭제

git merge 브랜치 명 : git merge

git pull origin 브랜치 명 : 특정 branch pull

 

 

3. Migrations 오류

models을 만들고 테스트하는 과정에서 기존 모델의 데이터를 그대로 남긴채로 모델을 수정을 하고 새로 데이터를 입력하거나 수정하려고 하면 오류가 발생한다. 

 

 

해결방법은 app의 migrations 파일의 있는 파일들을 삭제해주고 계속 문제가 발생되면 원인을 찾아서 수정해주면 되지만 정 안되면 깔끔하게 데이터베이스를 날려버리는게 속편하다. 

 

 

3. Templates에서의 self, page차이 (Wagtail)

templates에서 {{ self.title }}와 {{ page.title }}은 기술적으로 같다. 두개 중 선택해서 사용하면 된다. 

 

 

4. Templates와 Static 파일 관리 (Wagtail)

특별히 정해진 형식은 없지만 보통 기본 프로젝트 폴더에서 통합적으로 관리한다. 

 

 

5. Settings에서 base.py dev.py production.py 차이점 (Wagtail)

Wagtail의 Settings에 보면 base.py, dev.py, production.py를 볼 수 있다. 이들의 차이점은 

 

1)base.py

This file is for global settings that will be used in both development and production. Aim to keep most of your configuration in this file.

 

2)dev.py

This file is for settings that will only be used by developers. For example: DEBUG = True

 

3)production.py

This file is for settings that will only run on a production server. For example: DEBUG = False

 

4)local.py

This file is used for settings local to a particular machine. This file should never be tracked by a version control system.

 

1) base.py 는 공통적으로 사용하는 것을 정의 해 놓는다.

2) dev.py에는 로컬 단계. 즉 개발할 때 사용하는 것을 정의 해 놓는다.

3) production.py는 배포 서버를 위해 사용하는 것을 정의한다.

4) local.py 특정 기기에서 사용할 것을 정의한다. 

 

 

6. __str__(self)의 용도 (Wagtail)

 
  def __str__(self):
        return self.full_name

의 설정은 다른 곳에서 model을 불러왔을 때, 보여지는 변수이다. 즉 위에 코드에서는 객체를 호출했을 때 full_name이 나타난다.

 

 

7. DEFAULT_AUTO_FIELD

https://docs.djangoproject.com/en/3.2/releases/3.2/#customizing-type-of-auto-created-primary-keys

 

Django 3.2 release notes | Django documentation | Django

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

docs.djangoproject.com

AutoField는 Django ORM에서 자동적으로 생성되는 Auto increment Primary key를 말한다. 기본값이 AutoField로 int 크기를 갖게 되는데 거의 매번 bigint 사이즈 사용을 위해 Model을 정의할 때 bigint로 정의 하기 모델마다 별도 선언을 해주었었다.

 

출처: https://www.morenice.kr/263 [morenice's blog]

 

Wagtail에서 apps.py를 확인해보면 BigAutoField로 되어 있다. Django 3.2 버전 이상부터 만들어진 프로젝트들은 주로 BigAutoField를 사용한다. 그래서 기존에 AutoField로 되어 있는 Field을 BigAutoField로 바꿔서 문제를 피하는 것이 바람직할 것이다. 

 

settings/base.py

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

 

 

8. Wagtail Snippets

Wagtail에서 Snippets은 언제 사용할까? 보통 재사용하는 Models을 만들 때 사용한다고 한다. 주로 Django Model이 Snippet에 저장된다. 

 

 

9. __str__(self):

Wagtail에서 Model을 작성할 때, __str__(self)을 정의해준다. 어떤 용도일까? 답은 orm을 사용해서 Model을 불러왔을 때, 표시되는 이름이다. 정의하지 않고 객체를 출력한다면 SampleModel(1), SampleModel(2), SampleModel(3) 이런 식으로 숫자로 구별되어서 전달이 될 것이다. 그래서 __str__(self)의 return 값을 모델의 필드로 한다면 필드 이름으로 보여질 것이다. 

def __str__(self):
	return self.name

 위와 같은 방법으로 사용하면 된다. 

 

 

10. 다른 Port로 서버를 실행하고 싶다면?

python manage.py runserver 포트번호

python manage.py runserver 8001

위와 같이 실행시키면 8001포트로 서버가 열린다. 

그렇게 되면 접속할 주소는 http://127.0.0.1:8001 이 된다. 

https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=dudwo567890&logNo=220924729927 

 

Django, SQL query와 ORM 정리

Django, SQL query와 ORM 정리models.pyclass Site(models.Model): name = models.CharField(ma...

blog.naver.com

 

ORM이란, 객채(Object)의 관계(Relational)를 연결(Mapper)해주는 것을 뜻한다. 객체 지향적인 방법을 사용하여 데이터베이스의 데이터를 쉽게 조작할 수 있게 해주는 것이다.

 

 

Django, SQL query와 ORM 정리

models.py

class Site(models.Model):

    name = models.CharField(max_length=255,)

 

class Category(models.Model):

    name = models.CharField(max_length=255,)

 

class Article(models.Model):

    subject = models.CharField(max_length=255,)

    url = models.URLField(unique=True,)

    date = models.DateTimeField()

    site = models.ForeignKey(Site)

    category = models.ManyToManyField(Category)

    hit = models.IntegerField(default=0)

 

SQL

CREATE TABLE `web_site` (

  `id` INT(11) NOT NULL AUTO_INCREMENT,

  `name` VARCHAR(255) NOT NULL,

  PRIMARY KEY (`id`)

) ENGINE=INNODB AUTO_INCREMENT=0

 

CREATE TABLE `web_category` (

  `id` INT(11) NOT NULL AUTO_INCREMENT,

  `name` VARCHAR(255) NOT NULL,

  PRIMARY KEY (`id`)

) ENGINE=INNODB AUTO_INCREMENT=0

 

CREATE TABLE `web_article` (

  `id` INT(11) NOT NULL AUTO_INCREMENT,

  `subject` VARCHAR(255) NOT NULL,

  `url` VARCHAR(200) NOT NULL,

  `date` DATETIME(6) NOT NULL,

  `site_id` INT(11) NOT NULL,

  `hit` INT(11) NOT NULL,

  PRIMARY KEY (`id`),

  UNIQUE KEY `url` (`url`),

  KEY `web_article_site_id_8dbc8100_fk_web_site_id` (`site_id`),

  CONSTRAINT `web_article_site_id_8dbc8100_fk_web_site_id` FOREIGN KEY (`site_id`) REFERENCES `web_site` (`id`)

) ENGINE=INNODB AUTO_INCREMENT=0

 

CREATE TABLE `web_article_category` (

  `id` INT(11) NOT NULL AUTO_INCREMENT,

  `article_id` INT(11) NOT NULL,

  `category_id` INT(11) NOT NULL,

  PRIMARY KEY (`id`),

  UNIQUE KEY `web_article_category_article_id_9ea0efa6_uniq` (`article_id`,`category_id`),

  KEY `web_article_category_category_id_d2d88408_fk_web_category_id` (`category_id`),

  CONSTRAINT `web_article_category_article_id_5b4eba1c_fk_web_article_id` FOREIGN KEY (`article_id`) REFERENCES `web_article` (`id`),

  CONSTRAINT `web_article_category_category_id_d2d88408_fk_web_category_id` FOREIGN KEY (`category_id`) REFERENCES `web_category` (`id`)

) ENGINE=INNODB AUTO_INCREMENT=0

 

SELECT

ALL

SQL

SELECT * FROM web_article;

ORM

Article.objects.all()

 

WHERE

SQL

SELECT * FROM web_article WHERE id=1;

ORM

Article.objects.filter(id=1)

 

LIMIT n

SQL

SELECT * FROM web_article LIMIT 10;

ORM

Article.objects.all()[:10]

 

LIMIT n,n

SQL

SELECT * FROM web_article LIMIT 5,5;

ORM

Article.objects.all()[5:10]

 

fetchone

SQL

SELECT * FROM web_article WHERE id=1;

ORM

Article.objects.get(id=1) #Return : Object

 

fetchall

SQL

SELECT * FROM web_article WHERE site_id=1;

ORM

Article.objects.filter(site_id=1) #Return : QuerySet

 

AND

SQL

SELECT * FROM web_article WHERE site_id=1 AND hit=0;

ORM

Article.objects.filter(site_id=1,hit=0)

 

OR

SQL

SELECT * FROM web_article WHERE site_id=1 OR hit=0;

ORM

from django.db.models import Q

Article.objects.filter(Q(site_id=1)|Q(hit=0))

 

LIKE '%s%'

SQL

SELECT * FROM web_article WHERE subject LIKE '%공무원%';

ORM

Article.objects.filter(subject__icontains='공무원')

 

LIKE 's%'

SQL

SELECT * FROM web_article WHERE SUBJECT LIKE '유승민%';

ORM

Article.objects.filter(subject__startswith='유승민')

 

LIKE '%s'

SQL

SELECT * FROM web_article WHERE SUBJECT LIKE '%의혹';

ORM

Article.objects.filter(subject__endswith='의혹')

 

>=

SQL

SELECT * FROM web_article WHERE hit >= 2;

ORM

Article.objects.filter(hit__gte=2)

 

<=

SQL

SELECT * FROM web_article WHERE hit <= 2;

ORM

Article.objects.filter(hit__lte=2)

 

>

SQL

SELECT * FROM web_article WHERE hit > 1;

ORM

Article.objects.filter(hit__gt=1)

 

<

SQL

SELECT * FROM web_article WHERE hit < 1;

ORM

Article.objects.filter(hit__lt=1)

 

LEFT JOIN(ManyToManyField)

SQL

SELECT b.id FROM web_article_category AS a LEFT JOIN web_article AS b ON a.article_id = b.id LEFT JOIN web_category AS c ON c.id=a.category_id WHERE c.name='정치';

ORM

Category.objects.get(name='정치').article_set.all()

 

INSERT

집어넣기

SQL

INSERT INTO web_site SET name='뉴스타파';

ORM

site = Site(name='뉴스타파')

site.save() #commit 

 

있으면 가져오고 없으면 집어 넣기

SQL

INSERT INTO web_site SET name='한겨레';

ORM

site = Site.objects.get_or_create(name='한겨레') #save 메서드 호출 없이도 바로 입력됨

 

ForeignKey, ManyToManyField

SQL

INSERT INTO web_site SET name='PPSS'; #id = 7

INSERT INTO web_article SET subject='뉴스제목', url='http://news.com/12345', date='2017-02-02 12:34:56', site_id=7; #id = 60

INSERT INTO web_category SET name='정치'; #id = 1

INSERT INTO web_category SET name='뉴스'; #id = 7

INSERT INTO web_article_category SET article_id=60, category_id=1; #category = 정치

INSERT INTO web_article_category SET article_id=60, category_id=7; #category = 뉴스

ORM

site, created = Site.objects.get_or_create(name='PPSS')

article = Article(subject='뉴스제목', url='http://news.com', date='2017-02-0 2 12:34:56', site=site)

article.save() #commit

cate1, created = Category.objects.get_or_create(name='정치')

cate2, created = Category.objects.get_or_create(name='뉴스')

article.category.add(cate1)  #relationship 추가

article.category.add(cate2)

article.category.remove(cate2) #relationship 취소

article.category.add(cate2)

 

DELETE

web_article 테이블의 기사를 하나 지우려고 할때, 

SQL에서는 relationshop이 있는 web_article_category의 해당 article_id를 삭제해 주어야 한다.

하지만, ORM에서는 article 객체의 delete 메서드를 이용하면 relationshop에 대한 부분까지 고려해가면서 코딩을 할필요가 없어진다.

SQL

DELETE FROM web_article_category WHERE article_id=2;

DELETE FROM web_article WHERE id=2;

ORM

Article.objects.get(id=2).delete()

 

UPDATE

SQL

UPDATE web_article SET subject = '제목변경' WHERE id=4;

ORM

article = Article.objects.get(id=4)

article.subject = '제목변경'

article.save() #commit

'Back-End > Django' 카테고리의 다른 글

Wagtail Django_extensions, ipython  (0) 2021.11.19
Django Signals  (0) 2021.10.22
Django iamport 적용하기  (0) 2021.10.06
Django Bulma 적용 방법  (0) 2021.08.24
Django template 태그 모음  (0) 2021.08.12

+ Recent posts