原文:Writing your first Django app, part 2
本文Python搭建在 Django Compose + Djang 執行Python需進入web server容器中,請參看[第一步:在Mac構建Django 容器]
翻譯整理:CKpython
已有建有的項目爲mysite,Docker Django 的教程裏創建了一個composeexample,這裏不適用它。假設咱們的真實項目爲mysite,打開myiste/settings.py,默認配置的數據庫是SQLite,SQLite包含在Python裏,無需安裝任何其餘東西。若是第一次建立真實的項目,仍然但願用可伸縮性更強的數據庫像PostgreSQL,省得未來又要切換數據庫則:web
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'postgres', 'USER': 'postgres', 'HOST': 'db', 'PORT': 5432, } }
其餘數據庫綁定參考:Database Bindingssql
因爲使用了Docker容器來運行python,數據遷移$ python manage.py migrate
也須要在容器裏作,因此先查看容器docker
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES bf1ef3b74f1b mysite_web "python3 manage.py..." About an hour ago Up 13 minutes 0.0.0.0:8000->8000/tcp mysite_web_1 09f6cebc8741 postgres "docker-entrypoint..." 2 hours ago Up 13 minutes 5432/tcp mysite_db_1
進入容器並執行migrate:shell
docker exec -it mysite_db_1 bash $ python manage.py migrate
這條命令根據myiste/settings.py裏的 INSTALLED_APPS 的須要安裝必要數據庫表格,安裝完成後再鏈接PostgreSQL psql DBNAME USERNAME
數據庫
root@09f6cebc8741:/# psql postgres postgres
輸入\dt
應該能看到相似Tablesdjango
postgres=# \dt List of relations Schema | Name | Type | Owner --------+----------------------------+-------+---------- public | auth_group | table | postgres public | auth_group_permissions | table | postgres public | auth_permission | table | postgres public | auth_user | table | postgres public | auth_user_groups | table | postgres public | auth_user_user_permissions | table | postgres public | django_admin_log | table | postgres public | django_content_type | table | postgres public | django_migrations | table | postgres public | django_session | table | postgres (10 rows)
退出Postgres後端
postgres-# \q
編輯polls/models.py
bash
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)
通知Prject,polls app 應用已安裝服務器
編輯mysite/settings.py,添加一行`'polls.apps.PollsConfig',`
INSTALLED_APPS = [ 'polls.apps.PollsConfig', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ]
$ python manage.py makemigrations polls
makemigrations 告訴Django你對模型作了些修改,並並但願保存下來。Django會建立修改腳本polls/migrations/0001_initial.py,
應該會看到相似:
Migrations for 'polls': polls/migrations/0001_initial.py: - Create model Choice - Create model Question - Add field question to choice
到此數據庫並未改變,真正執行變動而且幫助你管理數據庫模式是migration
命令,能夠用以下命令查看將要執行的SQL命令
$ python manage.py sqlmigrate polls 0001
應該會看到相似(不一樣數據庫會有所不一樣):
BEGIN; -- -- Create model Choice -- CREATE TABLE "polls_choice" ("id" serial NOT NULL PRIMARY KEY, "choice_text" varchar(200) NOT NULL, "votes" integer NOT NULL); -- -- Create model Question -- CREATE TABLE "polls_question" ("id" serial NOT NULL PRIMARY KEY, "question_text" varchar(200) NOT NULL, "pub_date" timestamp with time zone NOT NULL); -- -- Add field question to choice -- ALTER TABLE "polls_choice" ADD COLUMN "question_id" integer NOT NULL; CREATE INDEX "polls_choice_question_id_c5b4b260" ON "polls_choice" ("question_id"); ALTER TABLE "polls_choice" ADD CONSTRAINT "polls_choice_question_id_c5b4b260_fk_polls_question_id" FOREIGN KEY ("question_id") REFERENCES "polls_question" ("id") DEFERRABLE INITIALLY DEFERRED; COMMIT;
保存數據庫修改:
$ python manage.py migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, polls, sessions Running migrations: Rendering model states... DONE Applying polls.0001_initial... OK
小結:
修改數據庫分三步:
python manage.py makemigrations
建立一個遷移python manage.py migrate
保存變動下面進入交互式的Python Shell跟API一塊兒玩耍吧,別忘了先進入容器。
$ python manage.py shell
如需瞭解database API的相關內容:database API
可進行一下嘗試瞭解API:
>>> from polls.models import Question, Choice # Import the model classes we just wrote. # No questions are in the system yet. >>> Question.objects.all() <QuerySet []> # Create a new Question. # Support for time zones is enabled in the default settings file, so # Django expects a datetime with tzinfo for pub_date. Use timezone.now() # instead of datetime.datetime.now() and it will do the right thing. >>> from django.utils import timezone >>> q = Question(question_text="What's new?", pub_date=timezone.now()) # Save the object into the database. You have to call save() explicitly. >>> q.save() # Now it has an ID. Note that this might say "1L" instead of "1", depending # on which database you're using. That's no biggie; it just means your # database backend prefers to return integers as Python long integer # objects. >>> q.id 1 # Access model field values via Python attributes. >>> q.question_text "What's new?" >>> q.pub_date datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=<UTC>) # Change values by changing the attributes, then calling save(). >>> q.question_text = "What's up?" >>> q.save() # objects.all() displays all the questions in the database. >>> Question.objects.all() <QuerySet [<Question: Question object>]>
爲了讓<Question: Question object>
輸出更可讀
給模塊Question and Choice (in the polls/models.py file) 添加 __str__()
方法
from django.db import models from django.utils.encoding import python_2_unicode_compatible @python_2_unicode_compatible # only if you need to support Python 2 class Question(models.Model): # ... def __str__(self): return self.question_text @python_2_unicode_compatible # only if you need to support Python 2 class Choice(models.Model): # ... def __str__(self): return self.choice_text
除了那些常見的Python方法,能夠添加一個自定義的
import datetime from django.db import models from django.utils import timezone class Question(models.Model): # ... def was_published_recently(self): return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
在次進入shell看看有哪些變化:
>>> from polls.models import Question, Choice # Make sure our __str__() addition worked. >>> Question.objects.all() <QuerySet [<Question: What's up?>]> # Django provides a rich database lookup API that's entirely driven by # keyword arguments. >>> Question.objects.filter(id=1) <QuerySet [<Question: What's up?>]> >>> Question.objects.filter(question_text__startswith='What') <QuerySet [<Question: What's up?>]> # Get the question that was published this year. >>> from django.utils import timezone >>> current_year = timezone.now().year >>> Question.objects.get(pub_date__year=current_year) <Question: What's up?> # Request an ID that doesn't exist, this will raise an exception. >>> Question.objects.get(id=2) Traceback (most recent call last): ... DoesNotExist: Question matching query does not exist. # Lookup by a primary key is the most common case, so Django provides a # shortcut for primary-key exact lookups. # The following is identical to Question.objects.get(id=1). >>> Question.objects.get(pk=1) <Question: What's up?> # Make sure our custom method worked. >>> q = Question.objects.get(pk=1) >>> q.was_published_recently() True # Give the Question a couple of Choices. The create call constructs a new # Choice object, does the INSERT statement, adds the choice to the set # of available choices and returns the new Choice object. Django creates # a set to hold the "other side" of a ForeignKey relation # (e.g. a question's choice) which can be accessed via the API. >>> q = Question.objects.get(pk=1) # Display any choices from the related object set -- none so far. >>> q.choice_set.all() <QuerySet []> # Create three choices. >>> q.choice_set.create(choice_text='Not much', votes=0) <Choice: Not much> >>> q.choice_set.create(choice_text='The sky', votes=0) <Choice: The sky> >>> c = q.choice_set.create(choice_text='Just hacking again', votes=0) # Choice objects have API access to their related Question objects. >>> c.question <Question: What's up?> # And vice versa: Question objects get access to Choice objects. >>> q.choice_set.all() <QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]> >>> q.choice_set.count() 3 # The API automatically follows relationships as far as you need. # Use double underscores to separate relationships. # This works as many levels deep as you want; there's no limit. # Find all Choices for any question whose pub_date is in this year # (reusing the 'current_year' variable we created above). >>> Choice.objects.filter(question__pub_date__year=current_year) <QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]> # Let's delete one of the choices. Use delete() for that. >>> c = q.choice_set.filter(choice_text__startswith='Just hacking') >>> c.delete()
$ python manage.py createsuperuser
Username: admin
Email address: admin@example.com
驗證兩次密碼:
Password: ********** Password (again): ********* Superuser created successfully.
註冊成功
Django的Admin是默認激活的
啓動服務器 docker-compose up
打開: http://127.0.0.1:8000/admin/
輸入帳號密碼
修改polls/admin.py 註冊Question到admin site 來告訴admin Question 已經提供了admin的接口
from django.contrib import admin from .models import Question admin.site.register(Question)
能夠看到以前建立的question
能夠修改
若是選擇時間Now的時候發現時間不對,那可能就是時區問題。
檢查以前的設置mysite/setting.py
例如:
#TIME_ZONE = 'UTC' TIME_ZONE = 'Pacific/Auckland'
好了,完成第二步了