diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..be32386 --- /dev/null +++ b/.gitignore @@ -0,0 +1,112 @@ +# Created by https://www.toptal.com/developers/gitignore/api/pycharm +# Edit at https://www.toptal.com/developers/gitignore?templates=pycharm + +### PyCharm ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +.idea + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### PyCharm Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +# End of https://www.toptal.com/developers/gitignore/api/pycharm \ No newline at end of file diff --git a/README.md b/README.md index 85dad61..879dbf7 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,22 @@ # Start Django ✨ [Django Documentation Tutorial](https://docs.djangoproject.com/ko/3.2/intro/)을 따라가며 코드 및 배운 내용을 기록합니다. - +- **기간** : 2021.08.23 ~ 2021.08.27 +- **목적** : Django 프레임 워크 기본 기능 익히기 +- **순서** + 1. Django 공식 문서를 참고하여 실습 진행 + 2. 배운 내용 문서로 정리 + 3. 매일 아침 8:40 스크럼 회의 + 4. 피드백 반영하여 코드 및 문서 수정 ## Django > Django makes it easier to build better Web apps more quickly and with less code. -- **Development Environment** - - Python 3.9.6 - - Django 3.2.6 +**Development Environment** +- Python 3.9.6 +- Django 3.2.6 diff --git a/docs/02-database_and_admin.md b/docs/02-database_and_admin.md new file mode 100644 index 0000000..24e06b9 --- /dev/null +++ b/docs/02-database_and_admin.md @@ -0,0 +1,288 @@ +# 02. Database and Admin + +데이터 베이스 설치 및 관리자 사이트 생성 + +## 데이터베이스 설치 + +- django는 기본적으로 SQLite : `mysite/settings.py` + ```python + DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': BASE_DIR / 'db.sqlite3', + } + } + ``` +- 다른 데이터베이스 사용 시 [공식 문서](https://docs.djangoproject.com/ko/3.2/ref/settings/#std:setting-DATABASES) 참고 + ```python + DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': 'mydatabase', + 'USER': 'mydatabaseuser', + 'PASSWORD': 'mypassword', + 'HOST': '127.0.0.1', + 'PORT': '5432', + } + } + ``` + +- `TIME_ZONE` 설정 + ```python + TIME_ZONE = 'Asia/Seoul' + ``` + +<br> + +- `INSTALLED_APPS` 확인 + ```python + INSTALLED_APPS = [ + 'django.contrib.admin', # 관리용 사이트 + 'django.contrib.auth', # 인증 시스템 + 'django.contrib.contenttypes', # 컨텐츠 타입을 위한 프레임워크 + 'django.contrib.sessions', # 세션 프레임워크 + 'django.contrib.messages', # 메세징 프레임워크 + 'django.contrib.staticfiles', # 정적 파일을 관리하는 프레임워크 + ] + ``` + +- 데이터베이스 설치 (`INSTALLED_APPS`에 대해서만) + ```bash + $ python manage.py migrate + ``` + +<br> + +## 모델 만들기 + +- **모델** : 부가적인 메타데이터를 가진 데이터베이스의 구조 (layout) +- 우리의 `polls`앱에서는 `Question` 과 `Choice`라는 두 가지 모델을 생성할 것. + +<br> + +- `polls/models.py` + ```python + from django.db import models + + + class Question(models.Model): + question_text = models.CharField(max_length=200) + pub_date = models.DateTimeField('date Published') + + class Choice(models.Model): + question = models.ForeignKey(Question, on_delete=models.CASCADE) + choice_text = models.CharField(max_length=200) + votes = models.IntegerField(default=0) + ``` + +- 각 데이터베이스 필드를 `Field` 클래스의 인스턴스로 표현 +- `ForeignKey()` : 외래키 참조 + +<br> + +- `INSTALLED_APPS`에 `polls.apps.PollsConfig` 추가 +```bash +$ python manage.py makemigrations polls +Migrations for 'polls': + polls\migrations\0001_initial.py + - Create model Question + - Create model Choice +``` + +- `polls/migrations/0001_initial.py` 파일 생성 확인 +- SQL 명령어 확인 + - app 이름과 모델명을 이용해 테이블 이름 자동 생성됨. + - 필드명, 필드 타입 관례에 따라 자동 생성 +```bash +$ python manage.py sqlmigrate polls 0001 +BEGIN; +-- +-- Create model Question +-- +CREATE TABLE "polls_question" ( + "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, + "question_text" varchar(200) NOT NULL, + "pub_date" datetime NOT NULL); +-- +-- Create model Choice +-- +CREATE TABLE "polls_choice" ( + "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, + "choice_text" varchar(200) NOT NULL, + "votes" integer NOT NULL, + "question_id" bigint NOT NULL REFERENCES + "polls_question" ("id") DEFERRABLE INITIALLY DEFERRED); +CREATE INDEX "polls_choice_question_id_c5b4b260" ON "polls_choice" ("question_id"); +COMMIT; +``` + +- 데이터베이스 모델 관련 테이블 생성하기 (변경사항 데이터베이스에 적용) +```bash +$ python manage.py migrate +Operations to perform: + Apply all migrations: admin, auth, contenttypes, polls, sessions +Running migrations: + Applying polls.0001_initial... OK +``` + +<br> + +## 데이터베이스 API 사용하기 + +```bash +$ python manage.py shell +Python 3.9.6 (tags/v3.9.6:db3ff76, Jun 28 2021, 15:26:21) [MSC v.1929 64 bit (AMD64)] +Type 'copyright', 'credits' or 'license' for more information +IPython 7.25.0 -- An enhanced Interactive Python. Type '?' for help. +``` +```python +In [1]: from polls.models import Choice, Question + +In [2]: Question.objects.all() +Out[2]: <QuerySet []> + +In [3]: from django.utils import timezone + +In [4]: q = Question(question_text="What's new?", pub_date=timezone.now()) + +In [5]: q.save() + +In [6]: q.id +Out[6]: 1 + +In [7]: q.question_text +Out[7]: "What's new?" + +In [8]: q.pub_date +Out[8]: datetime.datetime(2021, 8, 24, 8, 12, 43, 552040, tzinfo=<UTC>) + +In [9]: q.question_text = "What's up?" + +In [10]: q.save() + +In [11]: Question.objects.all() +Out[11]: <QuerySet [<Question: Question object (1)>]> +``` + +- timezone을 `Asia/Seoul`로 설정했음에도 `UTC`로 표시됨 발견 +- `settings.py`에서 `USE_TZ = False`로 변경 → 해결됨 + +```python +In [8]: q.pub_date +Out[8]: datetime.datetime(2021, 8, 24, 17, 49, 58, 504533) +``` + +- 객체 표현법 변경 +```python +# polls/models.py + +# Question +def __str__(self): + return self.question_text + +# Choice +def __str__(self): + return self.choice_text +``` +- 커스텀 메서드 추가 +```python +def was_published_recently(self): + return self.pub_date >= timezone.now() - datetime.timedelta(days=1) +``` + +<br> + +- 커스텀 메서드 확인 및 api 다루기 +- 이중 밑줄 (`__`)을 이용한 필드 조회 + +```bash +$ python manage.py shell +Python 3.9.6 (tags/v3.9.6:db3ff76, Jun 28 2021, 15:26:21) [MSC v.1929 64 bit (AMD64)] +Type 'copyright', 'credits' or 'license' for more information +IPython 7.25.0 -- An enhanced Interactive Python. Type '?' for help. +``` +```python +In [1]: from polls.models import Choice, Question + +In [2]: Question.objects.all() +Out[2]: <QuerySet [<Question: What's up?>]> + +In [3]: Question.objects.filter(id=1) +Out[3]: <QuerySet [<Question: What's up?>]> + +In [4]: Question.objects.filter(question_text__startswith='What') +Out[4]: <QuerySet [<Question: What's up?>]> + +In [5]: from django.utils import timezone + +In [6]: current_year = timezone.now().year + +In [7]: Question.objects.get(pub_date__year=current_year) +Out[7]: <Question: What's up?> + +In [8]: Question.objects.get(pk=1) # primary key +Out[8]: <Question: What's up?> + +In [9]: q = Question.objects.get(pk=1) + +In [10]: q.was_published_recently() +Out[10]: True +``` + +- choice 추가하기 +```python +In [11]: q.choice_set.all() +Out[11]: <QuerySet []> + +In [12]: q.choice_set.create(choice_text='Not much', votes=0) +Out[12]: <Choice: Not much> + +In [13]: q.choice_set.create(choice_text='The sky', votes=0) +Out[13]: <Choice: The sky> + +In [14]: c = q.choice_set.create(choice_text='Just hacking again', votes=0) + +In [15]: c.question +Out[15]: <Question: What's up?> + +In [16]: q.choice_set.all() +Out[16]: <QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]> + +In [17]: q.choice_set.count() +Out[17]: 3 + +In [18]: Choice.objects.filter(question__pub_date__year=current_year) +Out[18]: <QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]> + +In [19]: c = q.choice_set.filter(choice_text__startswith='Just hacking') + +In [20]: c.delete() +Out[20]: (1, {'polls.Choice': 1}) +``` + +## 관리자 생성하기 +```bash +$ python manage.py createsuperuser +Username (leave blank to use 'user'): admin +Email address: chaeyeonhee@kakao.com +Password: +Password (again): +Superuser created successfully. +``` + +<br> + +- 개발 서버 : http://127.0.0.1:8000/admin/ +```bash +- $ python manage.py runserver +Watching for file changes with StatReloader +Performing system checks... + +System check identified no issues (0 silenced). +August 24, 2021 - 19:56:57 +Django version 3.2.6, using settings 'mysite.settings' +Starting development server at http://127.0.0.1:8000/ +Quit the server with CTRL-BREAK. +[24/Aug/2021 19:57:21] "GET /admin/ HTTP/1.1" 302 0 +... +``` diff --git a/mysite/db.sqlite3 b/mysite/db.sqlite3 index e69de29..0ab4cac 100644 Binary files a/mysite/db.sqlite3 and b/mysite/db.sqlite3 differ diff --git a/mysite/mysite/__pycache__/__init__.cpython-39.pyc b/mysite/mysite/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000..16ffc23 Binary files /dev/null and b/mysite/mysite/__pycache__/__init__.cpython-39.pyc differ diff --git a/mysite/mysite/__pycache__/settings.cpython-39.pyc b/mysite/mysite/__pycache__/settings.cpython-39.pyc new file mode 100644 index 0000000..e27fdc2 Binary files /dev/null and b/mysite/mysite/__pycache__/settings.cpython-39.pyc differ diff --git a/mysite/mysite/__pycache__/urls.cpython-39.pyc b/mysite/mysite/__pycache__/urls.cpython-39.pyc new file mode 100644 index 0000000..0152486 Binary files /dev/null and b/mysite/mysite/__pycache__/urls.cpython-39.pyc differ diff --git a/mysite/mysite/__pycache__/wsgi.cpython-39.pyc b/mysite/mysite/__pycache__/wsgi.cpython-39.pyc new file mode 100644 index 0000000..c84bdf4 Binary files /dev/null and b/mysite/mysite/__pycache__/wsgi.cpython-39.pyc differ diff --git a/mysite/mysite/settings.py b/mysite/mysite/settings.py index 6ebe44d..53ea25f 100644 --- a/mysite/mysite/settings.py +++ b/mysite/mysite/settings.py @@ -31,6 +31,7 @@ # Application definition INSTALLED_APPS = [ + 'polls.apps.PollsConfig', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', @@ -105,13 +106,13 @@ LANGUAGE_CODE = 'en-us' -TIME_ZONE = 'UTC' +TIME_ZONE = 'Asia/Seoul' USE_I18N = True USE_L10N = True -USE_TZ = True +USE_TZ = False # Static files (CSS, JavaScript, Images) diff --git a/mysite/mysite/urls.py b/mysite/mysite/urls.py index 4552108..0698963 100644 --- a/mysite/mysite/urls.py +++ b/mysite/mysite/urls.py @@ -14,8 +14,10 @@ 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.contrib import admin -from django.urls import path +from django.urls import path, include urlpatterns = [ + # route : url 패턴 + path('polls/', include('polls.urls')), # 다른 url 패턴을 포함할 때 include 함수 사용 path('admin/', admin.site.urls), ] diff --git a/mysite/polls/__pycache__/__init__.cpython-39.pyc b/mysite/polls/__pycache__/__init__.cpython-39.pyc index f7a8766..e892ef2 100644 Binary files a/mysite/polls/__pycache__/__init__.cpython-39.pyc and b/mysite/polls/__pycache__/__init__.cpython-39.pyc differ diff --git a/mysite/polls/__pycache__/admin.cpython-39.pyc b/mysite/polls/__pycache__/admin.cpython-39.pyc new file mode 100644 index 0000000..7122fd3 Binary files /dev/null and b/mysite/polls/__pycache__/admin.cpython-39.pyc differ diff --git a/mysite/polls/__pycache__/apps.cpython-39.pyc b/mysite/polls/__pycache__/apps.cpython-39.pyc new file mode 100644 index 0000000..e252dd3 Binary files /dev/null and b/mysite/polls/__pycache__/apps.cpython-39.pyc differ diff --git a/mysite/polls/__pycache__/models.cpython-39.pyc b/mysite/polls/__pycache__/models.cpython-39.pyc new file mode 100644 index 0000000..6a76737 Binary files /dev/null and b/mysite/polls/__pycache__/models.cpython-39.pyc differ diff --git a/mysite/polls/__pycache__/urls.cpython-39.pyc b/mysite/polls/__pycache__/urls.cpython-39.pyc index 225b681..513f7ee 100644 Binary files a/mysite/polls/__pycache__/urls.cpython-39.pyc and b/mysite/polls/__pycache__/urls.cpython-39.pyc differ diff --git a/mysite/polls/__pycache__/views.cpython-39.pyc b/mysite/polls/__pycache__/views.cpython-39.pyc index 87e4b42..0e63f11 100644 Binary files a/mysite/polls/__pycache__/views.cpython-39.pyc and b/mysite/polls/__pycache__/views.cpython-39.pyc differ diff --git a/mysite/polls/admin.py b/mysite/polls/admin.py index 8c38f3f..aa3fdc6 100644 --- a/mysite/polls/admin.py +++ b/mysite/polls/admin.py @@ -1,3 +1,4 @@ from django.contrib import admin +from .models import Question -# Register your models here. +admin.site.register(Question) \ No newline at end of file diff --git a/mysite/polls/migrations/0001_initial.py b/mysite/polls/migrations/0001_initial.py new file mode 100644 index 0000000..18ab3b2 --- /dev/null +++ b/mysite/polls/migrations/0001_initial.py @@ -0,0 +1,32 @@ +# Generated by Django 3.2.6 on 2021-08-24 17:48 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Question', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('question_text', models.CharField(max_length=200)), + ('pub_date', models.DateTimeField(verbose_name='date Published')), + ], + ), + migrations.CreateModel( + name='Choice', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('choice_text', models.CharField(max_length=200)), + ('votes', models.IntegerField(default=0)), + ('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='polls.question')), + ], + ), + ] diff --git a/mysite/polls/migrations/__pycache__/0001_initial.cpython-39.pyc b/mysite/polls/migrations/__pycache__/0001_initial.cpython-39.pyc new file mode 100644 index 0000000..a9c4a00 Binary files /dev/null and b/mysite/polls/migrations/__pycache__/0001_initial.cpython-39.pyc differ diff --git a/mysite/polls/migrations/__pycache__/__init__.cpython-39.pyc b/mysite/polls/migrations/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000..8eb54ec Binary files /dev/null and b/mysite/polls/migrations/__pycache__/__init__.cpython-39.pyc differ diff --git a/mysite/polls/models.py b/mysite/polls/models.py index 71a8362..30ea737 100644 --- a/mysite/polls/models.py +++ b/mysite/polls/models.py @@ -1,3 +1,24 @@ +import datetime + from django.db import models +from django.utils import timezone + + +class Question(models.Model): + question_text = models.CharField(max_length=200) + pub_date = models.DateTimeField('date Published') + + def __str__(self): + return self.question_text + + def was_published_recently(self): + return self.pub_date >= timezone.now() - datetime.timedelta(days=1) + + +class Choice(models.Model): + question = models.ForeignKey(Question, on_delete=models.CASCADE) + choice_text = models.CharField(max_length=200) + votes = models.IntegerField(default=0) -# Create your models here. + def __str__(self): + return self.choice_text