- ๋ค์ํ ์ฑ์ django ๋ด๋ถ์์ ๋ง๋ค๊ณ ์ฑ๋ง๋ค ์์ฒด์ ์ผ๋ก ๊ฐ์ง๊ณ ์๋ Front-endํ์ผ์ ๊ตฌ์ถํ๋ค.
- ๊ทธ๋ฆฌ๊ณ ํด๋น ์ฑ์ ์ฎ์ด์ ์น ์๋น์ค๋ฅผ ๊ตฌ์ถํ๋ค.
- Django๊ฐ ๋ชจ์ฌ์ Docker์ ์ปจํ ์ด๋๊ฐ ๋๋ค.
- Django ์ปจํ ์ด๋๋ Django์๋น์ค์ ์ผ, MariaDB๋ DB๊ด๋ จ ์ผ, Nginx๋ ์๋ฒ ๊ด๋ จ ์ผ์ ๋๋ ์ ์งํํ๋ค. (MVC?)
- ์ด๋ค์ด ๋ชจ์ฌ์ Docker ์์คํ ์ ๊ตฌ์ถํ๋ค.
- Docker๋ฅผ ๊ตฌ์ถํ๋ฉด Vultr๋ก ์๋ฒ์ ๋์ปค๋ฅผ ์ฌ๋ฆฐ๋ค.(AWS๋ ์ด์ฉ ๊ฐ๋ฅ)
- Model
- ๋ชจ๋ธ์ ์ฅ๊ณ ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ํต์ ํ๊ฒ ๋์์ฃผ๋ ๋๊ตฌ์ด๋ค.
- ๊ฐ์ฒด๋ฅผ ๋ง๋๋๋ฐ ์ ์ ๊ฐ ์๋ก ๊ฐ์ ํ๋ฉด ๊ฐ์ฒด๊ฐ ์๊น, ์ ์ ๊ฐ ๊ฒ์๊ธ์ ์ฐ๋ ๊ฒ๋ ๊ฐ์ฒด.
- ๋ฐ์ดํฐ๋ฒ ์ด์ค์๋ Row, columns๊ฐ ์๋ค.
- Article์ด๋ผ๋ ๊ฒ์๊ธ์ด ์์ผ๋ฉด ๊ทธ ์์ Title,article,image๊ฐ ์๋๋ฐ ์ด๊ฐ DB์์๋ Columns๋ก ๋งค์นญ๋๋ค.
- ๋ชจ๋ธ์ ์ค์ ํ๋ฉด CRUD๋ ์ฅ๊ณ ๊ฐ ๋ค ํด์ค๋ค.
- View
- ์ฅ๊ณ ์ ๊ณ์ฐ์ ๋ด๋นํ๋ ๋ถ๋ถ
- User๊ฐ Server์ Request๋ฅผ ๋ณด๋ด๋ฉด Server๋ ์๋ต์ ์ํ ์ ์ฐจ๋ฅผ ๊ฑฐ์น๋ค. ๊ทธ๋ฆฌ๊ณ ์๋ต์ ๋ณด๋ธ๋ค.
- ex) ์ ์ ๋ก๊ทธ์ธ ,์ ์ ๊ฐ ๋ณด๋ธ ์์ฒญ์ด ์ ํจํ์ง, DB์์ ๊ฐ์ ธ์ค๋ ๊ณผ์ , ์ ์ ์๊ฒ ๋๋๋ ค์ฃผ๋ ๊ณผ์
- Template
- ์ค์ง์ ์ผ๋ก ๋ณด์ด๋ Front-end์์
- ์ ์ ๋ ์น์ฌ์ดํธ์ visual์ ๋ณด๋๋ฐ ๊ทธ ๋ด๋ถ๋ฅผ ์ด๋ป๊ฒ ๊ตฌํํ๊ณ ์์ฑํ ๊ฒ์ธ์ง ๋ณด์ฌ์ฃผ๋ ๋ฐฉ๋ฒ
- ex) ์ ์ ๊ฐ ๊ฒ์๊ธ์ ๋ณด๊ณ ์ถ์ ๋, HTML์ ๋์ ์ผ๋ก ๋ฐํ
-
Version Control System
-
๊ฐ๋ฐ์ ๊ณ์ํด์ ์งํํ ๋, ๋ฒ์ ์ ์ ๋ฐ์ดํธํ ํ ๋ฐ ๋ฒ์ ์ฐจ์ด์์ ๋ฌธ์ ๋ ํ์๋ฅผ ํด๊ฒฐํด์ฃผ๋ ์ญํ ์ ํ ์ ์๋ค.
-
git์ ํ์ !
- ์๋ฅผ ๋ค์ด 1.1๋ถํฐ 1.3๊น์ง ๊ฐ๋ฐ์ ํ๊ฒ ๋๋๋ฐ 1.3์์ ์๋ฌ๊ฐ ์๊ฒผ์ ๋, ๋กค๋ฐฑ์ ํ ์ ์๋ค. 1.2๋ก
-
Branch
- Main branch๋ก ๋ฐฐํฌ๋ฅผ ํ ๋, 1.1,1.2๋ก ์ฌ๋ผ๊ฐ ๋ ์ถ๊ฐ์ ์ธ ๊ธฐ๋ฅ์ ๊ตฌํํ๊ณ ์ถ์ ์ ์๋ค. ๊ธฐ์กด์ ์ํฅ์ ์ฃผ์ง ์์ผ๋ฉด์
- ๊ทธ๋ด ๋ ๋ธ๋์น๋ฅผ ์๋ก ํ์ ์๋ก์ด ๊ธฐ๋ฅ์ ๋ฃ๊ณ ๋ฐฐํฌ๋ฒ์ ์ Merge๋ก ํฉ์น ์ ์๋ค.
- ํ์ํฌ๋ฅผ ํ ๋ ์ปค๋ฐ์ ํตํด ๋๊ฐ ์ฌ๋ ธ๋์ง ํ์ธํ ์ ์๊ณ , ์จ๋ผ์ธ ๊น ์ ์ฅ์๋ฅผ ํตํด ์คํ์์ค๋ฅผ ์ฌ๋ ค์ ๋ชจ๋๊ฐ ๋ณผ ์ ์๊ฒํ๊ณ ์ด์๋ฅผ ๊ฐ์ ํ ์ ์๊ฒํ๋ค.
-
Command
- add
- commit
- push
- pull
- branch
- checkout
-
environ
-
Django๋ฅผ git์ ๋ฐฐํฌํ ๋ Secret key๊ฐ ์๋๋ฐ ์ด๋ฅผ ๋ ธ์ถ์ํค์ง ์๊ฒ ํ๋ ์ญํ
-
๋ฉ์ธ ํ๋ก์ ํธ ํด๋ ๋ด๋ถ์ ํด๋น ์ฝ๋ ๋ณต์ฌ
- ์ฃผ์์ฌํญ : SECRET_KEY = '' ์ด๋ฐ์์ผ๋ก ๋์ด์ฐ๊ธฐ ๊ธ์ง
DEBUG=on SECRET_KEY=๋์ ์ํฌ๋ฆฟ ํค ๋ฃ์ด์ฃผ๊ธฐ DATABASE_URL=psql://urser:un-githubbedpassword@127.0.0.1:8458/database SQLITE_URL=sqlite:///my-local-sqlite.db CACHE_URL=memcache://127.0.0.1:11211,127.0.0.1:11212,127.0.0.1:11213 REDIS_URL=rediscache://127.0.0.1:6379/1? client_class=django_redis.client.DefaultClient&password=ungithubbed-secret
-
settings.py์ ํด๋น ์ฝ๋ ๋ณต์ฌ (BASE_DIR์ ์ง์ฐ๊ณ ๋ง๋ค๊ฑฐ๋, BASE_DIR ์ ์๋์ ์ฝ๋ ์ถ๊ฐํด์ค๋ ๋จ )
import os,environ env = environ.Env( # set casting, default value DEBUG=(bool, False) ) # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent # reading .env file environ.Env.read_env()
-
settings.py์์ SECRET_KEY ๋ณ๊ฒฝ
SECRET_KEY = env('SECRET_KEY')
-
git ignore์ env ํ์ผ ์ถ๊ฐ
.env
-
-
๊นํ๋ธ ํ
- ๋ก์ปฌ์์ ์๊ฐ ๋๋๋ฆฌ๊ธฐ
git log --oneline #๊น์์ ๋ก๊ทธ ํ์ธ git reset -- hard ~num #์ํ๋ ๋งํผ ์ปค๋ฐ์ ์์จ ์ ์๋ค.
- ์ฅ๊ณ ์์๋ extends์ include๋ฅผ ๋ง์ด ์ด๋ค. ์ฉ๋๊ฐ ์ด์ง ๋ค๋ฅด๋ค
- extends : ๋ฏธ๋ฆฌ ๋ง๋ค์ด๋์ HTML ํ ํ๋ฆฟ์ ๊ฐ์ ธ์์ ๋ธ๋ก์ ์ด์ฉํด ๋ด์ฉ์ ์ฑ์๋๊ฐ๋ค. (๋ฐํ์ ๊น์์ค๋ค. )
- include : ์กฐ๊ฐ์ ๊ฐ์ ธ์์ HTML์ ์ถ๊ฐํ๋ค. (๋๋ฏธ๋ฅผ ๊ฐ์ ธ์์ ๋ถ์ฌ์ค๋ค. )
- ์ฆ, extends๋ก ๋ฐํ์ ๋ง๋ค๊ณ include๋ก ๋ด์ฉ์ ์ฑ์์ค๋ค!
- HTML ๋ผ๋ ์์ฒด๋ base๋ก ๋ง๋ค๊ณ head๋ ์ฑ๋ง๋ค ๋ค๋ฅผ ์ ์๊ธฐ ๋๋ฌธ์ ๋ฐ๋ก ํ ํ๋ฆฟ์ ๋ง๋ค์ด์ include๋ก ์ธ ์ ์๋ค.
- header์ footer๋ ์ฌ์ฌ์ฉํ๊ณ , body๋ถ๋ถ๋ง ์์ ํ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ include๋ก ๋นผ์ค๋ค.
- border-radius๋ ๋ธ๋ก์ ๊ฐ์ฅ์๋ฆฌ์ ๊ณต์ ๋ฅผ ์ค์ ๋ถ๋๋ฝ๊ฒ ๋ง๋๋ ๊ฒ
- base.html์ ์ด์ ๋ฒ ์ด์ค๋ก ์ฐ๊ณ ์ฑ ๋ด๋ถ์ templates๋ฅผ ๋ง๋ ๋ค.
- templates ๋ด๋ถ์ ์ฑ ์ด๋ฆ๊ณผ ๊ฐ์ ํด๋๋ฅผ ๋ค์ ๋ง๋ค๊ณ htmlํ์ผ์ ๊ทธ ์์ ๋ฃ๋๋ฐ ๊ทธ ์ด์ ๋ ์ฑ ๋ด๋ถ์ ํ ํ๋ฆฟ์์ html์ ๊ฐ์ ธ์ฌ ๋, ์ด๋ค ์ฑ์์ html์ ๊ฐ์ ธ์๋์ง ๊ฐ๋ ์ฑ ์๊ฒ ํ์ธํ ์ ์๋ค.
- ๋ง์ง์ ๊ฐ์ ๋๊ฐ ๋ฃ์ด์ฃผ๋ฉด ์์๋ ์ํ, ๋ค์๋ ์ข์ฐ
margin : 2rem 0;
-
๊ตฌ๊ธ ํฐํธ ์ ์ฉํ๋ ๋ฒ
- https://fonts.google.com/?subset=korean ์ ์
- ์๋จ sentence ๋น์นธ์ ์์์ ๊ธ ์ ๋ ฅ
- ๋ง์๋๋ ํฐํธ ํด๋ฆญ
- ๋งํฌ ๋ณต์ฌํด์ head์ ๋ฃ์ด์ฃผ๊ณ (๋ถํธ์คํธ๋ฉ ์ฒ๋ผ)
- ์ฌ์ฉํ ๋๋ ์๋ CSSrules to specify families ์ฐธ๊ณ ํด์ sytle๋ก ์ ์ฉ์ํค๊ธฐ
-
STATIC
-
๋ฒ ์ด์ค ๋๋ ํ ๋ฆฌ๋ settings.py ๊ฒฝ๋ก์์ rootํด๋๋ก ๊ฐ์ ๊ทธ ํด๋๋ฅผ BASE_DIR๋ก ํ๊ฒ ๋ค.
-
์ฆ, BASE_DIR ํ์์ staticfiles์ ์ถํ ์คํํฑ ํ์ผ๋ค์ ๋ชจ์ผ๊ฒ ๋ค.
-
STATIC_URL
STATIC_URL = '/static/'
-
STATIC_ROOT์ ์๋ ์ ์ ํ์ผ์ ์ฐธ์กฐ ํ ๋ ์ฌ์ฉํ URL
-
๊ฐ๋ฐ ๋จ๊ณ์์๋ ์ค์ ์ ์ ํ์ผ๋ค์ด ์ ์ฅ๋์ด ์๋ app/static/ ๊ฒฝ๋ก (๊ธฐ๋ณธ ๊ฒฝ๋ก) ๋ฐSTATICFILES_DIRS์ ์ ์๋ ์ถ๊ฐ ๊ฒฝ๋ก๋ค์ ํ์ํจ
-
์ค์ ํ์ผ์ด๋ ๋๋ ํ ๋ฆฌ๊ฐ ์๋๋ฉฐ, URL๋ก๋ง ์กด์ฌ ๋น์ด ์์ง ์์ ๊ฐ์ผ๋ก ์ค์ ํ๋ค๋ฉด ๋ฐ๋์ slash(/)๋ก ๋๋์ผ ํจ
-
STATIC_ROOT
-
collectstatic์ด ๋ฐฐํฌ๋ฅผ ์ํด ์ ์ ํ์ผ์ ์์งํ๋ ๋๋ ํ ๋ฆฌ์ ์ ๋ ๊ฒฝ๋ก
-
django ํ๋ก์ ํธ์์ ์ฌ์ฉํ๋ ๋ชจ๋ ์ ์ ํ์ผ์ ํ ๊ณณ์ ๋ชจ์ ๋ฃ๋ ๊ฒฝ๋ก
-
๊ฐ๋ฐ ๊ณผ์ ์์ setting.py์ DEBUG ๊ฐ์ด True๋ก ์ค์ ๋์ด ์์ผ๋ฉด ํด๋น ๊ฐ์ ์์ฉ๋์ง ์์
-
์ง์ ์์ฑํ์ง ์์ผ๋ฉด django ํ๋ก์ ํธ์์๋ setting.py์ ์์ฑ๋์ด ์์ง ์์
-
์ค ์๋น์ค ํ๊ฒฝ(๋ฐฐํฌ ํ๊ฒฝ)์์ django์ ๋ชจ๋ ์ ์ ํ์ผ์ ๋ค๋ฅธ ์น ์๋ฒ๊ฐ ์ง์ ์ ๊ณตํ๊ธฐ ์ํจ
[์ฐธ๊ณ ] collectstatic
- ํ๋ก์ ํธ ๋ฐฐํฌ ์ ํฉ์ด์ ธ์๋ ์ ์ ํ์ผ๋ค์ ๋ชจ์ ํน์ ๋๋ ํ ๋ฆฌ๋ก ์ฎ๊ธฐ๋ ์์
# settings.py ์์ STATIC_ROOT = BASE_DIR / 'staticfiles' $ python manage.py collectstatic
-
CSS
- STATIC ํด๋ ์์ CSSํ์ผ์ ๋ง๋ค๊ณ , head์์ ๋งํฌ๋ฅผ ๋ฐ๊ฒํ๋ค.
- ๊ณตํต๋ ์์๋ค์ ๊ฐ์์ฑ ์๊ฒ ํด๋์ค๋ก ๋ง๋ค๊ณ style์ cssํ์ผ์์ ์ค๋ค.
-
Cascading Style Sheet
-
CSS ์ฐ์ ์์๋ ์ธ๋ผ์ธ --> ๋ด์ฅ -> ๋งํฌ ์์
-
DISPLAY
-
Block
- HTML ํ๊ทธ ๋ถ๋ชจ ์ต๋ ๋๋น๋ฅผ ๋ค ๊ฐ์ ธ๊ฐ ๋์ด๋ ๋ฐ๋ก ์ค์นํ์ง ์๋ ์ด์ ๋ด์ฉ์ ๋ง์ถฐ์ ์์ญ์ ๊ฐ์ง
- div๋ ๊ธฐ๋ณธ์ ์ผ๋ก display:block; ์ ๊ฐ์ง๊ณ ์๋ค.
-
Inline
- ๊ธ์จ์ ๋์ด์ ์์ญ๋งํผ๋ง ์์ญ์ ๊ฐ์ ธ๊ฐ๋ค ์ถ๊ฐํ๋ฉด ์์ญ๋ผ๋ฆฌ ์ค๋ฅธ์ชฝ์ผ๋ก ์์ธ๋ค.
- span๊ฐ์ ๊ฒฝ์ฐ์๋ ๊ธฐ๋ณธ์ ์ผ๋ก inline์ด๋ค. ํ์ง๋ง display block์ ์ฃผ๋ฉด ๋ธ๋ฝ์ผ๋ก ๋ณ๊ฒฝ๋๋ค.
-
Inline-block
- ๋ธ๋ญ์ด์ง๋ง Inline์ฒ๋ผ ์์ชฝ์ผ๋ก ์์ธ๋ค.
-
None
- ์์๊ฐ ์กด์ฌํ์ง ์๋ ๊ฒ
-
Hidden
- ์์๋ฅผ ์จ๊ธฐ๋ ๊ฒ, ์กด์ฌํ์ง๋ง ๋ณด์ด์ง ์๋๋ค.
-
-
SIZE
- px
- ๋ถ๋ชจ๊ฐ ์ด๋ป๊ฒ ๋๋ ์๊ด์์ด ํฌ๊ธฐ๊ฐ ๊ณ ์ ๋๋ค.
- em
- ๋ถ๋ชจ๊ฐ ์ปค์ง๋๋งํผ ์ปค์ง๋ค. (๋ถ๋ชจ๊ฐ 1.5๋ฐฐ ์ปค์ง๋ฉด 1.5๋ฐฐ ์ปค์ง )
- ๋ถ๋ชจ๊ฐ 2๊ฐ ์์ ๋ ์ฒซ๋ฒ์งธ ๋ถ๋ชจ๊ฐ 2๋ฐฐ๊ฐ ๋๋ฉด 2๋ฒ์งธ ๋ถ๋ชจ๋ 2๋ฐฐ ์์์ 4๋ฐฐ๊ฐ ๋๋ค.
- ๋ฐ๋ผ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์๋ค.
- rem
- rem์ ๋ฃจํธ HTML์ ํฌ๊ธฐ ๋ณํ์ ๋ํ ์ํฅ๋ง ๋ฐ๋๋ค.
- ์ฆ ๋ฐ์ํ ์น์ ์ ํฉํ๋ค.
- 1rem์ 16px
- %
- ๋ฐ๋ก ์ ๋ถ๋ชจ ์ํฅ๋ง ๋ฐ๋๋ค
- px
-
Model
- ์ฅ๊ณ ์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฌ์ฉ์ด ํธํ๋๋ก ์ฐ๋์ ํด์ฃผ๋ ์ญํ
- ๊ฐ๋ฐ์ ์์ด DB์ ๋ํด์ ํฌ๊ฒ ์ ๊ฒฝ์ฐ์ง ์๋๋ก ๋์์ค
- makemigration
- models์์ ๋ง๋ DB๋ฅผ ํ์ด์ฌ ํ์ผ๊ณผ ์ฐ๋
-
HTTP Protocol
- user์ server๊ฐ ์์ฒญ์ ๋ณด๋ด๊ณ ์๋ต์ ํ๋ฉด์ ๋์์ ํ๋๋ฐ, ์ฌ๊ธฐ์ GET๊ณผ POST๋ฅผ ์ฌ์ฉํด์ ์๋ฒ๋ ์ ์ ๊ฐ ์ํ๋ ์ ๋ณด๋ฅผ ๋ณด๋ธ๋ค.
- GET
- ๋ณดํต ์กฐํ์ ๊ธฐ๋ฅ
- ์กฐํ๋ฅผ ํ ๋์๋ ์๋กญ๊ฒ ๋ง๋๋ ๊ฒ๋ณด๋ค ๋ ์ ์ ์ฝ์คํธ๋ฅผ ์ฌ์ฉํ๋ค.
- ์ฃผ์ ์์ ์ถ๊ฐ์ ์ธ ํ๋ผ๋ฏธํฐ๋ฅผ ๋ฃ์ด์ ๋ณด๋ด์ค๋ค.
- ?๋ ํ๋ผ๋ฏธํฐ๊ฐ ์์ํ๋ค๋ ํ์
- POST
- ์์ ์ด๋ ์๋ก ๋ง๋ค ๋ ์ฌ์ฉ
- ๊ฐ์ ์ฃผ์๋ฅผ ๋ณด๋ด๋๋ผ๋ ์ถ๊ฐ์ ์ผ๋ก ๋ฐ๋๋ผ๋ ์๋ต ๋ด๋ถ ๋ชธํต์ ๋ฐ์ดํฐ๋ฅผ ๋ฃ์ด์ ๋ณด๋ธ๋ค.
-
Form
- ์๋ฒ์ ๋ณด๋ด๋ ์์ฒญ ๋ช ์ธ์
- ๊ธ์ด๋ ํ์ผ ๋ฑ์ ํฌ์คํธ ๋ฐ๋์์ ์ฒจ๋ถํ๋๋ฐ ๊ทธ ๋ฐ์ดํฐ๋ค์ด Form ๋ฐ๋ ์์ ๋ค์ด๊ฐ๋ค.
- action์ ์์ฒญ์ ํ๋ ค๋ url
- CSRF_TOKEN
- ์ฅ๊ณ ์์ POST๋ฅผ ๋ณด๋ด๋ ค๋ฉด CSRF_TOKEN์ ๋ช ์ํด์ผํ๋ค.
- CSRF_TOKEN์ ๋ณด์ ๊ธฐ๋ฅ์ ํ๋ค.
-
DB SAVE
-
Send POST DATA
- POST๋ก ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ผ ๋ ๋ฐ์ดํฐ๋ฅผ ๊ตฌ๋ถํ๊ธฐ ์ํด name์ ์ค์ ํ ์ ์๋ค.
- ์ด name์ ๊ธฐ๋ฐ์ผ๋ก views.py์์ request.POST.get(name)์ผ๋ก ํด๋น ๋ฐ์ดํฐ๋ฅผ ์ธ์ํ๊ณ ์ฌ์ฉํ ์ ์๋ค.
## hello_world.html <input type="text" name="hello_world_input"> ## views.py temp = request.POST.get('hello_world_input') # DB๋ฅผ ๊ตฌ์ฑํ ๋ชจ๋ธ์ ๊ฐ์ ธ์จ๋ค. new_hello_world = HelloWorld() #POST๋ก ๋ฐ์์จ ๊ฐ์ DB์ ์ถ๊ฐํด์ค๋ค. #new_hello_world ์ธ์คํด์ค์ text์ temp๋ฅผ ์ ์ฅ new_hello_world.text = temp new_hello_world.save() return render(request,'accountapp/hello_world.html',context={'hello_world_output': new_hello_world})
-
Model์ ์๋ ๋ฐ์ดํฐ ์ถ๋ ฅ
## Html {% if hello_world_list %} {% comment %} hello_world_output์ ํ ์คํธ๋ฅผ ์ถ๋ ฅ {% endcomment %} <h1>{{hello_world_list}}</h1> {% endif %} #views.py # DB์ ๋ชจ๋ ๊ฐ์ฒด hello_world_list = HelloWorld.objects.all() return render(request,'accountapp/hello_world.html',context={'hello_world_list': hello_world_list}) # POST ์์ฒญ์ ๋ฐ์ผ๋ฉด POST MEHTOD ํ์ else : return render(request,'accountapp/hello_world.html',context={'hello_world_list': hello_world_list})
-
ํ์ด์ง๋ฅผ ์๋ก๊ณ ์นจ ํ ๋ ์๋ฌด๊ฒ๋ ๋๋ฅด์ง ์์๋๋ฐ๋, ๋ง์ง๋ง์ ์์ฑํ ๊ฐ์ฒด๊ฐ ๊ณ์ ๋ฐ๋ณต์ ์ผ๋ก ์์ฑ๋จ
- why? views.py์์ POST๊ฐ ์์ ๋ ๋ฐ๋ณต์ ์ผ๋ก ์ ์ฅ์ ํ๊ธฐ ๋๋ฌธ์
- ํด๊ฒฐ์ ์ํด render๋ฅผ ํ์ง ์๊ณ ๊ทธ๋ฅ ํ์ด์ง๋ง ๋ฐํํ๊ฒ redirect๋ฅผ ์ฌ์ฉํด์ผํ๋ค.
-
POST๋ก ๋ฐ์ดํฐ๋ฅผ ์ ์ฅ๋ง ์ํค๊ณ , POST๋ฅผ ๋ณด๋ด์ง ์์ ๋๋ ๊ทธ๋ฅ DB์ ์์๋ง ๋์ดํ๊ฒ ํ๋ค...?
-
render ์ redirect ๊ตฌ๋ถ
๋ ํจ์๋ฅผ ํท๊ฐ๋ ค ํผ๋ํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ต๋๋ค. ์๊ฐ ์ธ๋ก ๋์ ์ฐจ์ด๋ ๋ช ํํฉ๋๋ค.
render
๋ ํ ํ๋ฆฟ์ ๋ถ๋ฌ์ค๊ณ ,redirect
๋ URL๋ก ์ด๋ํฉ๋๋ค. URL ๋ก ์ด๋ํ๋ค๋ ๊ฑด ๊ทธ URL ์ ๋ง๋ views ๊ฐ ๋ค์ ์คํ๋ ํ ๊ณ ์ฌ๊ธฐ์ render ๋ฅผ ํ ์ง ๋ค์ redirect ํ ์ง ๊ฒฐ์ ํ ๊ฒ ์ ๋๋ค. ์ด ์ ์ ์ ์ํด์ ์ฌ์ฉํ์ ๋ค๋ฉด ์ํฉ์ ๋ง๊ฒ ์ฌ์ฉํ์ค ์ ์์ ๊ฒ๋๋ค.
-
-
Form(INPUT) -> Function(DB์ ์ํธ์์ฉ) -> HTML(OUTPUT)
-
Form์์ ์์ฒญ์ ๋ณด๋ผ ๋ ํ์ฌ๊น์ง๋ ์ ์ฝ์ด ์์ด ์๋ฌด๋ ๋ณด๋ผ ์ ์์๋ค.
- ๋ง์ฝ ์ฐ๋ฆฌ๊ฐ ๊ฐ์ ์๋ฒ๋ฅผ ์ค์ ์๋ฒ๋ก ์ฌ๋ฆฌ๋ฉด ์๋ฌด๋ ๊ธ์ ์ธ ์ ์๊ฒ ํ๋ฉด ์๋๋ค.
- ๋ฐ๋ผ์ ์ธ์ฆ ์์คํ ์ ๊ตฌ์ถํ๋ค.
-
์ธ์ฆ (์ต์ํ์ ๋ณด์)
- ์ฐ๋ฆฌ๋ ๊ณ์ ์ด ํ์ํ๋ค.
- ๊ณ์ ์ฑ์ ๊ตฌ์ฑ
- ๊ฐ์ - ๋ก๊ทธ์ธ - ๊ณ์ ์ ๋ณด - ์ ๋ณด ์์ - ํํด
-
CRUD
- ์ฅ๊ณ ๋ CRUD์ ์ต์ ํ๋์ด ์๋ ์ฑ ์ค ํ๋์ด๋ค.
- Class based View (์ฅ๊ณ )
- ํด๋์ค์ ๊ธฐ๋ฐํ ๋ชจ๋ธ
- Create view (๊ฐ์ )
- Read view (๊ณ์ ์ ๋ณด)
- Update view (์ ๋ณด ์์ )
- Delete view (ํํด)
- ์ฅ๊ณ ์์ ๋ช๊ฐ์ง ํด๋์ค๋ฅผ ์์ ๋ฐ์ผ๋ฉด ๋๋ถ๋ถ์ ๊ธฐ๋ฅ์ ์ฅ๊ณ ๊ฐ ์ ๊ณตํ๋ค.
- Function Based View
- ํจ์์ ๊ธฐ๋ฐํ ๋ชจ๋ธ
-
Create view
-
Django์์ ์ ๊ณตํ๋ CreateView๋ฅผ ์ฌ์ฉํ๋ค
-
class AccountCreateView(CreateView) : #ํ๋ผ๋ฏธํฐ 1 ๋ฌด์จ ๋ชจ๋ธ ? model = User # ๊ณ์ ์ ์ค์ํ ๊ณผ์ ์ด๊ธฐ ๋๋ฌธ์ ๊ธฐ๋ณธ์ ํ ํ๋ฆฟ์ ์ ๊ณตํ๋ค. form_class = UserCreationForm # ๊ณ์ ์ ๋ง๋ค ๋ ์ฑ๊ณตํ์ผ๋ฉด ๊ฒฝ๋ก ์ง์ # reverse_lazy๋ success_url = reverse_lazy('accountapp:hello_world') # ํ์๊ฐ์ ํ ๋ ๋ณผ HTML ์ง์ template_name = 'accountapp/create.html'
-
-
ํ๋ผ๋ฏธํฐ
-
model = User
- first_name,last_name, email, is_staff ๋ฑ์ด ์ด๋ฏธ ์ค์ ๋์ด ์๋ ์ํ๋ก ์ ๊ณต๋๋ค.
username = models.CharField( _('username'), max_length=150, unique=True, help_text=_('Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.'), validators=[username_validator], error_messages={ 'unique': _("A user with that username already exists."), }, ) first_name = models.CharField(_('first name'), max_length=150, blank=True) last_name = models.CharField(_('last name'), max_length=150, blank=True) email = models.EmailField(_('email address'), blank=True) is_staff = models.BooleanField( _('staff status'), default=False, help_text=_('Designates whether the user can log into this admin site.'), )
-
form_class = UserCreationForm
- password ์ ๋ ฅ, ํ์ธ ๋ฑ ๊ฒ์ฆ ์์ ์ ํ ์ ์๋ค.
-
-
-
django ๊ธฐ๋ณธ ํ์๊ฐ์ form ์ด์ฉํด์ ๊ตฌํํ๊ธฐ
<div style = "text-align: center">
{% comment %} accountapp_crate๋ก ์ฐ๊ฒฐํด๋ผ {% endcomment %}
<form action="{% url 'accountapp:create' %}" method = "post">
{% csrf_token %}
{% comment %} ์ฅ๊ณ ์์ ์ ๊ณตํ๋ ๊ธฐ๋ณธ ํผ ์ฌ์ฉ {% endcomment %}
{{ form }}
<input type="submit" class = "btn btn-primary">
</form>
</div>
-
Login view
-
๋ก๊ทธ์ธ๊ณผ ๋ก๊ทธ์์ view๋ฅผ ๋ง๋ค๊ณ Redirectํ ๊ฒฝ๋ก๋ฅผ ์ฐพ์์ผํ๋ค.
-
next๊ฐ ์กด์ฌํ๋ฉด next๋ก ๊ฐ๋ค.
-
next๊ฐ ์์ผ๋ฉด setting ์์ ์๋ Login Redirect_URL๋ก ๊ฐ๋ค.
-
Login Redirect_URL๋ ์์ผ๋ฉด Default๋ก ๊ฐ์ ์๋ฌ๊ฐ ๋๋ค.
-
๊ทธ๋์ ๊ฒฝ๋ก๋ฅผ ์ค์ ํด์ค์ผํ๋ค.
-
๊ฐ์ฅ ์ฒ์ ๋ฐ๋ next ๊ฒฝ๋ก๋ฅผ ์ค์ ํด์ฃผ๊ณ ,
<a href="{% url 'accountapp:login' %}?next={{request.path}}"> {% else %} <a href="{% url 'accountapp:logout' %}?next={{request.path}}">
-
Login_redirect_url๋ฅผ ์ค์ ํ๋ค.
- html ํ์ผ์ ๋ธ๋ผ์ฐ์ ์๊ฒ ๋ณด์ฌ์ค
- ๋ค๋ฅธ url๋ก ์ด๋ํด์ค ๊ทธ๋ผ ๋ค๋ฅธ url์ด view์ ์์ฒญ์ ๋ณด๋ด ๊ทธ๋ผ url์ด render๋ก ๋ค์ ๋ณด๋ด์ค๊ฑฐ์ผ
- ๋ง์ฝ html์ ์ง์ ๋ ๋๋งํ์ง ์๋ create๋ delete ํจ์๋ฅผ views.py์์ ์ฌ์ฉํ ๋๋ redirect๋ฅผ ์ฌ์ฉํด์ผํ๋ค.
- render๋ฅผ ์ฌ์ฉํ๋ฉด ๋ณด์ฌ์ค html์ด ์๊ธฐ๋ ํ๊ณ , html์ ์ง์ ๋ ๋ํ๋ค๋ฉด url์ด ๊ผฌ์
- Django์์ ๋ถํธ์คํธ๋ฉ์ ์ค์นํ๋ ค๋ฉด ๋จผ์ pip install์ ํด์ค๋ค.
pip install django-bootstrap4
- Seetings.py์ ๋ฑ๋กํด์ค๋ค.
- ์ฐ๊ณ ์ถ์ html์ ๋ก๋ํด์ค๋ค. (์คํํฑ์ฒ๋ผ)
{% load bootstrap4 %}
- form์ ์ฌ์ฉํ๋ ค๋ฉด
{% bootstrap_form form %}
-
๋ฐฉ๋ฒ 1. ๋ธ๋ผ์ฐ์ ์บ์ ์ญ์
- ๊ฒฝ๋ก : ํฌ๋กฌ > ์ค์ > ๊ฐ์ธ์ ๋ณด ๋ฐ ๋ณด์ > ์ฟ ํค ๋ฐ ๊ธฐํ ์ฌ์ดํธ ๋ฐ์ดํฐ > ๋ชจ๋ ์ฟ ํค ๋ฐ ์ฌ์ดํธ ๋ฐ์ดํฐ ๋ณด๊ธฐ > ๋ชจ๋ ์ญ์
-
๋ฐฉ๋ฒ 2. ๋งํฌ ์ฝ๋์ URL ๋ณ๊ฒฝ
- CSS ํ์ผ์ ๋งํฌํ๋ HTML ํ์ผ(PHP, JSP)์ ์ด์ด ๊ธฐ์กด CSS ํ์ผ์ URL ๋ค์ ?after๋ฅผ ๋ถ์ด๊ธฐ
-
**๋ฐฉ๋ฒ 3. ์๋ก๊ณ ์นจ **
- ctrl + shift + F5
-
** ๋ฐฉ๋ฒ4. ์บ์ฑ ์ฌ์ฉ ์ค์ง **
- ๊ฐ๋ฐ์๋๊ตฌ -> ๋คํธ์ํฌ -> ์บ์ฑ ์ฌ์ฉ ์ค์ง
-
์ฅ๊ณ ์์๋ Detail View๋ผ๊ณ ํ๋ค. ๋ค๋ฅธ๋ฐ์๋ Readview๋ผ๊ณ ๋ ํ๋ ๋ชจ์
-
DetailView ํด๋์ค๋ฅผ ์์ ๋ฐ์์ ์งํํ๋ค.
class AccountDetailView(DetailView) :
model = User
template_name = 'accountapp/detail.html'
-
url์๋ ์ ์ ๋ง๋ค ์ธ๋ถ ์ ๋ณด๋ฅผ ๋ฐ์์์ผํ๋๊น pk๋ฅผ ์ถ๊ฐ์ ์ผ๋ก ๋ฃ์ด์ค๋ค.
-
ํด๋์ค ๋ด๋ถ์ target_user๋ฅผ ์ค์ ํด์ผ ํ๋๋ฐ, ์ด๋ ๋ค๋ฅธ ์ฌ๋์ด ๋ด ํ์ด์ง๋ฅผ ๋ค์ด์์ ๋ ๋ด ํ์ด์ง๊ฐ ๋ณด์ด๊ฒ ํ๋ค. target_user๋ฅผ ์ค์ ํ์ง ์์ผ๋ฉด, ๋ค๋ฅธ ์ฌ๋์ด my_page์ ์ ๊ทผํด๋ ๊ทธ ์ฌ๋ ์ ๋ณด๊ฐ ๋ํ๋๊ฒ ๋๋ค..?
-
์๋ ๋ค๋ฅธ ์ฌ๋์ด ๋ด ์ ๋ณด๋ฅผ ์๋ณด๋๊ฑฐ์ง.... ์ธ์คํ ํผ๋ ๊ฐ์ ๋๋์ด๋ผํ๋ค ์ผ๋จ ^^
-
context_object_name
- ๊ธฐ๋ณธ์ ์ผ๋ก ํ์์ ๋ณด ์์ ์ด๊ธฐ ๋๋ฌธ์ CreateView์ ์ ์ฌํจ
class AccountUpdateView(UpdateView) :
model = User
form_class = UserCreationForm
success_url = reverse_lazy('accountapp:hello_world')
# ์ ๋ณด์์ ํ ๋ ํ ๋ ๋ณผ HTML ์ง์
template_name = 'accountapp/update.html'
#updateview
<div class="mb-4">
<h4>Change Info</h4> </div>
<form action="{% url 'accountapp:update' user.pk %}" method = "post">
{% csrf_token %}
{% comment %} ์ฅ๊ณ ์์ ์ ๊ณตํ๋ ๊ธฐ๋ณธ ํผ ์ฌ์ฉ {% endcomment %}
{% bootstrap_form form%}
<input type="submit" class = "btn btn-primary">
</form>
</div>
- ๋ค๋ง, CreateForm๋ฅผ ๊ทธ๋๋ก ์ฌ์ฉํ๋ฉด ID๋ ๋ณ๊ฒฝํ ์ ์๊ฒ ๋ํ๋จ
- ์ผ๋ฐ์ ์ผ๋ก ID๋ ๋ณ๊ฒฝ ๋ถ๊ฐ๋ฅํ๊ฒ ํ๋ค. ๋ฐ๋ผ์ ์ด๋ฅผ ๋นํ์ฑํํ๊ธฐ ์ํด UserCreateionForm์ด ์๋ ์๋ก์ด Form์ ์ด์ฉํ๋ค.
- ์๋ก์ด forms.py๋ฅผ ๋ง๋ค์ด์ UserCreationForm์ ์์๋ฐ์ ID๋ง ๋ณ๊ฒฝ ๋ถ๊ฐ๋ฅํ๊ฒ ๋ง๋ ๋ค.
from django.contrib.auth.forms import UserCreationForm
class AccountUpdateForm(UserCreationForm) :
def __init__(self,*args,**kwargs) :
super().__init__(*args, **kwargs)
# id๋ฅผ ๋ณ๊ฒฝ ๋ถ๊ฐ๋ฅํ๊ฒ ํ๋ค.
self.fields['username'].disabled = True
- Delete View๋ฅผ ์ด์ฉํ๋ค.
- detail.html์ delete ๊ธฐ๋ฅ์ ๋ง๋ค์ด์ ๋ง์ฝ ๋ณธ์ธ์ด๋ผ๋ฉด ํํด ๋ฒํผ์ด ๋ณด์ด๊ฒ ํ๋ค.
- ์ญ์ ๋ฒํผ์ ๋๋ฅด๋ฉด ์ญ์ ๋๋ค.
#views.py
class AccountDeleteView(DeleteView) :
model = User
success_url = reverse_lazy('accountapp:login')
template_name = 'accountapp/delete.html'
#delete.html
<form action="{% url 'accountapp:delete' pk=user.pk %}" method = "post">
#detail.html
{% if target_user == user %}
{% comment %} user ์ ๋ณด์์ ์ด ๊ฐ๋ฅํ ์ ๋ณด ์์ ํ์ด์ง๋ฅผ ํ์ {% endcomment %}
<a href="{% url 'accountapp:update' pk=user.pk %}">
<p>
Change Info
</p>
</a>
<a href="{% url 'accountapp:delete' pk=user.pk %}">
<p>Quit</p>
- ์ค๋ ๋ฆฌ์
์๋ชปํ๋ค๊ฐ ์๋จ๊ฐ ๊ฑด๋ ๋ปํจ
- ์ด์ ์ปค๋ฐ ํ ๋ฒ์ ํ ๋ ค๊ณ ;
๋จผ์ reset๋ถํฐ
git log --oneline
ํ๋ฉด ์ ๋ณด๊ฐ ๋์จ๋ค.
์ฌ๊ธฐ์ ์์์ 2๋ฒ์งธ ์ปค๋ฐ์ ์ทจ์ํ๊ณ ์ถ์ผ๋ฉด ์ด๋ ๊ฒ ์น๋ฉด๋๋ค.
git reset --hard 2a4978e
- ์ปค๋ฐ์ ์ทจ์ํ๋ฉด ๊ทธ ๋๊น์ง ํ์ดํ ํ๋ ๊ฒ๋ค๋ ์ปค๋ฐ ์ด์ ์ผ๋ก ๋ค ํ์ผ์ด ๋์๊ฐ๋ค.
$git reflog
์ด๋ ๊ฒ ๋์ค๋๋ฐ, ๋๋ HEAD{3}์์ ๋ฆฌ์ ์ ์๋ชปํด์ ํ์ผ์ด ๋ค ๋ ๋ผ๊ฐ์ HEAD 4๋ก ๊ฐ์ด์ผํ๋ค.
๊ทธ๋ฌ๋ฉด
git reset --hard HEAD@{4}
์ด๋ ๊ฒ ์น๋ฉด ๋๋ค.
git push -f
ํด์ผํ๋ค ์ปจํ๋ฆญํธ๋๋๊น .,.
- ๊ธฐ์กด ๊ณผ์ ์์๋ ๋ก๊ทธ์ธ์ ํ์ง ์์๋ url์ ํตํด update๋ delete๋ฅผ ํ ์ ์์๋ค.
- ๋ฐ๋ผ์ user๊ฐ ์ธ์ฆ๋์ด์์๋๋ง ์ ๊ทผํ ์ ์๊ฒ ๋ณ๊ฒฝํ๋ค.
class AccountUpdateView(UpdateView) :
model = User
form_class = AccountUpdateForm
success_url = reverse_lazy('accountapp:hello_world')
template_name = 'accountapp/update.html'
context_object_name = 'target_user'
def get(self,*args, **kwargs) :
if self.request.user.is_authenticated :
return super().get(*args,**kwargs)
else :
return HttpResponseRedirect(reverse('accountapp:login'))
def post(self,*args, **kwargs) :
if self.request.user.is_authenticated :
return super().get(*args,**kwargs)
else :
return HttpResponseRedirect(reverse('accountapp:login'))
- ์ด๋ ๊ฒ ๊ตฌ์ฑํ๋ฉด ๋ก๊ทธ์ธ ํ์ง ์์ ์ ์ ๋ ๊ฑฐ๋ฅผ ์ ์์ง๋ง, ๋ก๊ทธ์ธ ๋์ด์์ผ๋ฉด ๋๊ตฌ๋ ๋ณธ์ธ์ ์ ๋ณด๋ ๋ฌผ๋ก ๋ค๋ฅธ ์ฌ๋์ ์ ๋ณด๋ฅผ ๋ณ๊ฒฝ ๋ฐ ์ญ์ ํ ์ ์๋ค.
- ๋ฐ๋ผ์ ์๋ฒ์ update๋ฅผ ์์ฒญํ ์ ์ ๊ฐ ํ์ฌ ๋ก๊ทธ์ธ ๋์ด์๋ ์ ์ ์ผ๋๋ง ๋ณ๊ฒฝ์ ํ๊ฒ ํ๊ณ , ์๋๋ผ๋ฉด 403์๋ฌ๋ฅผ ํ์ํ๊ฒ ํ๋ค.
def get(self,*args, **kwargs) :
# ๋ก๊ทธ์ธ์ด ๋์ด์์ผ๋ฉด์, pk ๋ก update๋ฅผ requestํ ์ ์ ๊ฐ ํ์ฌ ๋ก๊ทธ์ธ ํ ์ ์ ์ ๊ฐ์ผ๋ฉด
if self.request.user.is_authenticated and self.get_object() == self.request.user :
return super().get(*args,**kwargs)
#๊ฐ์ง ์๋ค๋ฉด ๊ธ์ง๋ ๊ณณ์ผ๋ก ์จ๊ฒ์ผ๋ก ๋ฐ์์ ๋ณด๋ธ๋ค.
else :
return HttpResponseForbidden()
def post(self,*args, **kwargs) :
if self.request.user.is_authenticated and self.get_object() == self.request.user :
return super().get(*args,**kwargs)
else :
return HttpResponseForbidden()
-
ํ์ด์ฌ์์ ์ ๊ณตํ๋ ๊ธฐ๋ฅ, ๊ฐ๋ ์ฑ์ ํด์น๋ ๊ฒ์ ๋ง๊ธฐ ์ํด ์ฌ์ฉํจ @decorator ํํ
-
์์์ ๋ก๊ทธ์ธ์ฌ๋ถ์ ๋ฐ๋ผ ๋ง๋ ์ฝ๋๋ฅผ ์ผ์ผ์ด ์น ํ์ ์์ด django์์ ์ ๊ณตํ๋ login_required๋ง ํจ์ ์์ ์จ์ฃผ๋ฉด ๊ทธ๋๋ก ๊ตฌํํ ์ ์๋ค.
def hello_world(request) :
if request.user.is_authenticated :
if request.method == "POST" :
temp = request.POST.get('hello_world_input')
new_hello_world = HelloWorld()
new_hello_world.text = temp
new_hello_world.save()
return redirect(reverse('accountapp:hello_world'))
else :
hello_world_list = HelloWorld.objects.all()[::-1]
return render(request,'accountapp/hello_world.html',context={'hello_world_list': hello_world_list})
else :
return HttpResponseRedirect(reverse('accountapp:login'))
@login_required
def hello_world(request) :
if request.method == "POST" :
temp = request.POST.get('hello_world_input')
new_hello_world = HelloWorld()
new_hello_world.text = temp
new_hello_world.save()
return redirect(reverse('accountapp:hello_world'))
else :
hello_world_list = HelloWorld.objects.all()[::-1]
return render(request,'accountapp/hello_world.html',context={'hello_world_list': hello_world_list})
- ํด๋์ค์ ๊ฒฝ์ฐ๋ ํจ์์ ๋๊ฐ์ด @login_required๋ฅผ ์ ์ด๋ ์๋ํ์ง ์๋๋ค.
- login_required๋ฅผ ์ ์ผ๋ฉด ๋น๋ก๊ทธ์ธ ์ ์์ /์ญ์ ํ์ด์ง ์ ๊ทผ ๋ถ๊ฐ๋ง ๋ง๋ค ์ ์๋ค, ์ถ๊ฐ์ ์ผ๋ก ์๊น์ ๊ฐ์ด ํ์ธ์ ์ ๋ณด ์ ๊ทผ์ ๋ง๋๋ค.
- ๋ฐ๋ผ์ @method_decorator์ ์ธ์๋ฅผ ๋ฃ์ด์ค์ ์ ์ฉํ๋ค.
class AccountUpdateView(UpdateView) :
model = User
form_class = AccountUpdateForm
success_url = reverse_lazy('accountapp:hello_world')
template_name = 'accountapp/update.html'
context_object_name = 'target_user'
def get(self,*args, **kwargs) :
# ๋ก๊ทธ์ธ์ด ๋์ด์์ผ๋ฉด์, pk ๋ก update๋ฅผ requestํ ์ ์ ๊ฐ ํ์ฌ ๋ก๊ทธ์ธ ํ ์ ์ ์ ๊ฐ์ผ๋ฉด
if self.request.user.is_authenticated and self.get_object() == self.request.user :
return super().get(*args,**kwargs)
#๊ฐ์ง ์๋ค๋ฉด ๊ธ์ง๋ ๊ณณ์ผ๋ก ์จ๊ฒ์ผ๋ก ๋ฐ์์ ๋ณด๋ธ๋ค.
else :
return HttpResponseForbidden()
def post(self,*args, **kwargs) :
if self.request.user.is_authenticated and self.get_object() == self.request.user :
return super().get(*args,**kwargs)
else :
return HttpResponseForbidden()
@method_decorator(login_required,'get')
@method_decorator(login_required,'post')
@method_decorator(account_ownership_required,'get')
@method_decorator(account_ownership_required,'post')
class AccountUpdateView(UpdateView) :
#ํ๋ผ๋ฏธํฐ 1 ๋ฌด์จ ๋ชจ๋ธ ?
model = User
# ๊ณ์ ์ ์ค์ํ ๊ณผ์ ์ด๊ธฐ ๋๋ฌธ์ ๊ธฐ๋ณธ์ ํ
ํ๋ฆฟ์ ์ ๊ณตํ๋ค.
form_class = AccountUpdateForm
# ๊ณ์ ์ ๋ง๋ค ๋ ์ฑ๊ณตํ์ผ๋ฉด ๊ฒฝ๋ก ์ง์
# reverse_lazy๋
success_url = reverse_lazy('accountapp:hello_world')
# ์ ๋ณด์์ ํ ๋ ํ ๋ ๋ณผ HTML ์ง์
template_name = 'accountapp/update.html'
context_object_name = 'target_user'
#decorator.py
from django.contrib.auth.models import User
from django.http import HttpResponseForbidden
def account_ownership_required(func) :
def decorated(request,*args, **kwargs) :
# ์์ฒญ์ ๋ฐ์ผ๋ฉด์ ๋ฐ์ pk๋ฅผ pk๋ก ๊ฐ์ง๊ณ ์๋ userobject
user = User.objects.get(pk=kwargs['pk'])
if user != request.user :
return HttpResponseForbidden()
return func(request,*args,**kwargs)
return decorated
- ์ถ๊ฐ์ ์ผ๋ก decorator๋ฅผ ๋ฆฌ์คํธ์ ๋ด์ ํด๋์ค์ ํ ๋ฒ์ ์ ์ฉ์ํฌ ์ ์๋ค.
has_ownership = [account_ownership_required,login_required]
@method_decorator(has_ownership,'get')
-
superuser ๋ง๋ค๊ธฐ
python manage.py cratesuperuser
ID, ๋น๋ฐ๋ฒํธ ์ ๋ ฅ
media ์ธํ ํ๊ธฐ (STATIC๊ณผ ์ ์ฌ)
- media url : ์ฃผ์์ฐฝ์ media์ดํ ๊ฒฝ๋ก๋ก ์ ๊ทผํด์ผ ์ค์ ๋ก ๋ฏธ๋์ด์ ์ ๊ทผ ๊ฐ๋ฅ
- media root : ๋ฏธ๋์ดํ์ผ์ ์๋ฒ์ ์ฌ๋ ธ์ ๋ ์๋ฒ ์ด๋ ๊ฒฝ๋ก์ ์ง์ ๋ ๊ฒ์ธ์ง ๊ทธ ๋ฃจํธ๊ฐ ์ด๋์ธ์ง๊ฐ์ ๋ํ ์ ๋ณด
- ์ฐ๋ฆฌ๊ฐ ํ์ผ์ ์๋ฒ์ ์ฌ๋ฆฌ๋ฉด ํ๋ก์ ํธ ๋ด๋ถ์ meida ๋๋ ํ ๋ฆฌ๊ฐ ์๊ธฐ๋ฉด์ media๊ฐ ๊ฑฐ๊ธฐ์ ์ ์ฅ๋จ
MEDIA_URL = '/media1/'
#ex) 127.8.8.1:8880/media1/test.jpg
MEDIA_ROOT = os.path.join(BASE_DIR,'media')
- ์ฅ๊ณ ์์ ์ด๋ฏธ์ง๋ฅผ ๊ด๋ฆฌํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ pillow
- ๊ธ์ด ๋๋ฌด ๋ง์์ ๋ต๋ตํด์ Posting์ ์ญ์ ํ๋ ๊ธฐ๋ฅ ๊ตฌํ
def text_delete(request,pk) :
if request.method == "POST" :
text = HelloWorld.objects.get(pk=pk)
text.delete()
return redirect('accountapp:hello_world')
{% if hello_world_list %}
{% comment %} hello_world_list๋ฅผ ๋๋ฉด์ ๋ด๋ถ ์์ ํ๋์ฉ ์ถ๋ ฅ {% endcomment %}
{% for hello_world in hello_world_list %}
<h1>{{hello_world.text}}</h1>
<form action="{% url 'accountapp:text_delete' hello_world.pk %}" method='POST'>
{% csrf_token %}
<input type="submit" class = "btn btn-danger" value="์ญ์ " width="20px">
</form>
{% endfor %}
{% endif %}
- detail.html์ ๋ค์ด๊ฐ๋ฉด ID๊ฐ ๊ทธ๋๋ก ๋ ธ์ถ์ด๋๋ค. ์ด๋ ๋ณด์์ ๋ฌธ์ ๊ฐ ๋ ์๋ ์์ผ๋ฏ๋ก, ๋๋ค์์ผ๋ก ํ์๋๊ฒ ํ๋ค.
- ํ๋กํ ์ฌ์ง - ๋๋ค์ - ๋ฉ์์ง ๊ฐ ํฌํจ๋ ํ๋กํ์ ๋ง๋ค์ด๋ณด์ ! !
- Account์ Profile์ 1๋1 ๋งค์นญ ์์ผ์ ๊ณ์ ํ๋์ ํ๊ฐ์ ํ๋กํ๋ง ๊ตฌ์ถํ ์ ์๋๋ก ๊ตฌ์ฑํ ๊ฒ์ด๋ค.
- account์ profile์ 1๋1 ๋งค์นญ ์ํค๊ธฐ ์ํด onteonefield๋ฅผ ์ฌ์ฉํ๋ค.
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Profile(models.Model) :
# profile ์ฃผ์ธ์ด ๋๊ตฐ์ง ์ผ๋์ผ ๋งค์นญ
# on_delete์ ์ญํ ์ ๊ฐ์ฒด๊ฐ ์ฌ๋ผ์ง ๋ ์ด ํ๋กํ๋ ์์ด์ง๊ฒ ํ๋ ค๊ณ CASCADE ์ค์
#๋ฐ๋ผ์ ์ ์ ๊ฐ ์ฌ๋ผ์ง ๋ ์ญ์ ๋จ
# related_name์ค์ ํ๋ฉด user.profile ๊ฐ์ ์์ผ๋ก profile์ ์ ๊ทผํ ์ ์๋ค
user = models.OneToOneField(User, on_delete=models.CASCADE,related_name='profile')
# ์ด๋ฏธ์ง๊ฐ ์
๋ก๋ ๋์์ ๋ ์๋ฒ๋ด๋ถ์ ์ ์ฅ๋ ๊ณณ์ ์ ํด์ฃผ๋ ๊ฒฝ๋ก upload_to
# null์ ture๋ก ์ค์ ํด์ ํ๋กํ ์ฌ์ง์ ์ฌ๋ฆฌ์ง ์์๋ ๊ด์ฐฎ๊ฒ ๋ง๋ฌ
image = models.ImageField(upload_to='profile/',null=True)
# ๋๋ค์์ ์ ์ผํ ๋๋ค์์ ๊ฐ์ง๋๋ก unique= True๋ก ํจ
nickname = models.CharField(max_length = 20, unique=True, null=True)
#๋ํ๋ช
message = models.CharField(max_length=100, null =True)
- form๊ณผ modelform์ด ์๋๋ฐ, user๊ด๋ จ์ django์์ ์ ๊ณตํด์ฃผ๋ ๊ธฐ๋ณธ ํผ๋ค์ ์ฌ์ฉํ๋๋ฐ, Profile์ ๊ธฐ๋ณธ ์ ๊ณต form์ด ์๋ค. ๊ทธ๋์ ๋ฐ๋ก ๋ง๋ค์ด์ผ ํ๋๋ฐ, ๋ค ํ์ดํํ๊ธฐ ํ๋๋๊น ModelForm์ ์ ๊ณตํ๋ค.
- ๊ธฐ์กด ๋ชจ๋ธ์ Form์ผ๋ก ๋ณํํด์ฃผ๋ ๋ฐฉ์
- ํ๋กํ์ด ์๋ค๋ฉด ์ ์ ์ ๋๋ค์์ ๋ณด์ฌ์ฃผ๊ณ , ํ๋กํ์ด ์๋ค๋ฉด ํ๋กํ์ ๋ง๋ค ์ ์๋ ํ์ด์ง๋ก ์ด๋
# accounts/detail.html
{% if traget_user.profile %}
<h2>
{% comment %} user ์ด๋ฆ {% endcomment %}
{{ target_user.profile.nickname }}
</h2>
{% else %}
<a href="{% url 'profileapp:create' %}">
<h2>
Create Profile
</h2>
</a>
{% endif %}
-
ํ์ผ์ ๋ฃ์๋๋ฐ๋ ํ์ผ์ ๋ฃ์ผ๋ผ๋ ์ค๋ฅ ๋ฐ์
- create.html์์ img๋ฅผ ๋ณด๋ผ๋๋ enctype์ ๋ช ์ํด์ผํ๋๋ฐ, ํ์ง ์์์ ๋ฐ์ํ๋ ๋ฌธ์
- enctype์ ๋ช ์ํด์ ํด๊ฒฐํ ์ ์์
#profile/create.html <form action="{% url 'profileapp:create' %}" method = "post" enctype="multipart/form-data">
-
profile์ user.id๊ฐ ์๋ค๋ ๋ฌธ์ ๋ฐ์.
-
profile์ view์์ ๋ง๋ค ๋ validation ๊ณผ์ ์์ form ์ ์ฅ ์ commit์ ํ์ง ์๊ณ , user๋ฐ์ดํฐ๋ฅผ ๋ฐ์ ํ ์ ์ฅํ๊ฒ ๋ณ๊ฒฝ
class ProfileCreateView(CreateView) :
model = Profile
context_object_name = 'target_profile'
form_class = ProfileCreationForm
success_url = reverse_lazy('accountapp:hello_world')
template_name = 'profileapp/create.html'
def form_valid(self, form) :
temp_profile = form.save(commit=False)
temp_profile.user = self.request.user
temp_profile.save()
return super().form_valid(form)
@method_decorator(profile_ownership_required,'get')
@method_decorator(profile_ownership_required,'post')
class ProfileUpdateView(UpdateView) :
model = Profile
context_object_name = 'target_profile'
form_class = ProfileCreationForm
success_url = reverse_lazy('accountapp:hello_world')
template_name = 'profileapp/update.html'
- ํ๋กํ ์์ฑ ๋ฐ ๋ณ๊ฒฝ์ ๋ฉ์ธํ์ด์ง๊ฐ ์๋ account:detail๋ก ๋ณด๋ด๊ณ ์ถ์๋ฐ, ๊ทธ๋ฌ๋ ค๋ฉด pk๋ฅผ ์ค์ผํจ
- pk๋ฅผ ์ฃผ๋ ค๋ฉด ๋ด๋ถ method๋ฅผ ์์ ํด์ผํจ
- get_success_url์ ์์ ํด์ pk๋ฅผ ์ฐพ์ ๋๊ฒจ์ฃผ๊ฒํจ
class ProfileUpdateView(UpdateView) :
model = Profile
context_object_name = 'target_profile'
form_class = ProfileCreationForm
template_name = 'profileapp/update.html'
def get_success_url(self):
# object๋ profile์ ๊ฐ๋ฅดํด
#์ฆ, ์ฐ๊ฒฐ๋์ด์๋ user์ pk๋ฅผ ์ฐพ์์ detail๋ก ๊ฐ ๋ ๊ฐ์ด ๋๊ฒจ์ค
return reverse('accountapp:detail',kwargs={'pk':self.object.user.pk})
- ์นด๋ํ์ผ๋ก ๊ฒ์๊ธ ์ฐฝ์ ๋ง๋ค๊ธฐ ์ํด js์ magic_grid๊ฐ ํ์ํจ
- html ๋ง๋ค๊ธฐ
- divํ๊ทธ ๋ง๋ค๊ธฐ
<div class="container">
<div>
<img src="https://picsum.photos/200/300">
</div>
<div>
<img src="https://picsum.photos/200/340">
</div>
<div>
<img src="https://picsum.photos/200/280">
</div>
<div>
<img src="https://picsum.photos/200/390">
</div>
<div>
<img src="https://picsum.photos/200/200">
</div>
<div>
<img src="https://picsum.photos/200/500">
</div>
<div>
<img src="https://picsum.photos/200/100">
</div>
<div>
<img src="https://picsum.photos/200/250">
</div>
<div>
<img src="https://picsum.photos/200/320">
</div>
<div>
<img src="https://picsum.photos/200/300">
</div>
<div>
<img src="https://picsum.photos/200/300">
</div>
<div>
<img src="https://picsum.photos/200/270">
</div>
<div>
<img src="https://picsum.photos/200/330">
</div>
- style ์ถ๊ฐ
<style>
.container div {
width: 250px;
background-color: antiquewhite;
display: flex;
justify-content: center;
align-items: center;
border-radius: 1rem;
}
.container img {
width: 100%;
}
</style>
- static ํด๋ ๋ด๋ถ์ jsํด๋ ์์ฑ, magicgrid.js์์ฑ ํ magicgrid ์๋ url์์ ๊ฐ์ ธ์ค๊ณ ๋ถ์ฌ๋ฃ๊ธฐ
https://github.com/e-oj/Magic-Grid/blob/master/dist/magic-grid.cjs.js
- magicgrid.js ํ๋จ์ magicGrid์ต์ ์ถ๊ฐ
let magicGrid = new MagicGrid({
container: '.container',
animate: true,
gutter: 30,
static: true,
useMin: true
});
var masonrys = document.getElementsByTagName("img");
#๋ชจ๋ html ํ๊ทธ ๋ด์ ์ด๋ฏธ์ง์ ๋ํด์ ๋ค ๋ก๋๊ฐ ๋๋ฉด ๋งค์ง๊ทธ๋ฆฌ๋๋ฅผ ๋ค์ ํฌ์ง์
๋ํด๋ผ
for (let i = 0; i < masonrys.length; i++){
masonrys[i].addEventListener('load', function(){
magicGrid.positionItems();
}, false);
}
magicGrid.listen();
- ์ฌ์ง์ ์ผ๋จ ๋๋ค์ผ๋ก ๋ฐฐ์นํ๋ค.
<div>
<img src="https://picsum.photos/200/330">
</div>
#์ด๋ฏธ์ง ๋ค url์ ์ฒซ๋ฒ์งธ ์ธ์๋ ๋๋น, ๋๋ฒ์งธ ์ธ์๋ ๋์ด
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Article(models.Model) :
# set_null ์ค์ ์ ํจ์ผ๋ก ์ญ์ ๋์์ ๋๋ ๊ฒ์๊ธ์ด ์ฌ๋ผ์ง์ง ์๊ณ , ์ ์ ์์ ํ์๊ฐ ๋๊ฒ ํจ.
# related_name= 'article'์ ์ ์ ๊ฐ ์ฌ๋ฌ ๊ธ์ ์ธ ์ ์๊ธฐ ๋๋ฌธ์ user๊ฐ ์ด ๊ธ์ ๋ค ๋ณด๋ ค๊ณ ์ค์ ํ๋ ๊ฒ
writer = models.ForeignKey(User, on_delete=models.SET_NULL,related_name='article',null=True)
title = models.CharField(max_length=200, null=True)
image = models.ImageField(upload_to = 'article/', null=False)
content = models.TextField(null=True)
created_at = models.DateField(auto_now_add=True)
updated_at = models.DateField(auto_now=True)
class ArticleListView(ListView) :
#์ด๋ค ๋ชจ๋ธ ?
model = Article
#์ด๋ ํ
ํ๋ฆฟ ์ด๋ฆ์ ์ธ๊ฑด์ง
context_object_name = 'article_list'
template_name = 'articleapp/list.html'
#ํ ํ์ด์ง์ ๋ช๊ฐ์ ๊ฐ์ฒด๋ฅผ ๋ณด์ฌ์ค ๊ฒ์ธ์ง
paginate_by = 25
-
Generate Page of objects
- ํ์ด์ง์ ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ค
- ๊ตฌ๊ธ์์ ๊ฒ์์ ํ ๋ ํ๋จ์ ํ์ด์ง๊ฐ ๋ณด์ด๋ ๊ฒ์ ํ์ด์ง๋ค์ด์ ์ด๋ผ๊ณ ํ๋ค.
-
Infiniite Scroll
- ํ์ด์ค๋ถ์ด๋ ์ธ์คํ๊ทธ๋จ์์๋ ๋ฌดํ์ผ๋ก ์๋๋ก ๋ด๋ฆด๋ ๊ฒ์๊ธ๋ค์ด ๋์จ๋ค.
-
page_obj๋ฅผ ํ ํ๋ฆฟ์์ ์ฌ์ฉํ ์ ์๋ค.
- Article_LIst๋ ๊ฒ์๊ธ์ ๋ฆฌ์คํธ
- ์ด ์์ ๊ฐ์ฒด์ ๊ฐ๊ฐ ์ ๋ณด๊ฐ ๋ด๊ฒจ์๊ณ , ์ด ์ ๋ณด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก HTML์ ํํฐ๋ ์คํธ์ ์นด๋ํ ๋ ์ด์์์ ๊ฐ์ ธ์ค๋ฉด ๋ฟ๋ ค์ฃผ๋ ์ญํ ์ ํ๋ ๊พธ๋ฌ๋ฏธ์ด๋ค.
- Page_obj
- ํ๋จ์ ํ์ด์ง๋ฅผ ๋ง๋ค ๋ ์ฌ์ฉํ๋ ์ญํ
- create view
class ArticleCreatView(CreateView) :
model = Article
form_class = ArticleCreationForm
template_name = 'articleapp/create.html'
def form_valid(self,form) :
temp_article = form.save(commit=False)
temp_article.writer = self.request.user
temp_article.save()
return super().form_valid(form)
def get_success_url(self) :
return reverse('articleapp:detail',kwargs={'pk' : self.object.pk})
- detail view
class ArticleDetailView(DetailView) :
model = Article
context_object_name = 'target_article'
template_name = 'articleapp/detail.html'
- ํฌ๋ฆฌ์์ดํธ ๋ทฐ์์๋ form์ด ์๊ณ , detail view์์๋ object๊ฐ ์๋ค.
- ๋ง์ฝ ๋ํ
์ผ๋ทฐ๋ฅผ Form๊ณผ ๊ฐ์ด ์ฌ์ฉํ๊ณ ์ถ๋ค๋ฉด?
- Detail view์์ ์์๋ง Article์ ๋ด์ฉ์ด ๋์ค๊ณ ๋ฐ์๋ Comment Form์ ๋ง๋ค๋ ค๋ฉด ๋ฌธ์ ๊ฐ ์๊ธด๋ค.
- ์ด ๋ Mixin์ ์ฌ์ฉํด์ Detailview์ ๋ค์ค ์์์ ์ค ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์๋ค.
class AccountDetailView(DetailView,FormMixin) :
model = User
form_class = CommentCreationForm
context_object_name = 'target_user'
- Create/Delte
- Success_ulr to related article
- Model(article/writer/content/created_at)
-
์ด๋ป๊ฒ ๋ชจ๋ฐ์ผ ์น์ผ๋ก ์ฐ๊ฒฐํ๋๊ฐ?
-
django๋ฅผ ์ด๋ฉด 127.0.0.1:8000์ผ๋ก ์ฐ๊ฒฐ๋๋ค.
- ์ด๋ ๋ก์ปฌ์์๋ง ์ ์์ด ๊ฐ๋ฅํ๋ค.
-
๋ฐ๋ผ์ url์ 0.0.0.0:8000์ผ๋ก ๋ณ๊ฒฝํ๋ค.
- ๋ก์ปฌํธ์คํธ ๋ง๋๊ณ ip ์ฃผ์๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ ์ํ ์ ์๋ค.
-
์์ดํ์ด๊ฐ ๊ฐ์ด ์ฐ๊ฒฐ๋์ด ์์ผ๋ฉด ์ค์ ๊ฐ๋ฅํ๋ค.
-
django runserver๋ฅผ 0.0.0.0:8000์ผ๋ก ๊ตฌ๋ํ๊ธฐ
$ python manage.py runserver 0.0.0.0:8000
- settings.py์กฐ์ ํ๊ธฐ
ALLOWED_HOSTS = ['*']
- '*'๋ฅผ ๋ฃ์ด์ฃผ๋ฉด ๋ชจ๋ ํธ์คํธ๋ฅผ ํ์ฉํ๋ค๋ ๋ป
- ๋ฐฐํฌ์์๋ ์ ๊ฒฝ์ธ ๊ฒ !
cmd์ฐฝ์์ ipconfig๋ฅผ ์ ๋ ฅํ๋ค.
C:\Users\SAMSUNG>ipconfig
- ์ด๋ฐ์์ผ๋ก ๋จ๋๋ฐ, IPv4 ์ฃผ์๋ฅผ mobile์์ ์ ๋ ฅํด์ฃผ๋ฉด ๋๋ค.
000.000.00.00:8000/articles/list/
- What You See Is What You Get(๋ณด๋๋๋ก ๊ธ์ด ์จ์ง๋ค!)
- ํ์ฌ๊น์ง๋ ๊ธ์ ์ฐ๋ฉด ์ผ๋ฐ์ ์ธ ํ ์คํธ๋ก ์ด๋ฃจ์ด์ ธ์๋๋ฐ, ์ด๋ฅผ ๋ ๊ตต๊ฒ๋ ํ๊ณ , ํฌ๊ฒ๋ ํ๊ณ , ๋ฐ์ค๋ ๊ธ๋ ๊ฒ
https://github.com/yabwe/medium-editor
<script src="//cdn.jsdelivr.net/npm/[email protected]/dist/js/medium-editor.min.js"></script>
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/[email protected]/dist/css/medium-editor.min.css" type="text/css" media="screen" charset="utf-8">
<script>var editor = new MediumEditor('.editable');</script>
- ๊ธฐ์กด
class ArticleCreationForm(ModelForm) :
class Meta :
model = Article
#writer์ ์๋ฒ ๋ด๋ถ์์ ์ค์ ํ๋ค.
fields = ['title','image','project','content']
- ๋ฆฌํฉํ ๋ง
class ArticleCreationForm(ModelForm) :
content = forms.CharField(widget=forms.Textarea(attrs={'class' : 'editable',
'style' : 'height auto;' :}))
class Meta :
model = Article
#writer์ ์๋ฒ ๋ด๋ถ์์ ์ค์ ํ๋ค.
fields = ['title','image','project','content']
{% extends 'base.html' %}
{% load bootstrap4 %}
{% block content %}
<script src="//cdn.jsdelivr.net/npm/[email protected]/dist/js/medium-editor.min.js"></script>
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/[email protected]/dist/css/medium-editor.min.css" type="text/css" media="screen" charset="utf-8">
<div style = "text-align: center; max-width: 500px; margin: 4rem auto;">
{% comment %} accountapp_crate๋ก ์ฐ๊ฒฐํด๋ผ {% endcomment %}
<div class="mb-4">
<h4>Article Create</h4> </div>
<form action="{% url 'articleapp:create' %}" method = "post" enctype="multipart/form-data">
{% csrf_token %}
{% comment %} ์ฅ๊ณ ์์ ์ ๊ณตํ๋ ๊ธฐ๋ณธ ํผ ์ฌ์ฉ {% endcomment %}
{% bootstrap_form form%}
<input type="submit" class = "btn btn-primary">
</form>
</div>
<script>var editor = new MediumEditor('.editable');</script>
{% endblock content %}
- Detail ํ์ด์ง์์๋ ํ๊ทธ๊ฐ ๋ณด์
- Django HTML์ safe ํ๊ทธ ์ถ๊ฐ
{{ target_article.content | safe }}
ํด๋น ํ์ด์ง์์ Icon์ ๊ฐ์ ธ์ค๋ ค๋ฉด HTML์์ ํด๋์ค์ ์ด๋ฆ์ ์ ๋ ฅํด์ฃผ๋ฉด ๊ฐ๋จํ๋ค.
https://github.com/google/material-design-icons
The font
and variablefont
folders contain pre-generated font files that can be included in a project. This is especially convenient for the web; however, it is generally better to link to the web font hosted on Google Fonts:
<link href="https://fonts.googleapis.com/css2?family=Material+Icons"
rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols"
rel="stylesheet">
Read more on Material Symbols or Material Icons in the Google Fonts developer guide.
์ ์ฉ ์
{% if target_user == user %}
<a class="material-icons" href="{% url 'profileapp:update' pk=target_user.profile.pk %}">
edit
</a>
{% endif %}
์ ์ฉ ํ
{% if target_user == user %}
<a class="material-icons" href="{% url 'profileapp:update' pk=target_user.profile.pk %}">
edit
</a>
{% endif %}
์ ์ฉ ์
<a href="{% url 'accountapp:update' pk=user.pk %}">
<p>
Change Info
</p>
</a>
<a href="{% url 'accountapp:delete' pk=user.pk %}">
<p>Quit</p>
</a>
์ ์ฉ ํ
<a class="material-icons" style="box-shadow: 0 0 3px #ccc border-radius: 10rem; padding: 0.4rem; color:skyblue;" href="{% url 'accountapp:update' pk=user.pk %}">
<p>
settings
</p>
</a>
<a class="material-icons" style="box-shadow: 0 0 3px #ccc border-radius: 10rem; padding: 0.4rem; color:red;" href="{% url 'accountapp:delete' pk=user.pk %}">
<p>cancel</p>
</a>