# 공지사항
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 공식문서이다. 관련 기능은 옆에 설명이 되어있다.
일단 사용할 모델에 대해서 살펴보자. 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를 사용하기 때문에 추후에 바꿔서 수정할 계획이다.
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)을 사용한다.
그리고 새로운 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이름을 적어야 한다.
@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 }}은 기술적으로 같다. 두개 중 선택해서 사용하면 된다.
AutoField는 Django ORM에서 자동적으로 생성되는 Auto increment Primary key를 말한다. 기본값이 AutoField로 int 크기를 갖게 되는데 거의 매번 bigint 사이즈 사용을 위해 Model을 정의할 때 bigint로 정의 하기 모델마다 별도 선언을 해주었었다.
Wagtail에서 apps.py를 확인해보면 BigAutoField로 되어 있다. Django 3.2 버전 이상부터 만들어진 프로젝트들은 주로 BigAutoField를 사용한다. 그래서 기존에 AutoField로 되어 있는 Field을 BigAutoField로 바꿔서 문제를 피하는 것이 바람직할 것이다.
Wagtail에서 Snippets은 언제 사용할까? 보통 재사용하는 Models을 만들 때 사용한다고 한다. 주로 Django Model이 Snippet에 저장된다.
9. __str__(self):
Wagtail에서 Model을 작성할 때, __str__(self)을 정의해준다. 어떤 용도일까? 답은 orm을 사용해서 Model을 불러왔을 때, 표시되는 이름이다. 정의하지 않고 객체를 출력한다면 SampleModel(1), SampleModel(2), SampleModel(3) 이런 식으로 숫자로 구별되어서 전달이 될 것이다. 그래서 __str__(self)의 return 값을 모델의 필드로 한다면 필드 이름으로 보여질 것이다.
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')