Skip to content

Latest commit

ย 

History

History
1417 lines (1026 loc) ยท 44.5 KB

TIL.md

File metadata and controls

1417 lines (1026 loc) ยท 44.5 KB

TIL

Django

  • ๋‹ค์–‘ํ•œ ์•ฑ์„ django ๋‚ด๋ถ€์—์„œ ๋งŒ๋“ค๊ณ  ์•ฑ๋งˆ๋‹ค ์ž์ฒด์ ์œผ๋กœ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” Front-endํŒŒ์ผ์„ ๊ตฌ์ถ•ํ•œ๋‹ค.
  • ๊ทธ๋ฆฌ๊ณ  ํ•ด๋‹น ์•ฑ์„ ์—ฎ์–ด์„œ ์›น ์„œ๋น„์Šค๋ฅผ ๊ตฌ์ถ•ํ•œ๋‹ค.
  • Django๊ฐ€ ๋ชจ์—ฌ์„œ Docker์˜ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ๋œ๋‹ค.

Docker

  • Django ์ปจํ…Œ์ด๋„ˆ๋Š” Django์„œ๋น„์Šค์˜ ์ผ, MariaDB๋Š” DB๊ด€๋ จ ์ผ, Nginx๋Š” ์„œ๋ฒ„ ๊ด€๋ จ ์ผ์„ ๋‚˜๋ˆ ์„œ ์ง„ํ–‰ํ•œ๋‹ค. (MVC?)
  • ์ด๋“ค์ด ๋ชจ์—ฌ์„œ Docker ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ•œ๋‹ค.
  • Docker๋ฅผ ๊ตฌ์ถ•ํ•˜๋ฉด Vultr๋กœ ์„œ๋ฒ„์— ๋„์ปค๋ฅผ ์˜ฌ๋ฆฐ๋‹ค.(AWS๋„ ์ด์šฉ ๊ฐ€๋Šฅ)

MTV ํŒจํ„ด

  • Model
    • ๋ชจ๋ธ์€ ์žฅ๊ณ ์™€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ํ†ต์‹ ํ•˜๊ฒŒ ๋„์™€์ฃผ๋Š” ๋„๊ตฌ์ด๋‹ค.
    • ๊ฐ์ฒด๋ฅผ ๋งŒ๋“œ๋Š”๋ฐ ์œ ์ €๊ฐ€ ์ƒˆ๋กœ ๊ฐ€์ž…ํ•˜๋ฉด ๊ฐ์ฒด๊ฐ€ ์ƒ๊น€, ์œ ์ €๊ฐ€ ๊ฒŒ์‹œ๊ธ€์„ ์“ฐ๋Š” ๊ฒƒ๋„ ๊ฐ์ฒด.
    • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—๋Š” Row, columns๊ฐ€ ์žˆ๋‹ค.
      • Article์ด๋ผ๋Š” ๊ฒŒ์‹œ๊ธ€์ด ์žˆ์œผ๋ฉด ๊ทธ ์•ˆ์— Title,article,image๊ฐ€ ์žˆ๋Š”๋ฐ ์ด๊ฐ€ DB์—์„œ๋Š” Columns๋กœ ๋งค์นญ๋œ๋‹ค.
      • ๋ชจ๋ธ์„ ์„ค์ •ํ•˜๋ฉด CRUD๋Š” ์žฅ๊ณ ๊ฐ€ ๋‹ค ํ•ด์ค€๋‹ค.
  • View
    • ์žฅ๊ณ ์˜ ๊ณ„์‚ฐ์„ ๋‹ด๋‹นํ•˜๋Š” ๋ถ€๋ถ„
    • User๊ฐ€ Server์— Request๋ฅผ ๋ณด๋‚ด๋ฉด Server๋Š” ์‘๋‹ต์„ ์œ„ํ•œ ์ ˆ์ฐจ๋ฅผ ๊ฑฐ์นœ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์‘๋‹ต์„ ๋ณด๋‚ธ๋‹ค.
      • ex) ์œ ์ € ๋กœ๊ทธ์ธ ,์œ ์ €๊ฐ€ ๋ณด๋‚ธ ์š”์ฒญ์ด ์œ ํšจํ•œ์ง€, DB์—์„œ ๊ฐ€์ ธ์˜ค๋Š” ๊ณผ์ •, ์œ ์ €์—๊ฒŒ ๋˜๋Œ๋ ค์ฃผ๋Š” ๊ณผ์ •
  • Template
    • ์‹ค์งˆ์ ์œผ๋กœ ๋ณด์ด๋Š” Front-end์š”์†Œ
    • ์œ ์ €๋Š” ์›น์‚ฌ์ดํŠธ์˜ visual์„ ๋ณด๋Š”๋ฐ ๊ทธ ๋‚ด๋ถ€๋ฅผ ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ•˜๊ณ  ์ƒ์„ฑํ•  ๊ฒƒ์ธ์ง€ ๋ณด์—ฌ์ฃผ๋Š” ๋ฐฉ๋ฒ•
      • ex) ์œ ์ €๊ฐ€ ๊ฒŒ์‹œ๊ธ€์„ ๋ณด๊ณ  ์‹ถ์„ ๋•Œ, HTML์„ ๋™์ ์œผ๋กœ ๋ฐ˜ํ™˜

GIT

  • 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๊ฐ€ ์žˆ๋Š”๋ฐ ์ด๋ฅผ ๋…ธ์ถœ์‹œํ‚ค์ง€ ์•Š๊ฒŒ ํ•˜๋Š” ์—ญํ• 

    • https://django-environ.readthedocs.io/en/latest/

    • ๋ฉ”์ธ ํ”„๋กœ์ ํŠธ ํด๋” ๋‚ด๋ถ€์— ํ•ด๋‹น ์ฝ”๋“œ ๋ณต์‚ฌ

      • ์ฃผ์˜์‚ฌํ•ญ : 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
    #์›ํ•˜๋Š” ๋งŒํผ ์ปค๋ฐ‹์„ ์—†์•จ ์ˆ˜ ์žˆ๋‹ค. 

HTML

  • ์žฅ๊ณ ์—์„œ๋Š” 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

    • https://docs.djangoproject.com/ko/4.0/howto/static-files/

    • ๋ฒ ์ด์Šค ๋””๋ ‰ํ† ๋ฆฌ๋Š” 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ํŒŒ์ผ์—์„œ ์ค€๋‹ค.

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
    • %
      • ๋ฐ”๋กœ ์œ„ ๋ถ€๋ชจ ์˜ํ–ฅ๋งŒ ๋ฐ›๋Š”๋‹ค

    0404_๋””์Šคํ”Œ๋ ˆ์ด์—ฐ์Šต

  • 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})

      0404_POST_TEST

    • 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})

    0404_obects.all()

    • ํŽ˜์ด์ง€๋ฅผ ์ƒˆ๋กœ๊ณ ์นจ ํ•  ๋•Œ ์•„๋ฌด๊ฒƒ๋„ ๋ˆ„๋ฅด์ง€ ์•Š์•˜๋Š”๋ฐ๋„, ๋งˆ์ง€๋ง‰์— ์ƒ์„ฑํ•œ ๊ฐ์ฒด๊ฐ€ ๊ณ„์† ๋ฐ˜๋ณต์ ์œผ๋กœ ์ƒ์„ฑ๋จ

      • why? views.py์—์„œ POST๊ฐ€ ์žˆ์„ ๋•Œ ๋ฐ˜๋ณต์ ์œผ๋กœ ์ €์žฅ์„ ํ•˜๊ธฐ ๋•Œ๋ฌธ์—
      • ํ•ด๊ฒฐ์„ ์œ„ํ•ด render๋ฅผ ํ•˜์ง€ ์•Š๊ณ  ๊ทธ๋ƒฅ ํŽ˜์ด์ง€๋งŒ ๋ฐ˜ํ™˜ํ•˜๊ฒŒ redirect๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค.
    • POST๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅ๋งŒ ์‹œํ‚ค๊ณ , POST๋ฅผ ๋ณด๋‚ด์ง€ ์•Š์„ ๋•Œ๋Š” ๊ทธ๋ƒฅ DB์˜ ์š”์†Œ๋งŒ ๋‚˜์—ดํ•˜๊ฒŒ ํ•œ๋‹ค...?

    • render ์™€ redirect ๊ตฌ๋ถ„

      ๋‘ ํ•จ์ˆ˜๋ฅผ ํ—ท๊ฐˆ๋ ค ํ˜ผ๋™ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค. ์ƒ๊ฐ ์™ธ๋กœ ๋‘˜์˜ ์ฐจ์ด๋Š” ๋ช…ํ™•ํ•ฉ๋‹ˆ๋‹ค. render ๋Š” ํ…œํ”Œ๋ฆฟ์„ ๋ถˆ๋Ÿฌ์˜ค๊ณ , redirect ๋Š” URL๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค. URL ๋กœ ์ด๋™ํ•œ๋‹ค๋Š” ๊ฑด ๊ทธ URL ์— ๋งž๋Š” views ๊ฐ€ ๋‹ค์‹œ ์‹คํ–‰๋ ํ…Œ๊ณ  ์—ฌ๊ธฐ์„œ render ๋ฅผ ํ• ์ง€ ๋‹ค์‹œ redirect ํ• ์ง€ ๊ฒฐ์ •ํ•  ๊ฒƒ ์ž…๋‹ˆ๋‹ค. ์ด ์ ์— ์œ ์˜ํ•ด์„œ ์‚ฌ์šฉํ•˜์‹ ๋‹ค๋ฉด ์ƒํ™ฉ์— ๋งž๊ฒŒ ์‚ฌ์šฉํ•˜์‹ค ์ˆ˜ ์žˆ์„ ๊ฒ๋‹ˆ๋‹ค.

Form

  • 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>

0405_django_account_form

  • Login view

    0405_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๋ฅผ ์„ค์ •ํ•œ๋‹ค.

Render

  • html ํŒŒ์ผ์„ ๋ธŒ๋ผ์šฐ์ €์—๊ฒŒ ๋ณด์—ฌ์คŒ

Redirect

  • ๋‹ค๋ฅธ url๋กœ ์ด๋™ํ•ด์ค˜ ๊ทธ๋Ÿผ ๋‹ค๋ฅธ url์ด view์— ์š”์ฒญ์„ ๋ณด๋‚ด ๊ทธ๋Ÿผ url์ด render๋กœ ๋‹ค์‹œ ๋ณด๋‚ด์ค„๊ฑฐ์•ผ
  • ๋งŒ์•ฝ html์„ ์ง์ ‘ ๋ Œ๋”๋งํ•˜์ง€ ์•Š๋Š” create๋‚˜ delete ํ•จ์ˆ˜๋ฅผ views.py์—์„œ ์‚ฌ์šฉํ• ๋•Œ๋Š” redirect๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค.
  • render๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ณด์—ฌ์ค„ html์ด ์—†๊ธฐ๋„ ํ•˜๊ณ , html์„ ์ง์ ‘ ๋ Œ๋”ํ•œ๋‹ค๋ฉด url์ด ๊ผฌ์ž„

Bootstrap

  • Django์—์„œ ๋ถ€ํŠธ์ŠคํŠธ๋žฉ์„ ์„ค์น˜ํ•˜๋ ค๋ฉด ๋จผ์ € pip install์„ ํ•ด์ค€๋‹ค.
pip install django-bootstrap4
  • Seetings.py์— ๋“ฑ๋กํ•ด์ค€๋‹ค.
  • ์“ฐ๊ณ  ์‹ถ์€ html์— ๋กœ๋“œํ•ด์ค€๋‹ค. (์Šคํƒœํ‹ฑ์ฒ˜๋Ÿผ)
{% load bootstrap4 %}
  • form์— ์‚ฌ์šฉํ•˜๋ ค๋ฉด
{% bootstrap_form form %}

CSS ์Šคํƒ€์ผ ๋ณ€๊ฒฝ์ด ์ฆ‰์‹œ ์ž‘์šฉ๋˜์ง€ ์•Š์„ ๋•Œ

  • ๋ฐฉ๋ฒ• 1. ๋ธŒ๋ผ์šฐ์ € ์บ์‹œ ์‚ญ์ œ

    • ๊ฒฝ๋กœ : ํฌ๋กฌ > ์„ค์ • > ๊ฐœ์ธ์ •๋ณด ๋ฐ ๋ณด์•ˆ > ์ฟ ํ‚ค ๋ฐ ๊ธฐํƒ€ ์‚ฌ์ดํŠธ ๋ฐ์ดํ„ฐ > ๋ชจ๋“  ์ฟ ํ‚ค ๋ฐ ์‚ฌ์ดํŠธ ๋ฐ์ดํ„ฐ ๋ณด๊ธฐ > ๋ชจ๋‘ ์‚ญ์ œ
  • ๋ฐฉ๋ฒ• 2. ๋งํฌ ์ฝ”๋“œ์˜ URL ๋ณ€๊ฒฝ

    • CSS ํŒŒ์ผ์„ ๋งํฌํ•˜๋Š” HTML ํŒŒ์ผ(PHP, JSP)์„ ์—ด์–ด ๊ธฐ์กด CSS ํŒŒ์ผ์˜ URL ๋’ค์— ?after๋ฅผ ๋ถ™์ด๊ธฐ
  • **๋ฐฉ๋ฒ• 3. ์ƒˆ๋กœ๊ณ ์นจ **

    • ctrl + shift + F5
  • ** ๋ฐฉ๋ฒ•4. ์บ์‹ฑ ์‚ฌ์šฉ ์ค‘์ง€ **

    • ๊ฐœ๋ฐœ์ž๋„๊ตฌ -> ๋„คํŠธ์›Œํฌ -> ์บ์‹ฑ ์‚ฌ์šฉ ์ค‘์ง€

Detail view

  • ์žฅ๊ณ ์—์„œ๋Š” 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

    • ๋ทฐ์—์„œ ํ…œํ”Œ๋ฆฟ ํŒŒ์ผ์— ์ „๋‹ฌํ•˜๋Š” ์ปจํ…์ŠคํŠธ ๋ณ€์ˆ˜๋ช…์„ ์ง€์ •ํ•œ๋‹ค.

      0406_mypage

Update View

  • ๊ธฐ๋ณธ์ ์œผ๋กœ ํšŒ์›์ •๋ณด ์ˆ˜์ •์ด๊ธฐ ๋•Œ๋ฌธ์— 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๋„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋‚˜ํƒ€๋‚จ

0406_updateview

  • ์ผ๋ฐ˜์ ์œผ๋กœ 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 

0406_updateview2

Delete View

  • 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>

0406_deleteview

git reset ์ทจ์†Œ

  • ์˜ค๋Š˜ ๋ฆฌ์…‹ ์ž˜๋ชปํ–ˆ๋‹ค๊ฐ€ ์š”๋‹จ๊ฐ• ๊ฑด๋„ ๋ป”ํ•จ
    • ์ด์œ  ์ปค๋ฐ‹ ํ•œ ๋ฒˆ์— ํ• ๋ ค๊ณ ;

๋จผ์ € reset๋ถ€ํ„ฐ

reset

git log --oneline

ํ•˜๋ฉด ์ •๋ณด๊ฐ€ ๋‚˜์˜จ๋‹ค.

gitlog

์—ฌ๊ธฐ์„œ ์œ„์—์„œ 2๋ฒˆ์งธ ์ปค๋ฐ‹์„ ์ทจ์†Œํ•˜๊ณ  ์‹ถ์œผ๋ฉด ์ด๋ ‡๊ฒŒ ์น˜๋ฉด๋œ๋‹ค.

git reset --hard 2a4978e

์ฃผ์˜์‚ฌํ•ญ

  • ์ปค๋ฐ‹์„ ์ทจ์†Œํ•˜๋ฉด ๊ทธ ๋•Œ๊นŒ์ง€ ํƒ€์ดํ•‘ ํ–ˆ๋˜ ๊ฒƒ๋“ค๋„ ์ปค๋ฐ‹ ์ด์ „์œผ๋กœ ๋‹ค ํŒŒ์ผ์ด ๋Œ์•„๊ฐ„๋‹ค.

์ทจ์†Œ

$git reflog 

gitreflog

์ด๋ ‡๊ฒŒ ๋‚˜์˜ค๋Š”๋ฐ, ๋‚˜๋Š” HEAD{3}์—์„œ ๋ฆฌ์…‹์„ ์ž˜๋ชปํ•ด์„œ ํŒŒ์ผ์ด ๋‹ค ๋‚ ๋ผ๊ฐ€์„œ HEAD 4๋กœ ๊ฐ”์–ด์•ผํ–ˆ๋‹ค.

๊ทธ๋Ÿฌ๋ฉด

git reset --hard HEAD@{4}

์ด๋ ‡๊ฒŒ ์น˜๋ฉด ๋œ๋‹ค.

๋งŒ์•ฝ push ๊นŒ์ง€ ํ•ด๋ฒ„๋ ธ๋‹ค๋ฉด, push ํ•  ๋•Œ

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

  • ํŒŒ์ด์ฌ์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ, ๊ฐ€๋…์„ฑ์„ ํ•ด์น˜๋Š” ๊ฒƒ์„ ๋ง‰๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•จ @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')
Profile Setting
  • superuser ๋งŒ๋“ค๊ธฐ

    python manage.py cratesuperuser

    ID, ๋น„๋ฐ€๋ฒˆํ˜ธ ์ž…๋ ฅ

Media ์„ธํŒ…

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

Post ์‚ญ์ œ

  • ๊ธ€์ด ๋„ˆ๋ฌด ๋งŽ์•„์„œ ๋‹ต๋‹ตํ•ด์„œ 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 %}

Profile app

  • detail.html์— ๋“ค์–ด๊ฐ€๋ฉด ID๊ฐ€ ๊ทธ๋Œ€๋กœ ๋…ธ์ถœ์ด๋œ๋‹ค. ์ด๋Š” ๋ณด์•ˆ์— ๋ฌธ์ œ๊ฐ€ ๋ ์ˆ˜๋„ ์žˆ์œผ๋ฏ€๋กœ, ๋‹‰๋„ค์ž„์œผ๋กœ ํ‘œ์‹œ๋˜๊ฒŒ ํ•œ๋‹ค.
  • ํ”„๋กœํ•„ ์‚ฌ์ง„ - ๋‹‰๋„ค์ž„ - ๋ฉ”์‹œ์ง€ ๊ฐ€ ํฌํ•จ๋œ ํ”„๋กœํ•„์„ ๋งŒ๋“ค์–ด๋ณด์ž ! !
  • Account์™€ Profile์„ 1๋Œ€1 ๋งค์นญ ์‹œ์ผœ์„œ ๊ณ„์ • ํ•˜๋‚˜์— ํ•œ๊ฐœ์˜ ํ”„๋กœํ•„๋งŒ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์„ฑํ•  ๊ฒƒ์ด๋‹ค.

Model

  • 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)

ProfileUpdateView

@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'

get_success_url function and code refactoring

  • ํ”„๋กœํ•„ ์ƒ์„ฑ ๋ฐ ๋ณ€๊ฒฝ์‹œ ๋ฉ”์ธํŽ˜์ด์ง€๊ฐ€ ์•„๋‹Œ 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})

Articleapp ๋งŒ๋“ค๊ธฐ

  • ์นด๋“œํ˜•์œผ๋กœ ๊ฒŒ์‹œ๊ธ€ ์ฐฝ์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด js์˜ magic_grid๊ฐ€ ํ•„์š”ํ•จ
  1. html ๋งŒ๋“ค๊ธฐ
  2. 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>
  1. 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>
  1. static ํด๋” ๋‚ด๋ถ€์— jsํด๋” ์ƒ์„ฑ, magicgrid.js์ƒ์„ฑ ํ›„ magicgrid ์•„๋ž˜ url์—์„œ ๊ฐ€์ ธ์˜ค๊ณ  ๋ถ™์—ฌ๋„ฃ๊ธฐ

https://github.com/e-oj/Magic-Grid/blob/master/dist/magic-grid.cjs.js

  1. 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();
  1. ์‚ฌ์ง„์„ ์ผ๋‹จ ๋žœ๋ค์œผ๋กœ ๋ฐฐ์น˜ํ•œ๋‹ค.
<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)

LIst view

class ArticleListView(ListView) :
    #์–ด๋–ค ๋ชจ๋ธ ?
    model = Article
    #์–ด๋˜ ํ…œํ”Œ๋ฆฟ ์ด๋ฆ„์„ ์“ธ๊ฑด์ง€ 
    context_object_name = 'article_list'
    template_name = 'articleapp/list.html'
    #ํ•œ ํŽ˜์ด์ง€์— ๋ช‡๊ฐœ์˜ ๊ฐ์ฒด๋ฅผ ๋ณด์—ฌ์ค„ ๊ฒƒ์ธ์ง€ 
    paginate_by = 25

Pagination

  • Generate Page of objects

    • ํŽ˜์ด์ง€์— ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค
    • ๊ตฌ๊ธ€์—์„œ ๊ฒ€์ƒ‰์„ ํ•  ๋•Œ ํ•˜๋‹จ์— ํŽ˜์ด์ง€๊ฐ€ ๋ณด์ด๋Š” ๊ฒƒ์„ ํŽ˜์ด์ง€๋„ค์ด์…˜์ด๋ผ๊ณ  ํ•œ๋‹ค.
  • Infiniite Scroll

    • ํŽ˜์ด์Šค๋ถ์ด๋‚˜ ์ธ์Šคํƒ€๊ทธ๋žจ์—์„œ๋Š” ๋ฌดํ•œ์œผ๋กœ ์•„๋ž˜๋กœ ๋‚ด๋ฆด๋•Œ ๊ฒŒ์‹œ๊ธ€๋“ค์ด ๋‚˜์˜จ๋‹ค.
  • page_obj๋ฅผ ํ…œํ”Œ๋ฆฟ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

articlelist

  • Article_LIst๋Š” ๊ฒŒ์‹œ๊ธ€์˜ ๋ฆฌ์ŠคํŠธ
    • ์ด ์•ˆ์— ๊ฐ์ฒด์˜ ๊ฐ๊ฐ ์ •๋ณด๊ฐ€ ๋‹ด๊ฒจ์žˆ๊ณ , ์ด ์ •๋ณด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ HTML์— ํ•€ํ„ฐ๋ ˆ์ŠคํŠธ์˜ ์นด๋“œํ˜• ๋ ˆ์ด์•„์›ƒ์„ ๊ฐ€์ ธ์˜ค๋ฉด ๋ฟŒ๋ ค์ฃผ๋Š” ์—ญํ• ์„ ํ•˜๋Š” ๊พธ๋Ÿฌ๋ฏธ์ด๋‹ค.
  • Page_obj
    • ํ•˜๋‹จ์— ํŽ˜์ด์ง€๋ฅผ ๋งŒ๋“ค ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ์—ญํ• 

๊ตฌํ˜„ ๊ฒฐ๊ณผ

0420_artilce_list

ํŽ˜์ด์ง€๋„ค์ด์…˜ ์ถ”๊ฐ€

list

Detail ์ˆ˜์ •

0420

Mixin

  • 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'

Comment View ๊ตฌ์กฐ

  • 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 ์ฃผ์†Œ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ ‘์†ํ•  ์ˆ˜ ์žˆ๋‹ค.

Connect to WIFI local network server IP

  • ์™€์ดํŒŒ์ด๊ฐ€ ๊ฐ™์ด ์—ฐ๊ฒฐ๋˜์–ด ์žˆ์œผ๋ฉด ์„ค์ • ๊ฐ€๋Šฅํ•˜๋‹ค.

  • django runserver๋ฅผ 0.0.0.0:8000์œผ๋กœ ๊ตฌ๋™ํ•˜๊ธฐ

$ python manage.py runserver 0.0.0.0:8000

ALLOWED_HOST

  • settings.py์กฐ์ •ํ•˜๊ธฐ
ALLOWED_HOSTS = ['*']
  • '*'๋ฅผ ๋„ฃ์–ด์ฃผ๋ฉด ๋ชจ๋“  ํ˜ธ์ŠคํŠธ๋ฅผ ํ—ˆ์šฉํ•œ๋‹ค๋Š” ๋œป
  • ๋ฐฐํฌ์‹œ์—๋Š” ์‹ ๊ฒฝ์“ธ ๊ฒƒ !

Mobile ์—ฐ๊ฒฐ

cmd์ฐฝ์—์„œ ipconfig๋ฅผ ์ž…๋ ฅํ•œ๋‹ค.

C:\Users\SAMSUNG>ipconfig

ipconfig

  • ์ด๋Ÿฐ์‹์œผ๋กœ ๋œจ๋Š”๋ฐ, IPv4 ์ฃผ์†Œ๋ฅผ mobile์—์„œ ์ž…๋ ฅํ•ด์ฃผ๋ฉด ๋œ๋‹ค.
000.000.00.00:8000/articles/list/

mobile2mobile1

WYSIWYG

  • What You See Is What You Get(๋ณด๋Š”๋Œ€๋กœ ๊ธ€์ด ์จ์ง„๋‹ค!)
  • ํ˜„์žฌ๊นŒ์ง€๋Š” ๊ธ€์„ ์“ฐ๋ฉด ์ผ๋ฐ˜์ ์ธ ํ…์ŠคํŠธ๋กœ ์ด๋ฃจ์–ด์ ธ์žˆ๋Š”๋ฐ, ์ด๋ฅผ ๋” ๊ตต๊ฒŒ๋„ ํ•˜๊ณ , ํฌ๊ฒŒ๋„ ํ•˜๊ณ , ๋ฐ‘์ค„๋„ ๊ธ‹๋Š” ๊ฒƒ

Medium editer

https://github.com/yabwe/medium-editor

custom version

<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">

editable์„ ์ด์šฉํ•ด editor๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

<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']

editor script ์ ์šฉ

{% 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 %}

์ ์šฉ ๊ฒฐ๊ณผ

์ ์šฉ๊ฒฐ๊ณผ2์ ์šฉ๊ฒฐ๊ณผ

๋ฌธ์ œ ๋ฐœ์ƒ

  • Detail ํŽ˜์ด์ง€์—์„œ๋Š” ํƒœ๊ทธ๊ฐ€ ๋ณด์ž„

error

ํ•ด๊ฒฐ๋ฐฉ๋ฒ•

  • Django HTML์— safe ํƒœ๊ทธ ์ถ”๊ฐ€
{{ target_article.content | safe }}

ํ•ด๊ฒฐ

Material Design

Material Design

https://material.io/design

ํ•ด๋‹น ํŽ˜์ด์ง€์—์„œ Icon์„ ๊ฐ€์ ธ์˜ค๋ ค๋ฉด HTML์—์„œ ํด๋ž˜์Šค์™€ ์ด๋ฆ„์„ ์ž…๋ ฅํ•ด์ฃผ๋ฉด ๊ฐ„๋‹จํ•˜๋‹ค.

github

https://github.com/google/material-design-icons

Using a font

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.

Edit

์ ์šฉ ์ „

์ ์šฉ์ „

        {% if target_user == user %}
        <a class="material-icons" href="{% url 'profileapp:update' pk=target_user.profile.pk %}">
          edit
        </a>
        {% endif %}

์ ์šฉ ํ›„

edit

        {% if target_user == user %}
        <a class="material-icons" href="{% url 'profileapp:update' pk=target_user.profile.pk %}">
          edit
        </a>
        {% endif %}

Change Info, Quit

์ ์šฉ ์ „

์ ์šฉ์ „2

      <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>