时间:2023-05-29 01:36:01 | 来源:网站运营
时间:2023-05-29 01:36:01 来源:网站运营
Django 3网页开发指南第4版 第1章 Django 3.0入门:本文完整目录请见 Django 3网页开发指南 - 第4版$ sudo pip3 install --upgrade pip setuptools wheel
虚拟环境从Python 3.3开始就已进行内置了。$ cd ~/projects $ mkdir myproject_website 、 $ cd myproject_website $ python3 -m venv env
$ source env/bin/activate
$ . env/bin/activate
(env)$
(env)$ deactivate
(env)$ pip install "Django~=3.0.0"
(env)$ django-admin.py startproject myproject所执行命令会创建一个名为myproject的目录,其中存放项目文件。目录中包含一个名称同样为myproject的Python模块。为避免混淆、保持方便,我们将顶级目录重命名为django-myproject。这个目录会进行版本控制,其中应包含.git或相应的子目录。
myproject_website├── commands/├── db_backups/├── mockups/├── src/│ └── django-myproject/│ ├── externals/│ │ ├── apps/│ │ │ └── README.md│ │ └── libs/│ │ └── README.md│ ├── locale/│ ├── media/│ ├── myproject/│ │ ├── apps/│ │ │ ├── core/│ │ │ │ ├── __init__.py│ │ │ │ └── versioning.py│ │ │ └── __init__.py│ │ ├── settings/│ │ │ ├── __init__.py│ │ │ ├── _base.py│ │ │ ├── dev.py│ │ │ ├── production.py│ │ │ ├── sample_secrets.json│ │ │ ├── secrets.json│ │ │ ├── staging.py│ │ │ └── test.py│ │ ├── site_static/│ │ │ └── site/│ │ │ ├── css/│ │ │ │ └── style.css│ │ │ ├── img/│ │ │ │ ├── favicon-16x16.png│ │ │ │ ├── favicon-32x32.png│ │ │ │ └── favicon.ico│ │ │ ├── js/│ │ │ │ └── main.js│ │ │ └── scss/│ │ │ └── style.scss│ │ ├── templates/│ │ │ ├── base.html│ │ │ └── index.html│ │ ├── __init__.py│ │ ├── urls.py│ │ └── wsgi.py│ ├── requirements/│ │ ├── _base.txt│ │ ├── dev.txt│ │ ├── production.txt│ │ ├── staging.txt│ │ └── test.txt│ ├── static/│ ├── LICENSE│ └── manage.py*└── venv/
# requirements/_base.txt Django~=3.0.4 djangorestframework -e git://github.com/omab/python-social-auth.git@6b1e301c79#egg=python-social-auth
# requirements/production.txt -r _base.txt
# requirements/dev.txt -r _base.txt coverage django-debug-toolbar selenium
(env)$ pip install -r requirements/dev.txt
(env)$ pip freeze > requirements/_base.txt
(env)$ pip freeze > requirements.txt
要在新的虚拟环境中安装这些模块,仅需使用如下命令:(env)$ pip install -r requirements.txt
如果需要通过其它版本控制系统或本地路径安装Python库,可以通过官方文档学习pip的更多用法。BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))修改后长下面这样:BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# myproject/settings/production.py from ._base import *
# myproject/settings/dev.py from ._base import * EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings.production')
ℹ️对于每种环境,推荐在PyCharm的设置、env/bin/activate脚本或.bash_profile中单独设置DJANGO_SETTINGS_MODULE环境变量
# settings/_base.pyimport osBASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))# ... TEMPLATES = [{# ... DIRS: [ os.path.join(BASE_DIR, 'myproject', 'templates'), ],# ... }]# ... LOCALE_PATHS = [ os.path.join(BASE_DIR, 'locale'), ]# ... STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'myproject', 'site_static'), ]STATIC_ROOT = os.path.join(BASE_DIR, 'static') MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# settings/_base.py import os from django.core.exceptions import ImproperlyConfigured def get_secret(setting): """Get the secret variable or return explicit exception.""" try: return os.environ[setting] except KeyError: error_msg = f'Set the {setting} environment variable' # 译者注:f-string 用法在3.6版本开始引入 raise ImproperlyConfigured(error_msg)
SECRET_KEY = get_secret('DJANGO_SECRET_KEY') DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': get_secret('DATABASE_NAME'), 'USER': get_secret('DATABASE_USER'), 'PASSWORD': get_secret('DATABASE_PASSWORD'), 'HOST': 'db', 'PORT': '5432', } }
$ export DJANGO_SECRET_KEY="此处修改为50个字符的随机长字符串"$ export DATABASE_NAME="myproject"$ export DATABASE_USER="myproject"$ export DATABASE_PASSWORD="修改为数据库密码"
注意对于所有密码、API密钥及其它需要在Django配置中使用的敏感信息都应使用get_secret() 函数。# settings/_base.pyimport osimport jsonwith open(os.path.join(os.path.dirname(__file__), 'secrets.json'), 'r') as f: secrets = json.loads(f.read())def get_secret(setting): """Get the secret variable or return explicit exception.""" try: return secrets[setting] except KeyError: error_msg = f'Set the {setting} secret variable' raise ImproperlyConfigured(error_msg)
它会读取配置目录中的secrets.json 文件,该文件至少应当有如下结构:{ "DATABASE_NAME": "myproject", "DATABASE_USER": "myproject", "DATABASE_PASSWORD": "修改为数据库密码", "DJANGO_SECRET_KEY": "此处修改为50个字符的随机长字符串" }
确保在版本控制中忽略secrets.json文件,但为方便起见,可以创建一个带有空值的sample_secrets.json 文件并对其进行版本控制:{ "DATABASE_NAME": "", "DATABASE_USER": "", "DATABASE_PASSWORD": "", "DJANGO_SECRET_KEY": "此处修改为50个字符的随机长字符串" }
externals/ ├── apps │ ├── README.md │ ├── cms │ ├── haystack │ └── storages └── libs ├── README.md ├── boto ├── requests └── twython
# settings/_base.py import os import sys BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) EXTERNAL_BASE = os.path.join(BASE_DIR, "externals") EXTERNAL_LIBS_PATH = os.path.join(EXTERNAL_BASE, "libs") EXTERNAL_APPS_PATH = os.path.join(EXTERNAL_BASE, "apps") sys.path = ["", EXTERNAL_LIBS_PATH, EXTERNAL_APPS_PATH] + sys.path
(env)$ python manage.py shell >>> import sys>>> sys.path
在尝试导入模块时,Python在列表中搜索模块并在查找到时返回每一个结果。# versioning.py import subprocess from datetime import datetime def get_git_changeset_timestamp(absolute_path): repo_dir = absolute_path git_log = subprocess.Popen( "git log --pretty=format:%ct --quiet -1 HEAD", stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, cwd=repo_dir, universal_newlines=True, ) timestamp = git_log.communicate()[0] try: timestamp = datetime.utcfromtimestamp(int(timestamp)) except ValueError: # Fallback to current timestamp return datetime.now().strftime('%Y%m%d%H%M%S') changeset_timestamp = timestamp.strftime('%Y%m%d%H%M%S') return changeset_timestamp
# settings/_base.py from myproject.apps.core.versioning import get_git_changeset_timestamp # ... timestamp = get_git_changeset_timestamp(BASE_DIR) STATIC_URL = f'/static/{timestamp}/'
# settings/_base.pywith open(os.path.join(BASE_DIR, 'myproject', 'settings', 'last-modified.txt'), 'r') as f: timestamp = f.readline().strip() STATIC_URL = f'/static/{timestamp}/'
可以通过预提交钩子(hook)来让Git仓库更新last-modified.txt。这一可执行bash脚本名称就为pre-commit并放在django-myproject/.git/hooks/目录下:# django-myproject/.git/hooks/pre-commit#!/usr/bin/env pythonfrom subprocess import check_output, CalledProcessErrorimport osfrom datetime import datetimedef root(): ''' returns the absolute path of the repository root ''' try: base = check_output(['git', 'rev-parse', '--show-toplevel']) except CalledProcessError: raise IOError('Current working directory is not a git repository') return base.decode('utf-8').strip()def abspath(relpath): '''returns the absolute path for a path given relative to the root of the git repository''' return os.path.join(root(), relpath)def add_to_git(file_path): ''' adds a file to git ''' try: base = check_output(['git', 'add', file_path]) except CalledProcessError: raise IOError('Current working directory is not a git repository') return base.decode('utf-8').strip()def main(): file_path = abspath("myproject/settings/last-modified.txt") with open(file_path, 'w') as f: f.write(datetime.now().strftime("%Y%m%d%H%M%S")) add_to_git(file_path)if __name__ == '__main__': main()
这个脚本会提交到Git仓库时更新last-modified.txt并将该文件添加到Git索引中。# /etc/mysql/my.cnf[client] default-character-set = utf8[mysql] default-character-set = utf8[mysqld]collation-server = utf8_unicode_ci init-connect = 'SET NAMES utf8' character-set-server = utf8
如果以上有任何版块不存在,请自行在文件中添加。如果各版块已经存在,将设置添加到已有的配置中,并使用命令行工具重启MySQL,如下:$ /etc/init.d/mysql restart
$ createdb --encoding=UTF8 --locale=en_US.UTF-8 --template=template0 myproject
# .gitignore### Python template# Byte-compiled / optimized / DLL files __pycache__/*.py[cod]*$py.class# Installer logspip-log.txt pip-delete-this-directory.txt# Unit test / coverage reports htmlcov/.tox/.nox/.coverage .coverage.* .cache nosetests.xml coverage.xml *.cover .hypothesis/ .pytest_cache/# Translations *.mo*.pot# Django stuff: *.log db.sqlite3# Sphinx documentationdocs/_build/# IPython profile_default/ ipython_config.py# Environmentsenv/# Media and Static directories /media/!/media/.gitkeep/static/ !/static/.gitkeep# Secrets secrets.json
/media/ !/media/.gitkeep
这告诉Git要忽略 /media/目录但保持使用版本控制追踪 /media/.gitkeep文件。因为Git版本控制仅追踪文件而不是目录,所以我们使用.gitkeep 来确保在每个环境中都会创建media目录,但不进行追踪。alias delpyc='find . -name "*.py[co]" -deletefind . -type d -name "__pycache__" -delete'2. 此时要清理Python编译后文件,进入项目目录并在命令行中输入如下命令: ```(env)$ delpyc
# 系统库 import os import re from datetime import datetime# 第三方库import botofrom PIL import Image# Django模块from django.db import models from django.conf import settingsfrom cms.models import Page# 当前应用模块from .models import NewsArticle from . import app_settings
(env)$ cd myproject/apps/(env)$ django-admin.py startapp magazine
创建好magazine应用之后,在models.py中添加一个NewsArticle模型、在admin.py中为该模型配置管理界面并在设置的INSTALLED_APPS中添加myproject.apps.magazine。如何对于这些操作你还不熟悉,请参照Django官方课程进行学习。# myproject/apps/magazine/apps.py from django.apps import AppConfig from django.utils.translation import gettext_lazy as _ class MagazineAppConfig(AppConfig): name = "myproject.apps.magazine" verbose_name = _("Magazine") def ready(self): from . import signals
# myproject/apps/magazine/__init__.py default_app_config = "myproject.apps.magazine.apps.MagazineAppConfig"
# myproject/apps/magazine/signals.py from django.db.models.signals import post_save, post_delete from django.dispatch import receiver from django.conf import settings from .models import NewsArticle @receiver(post_save, sender=NewsArticle) def news_save_handler(sender, **kwargs): if settings.DEBUG: print(f"{kwargs['instance']} saved.") @receiver(post_delete, sender=NewsArticle) def news_delete_handler(sender, **kwargs): if settings.DEBUG: print(f"{kwargs['instance']} deleted.")
>>> from django.apps import apps as django_apps>>> magazine_app_config = django_apps.get_app_config("magazine") >>> magazine_app_config<MagazineAppConfig: magazine>>>> magazine_app_config.models_module<module 'magazine.models' from '/path/to/myproject/apps/magazine/models.py'>>>> NewsArticle = django_apps.get_model("magazine", "NewsArticle") >>> NewsArticle<class 'magazine.models.NewsArticle'>
译者注:以上请在django-myproject目录下操作并设置环境变量export DJANGO_SETTINGS_MODULE='myproject.settings.xxx'# myproject/apps/magazine/app_settings.py from django.conf import settings from django.utils.translation import gettext_lazy as _ # Example: SETTING_1 = getattr(settings, "MAGAZINE_SETTING_1", "default value") MEANING_OF_LIFE = getattr(settings, "MAGAZINE_MEANING_OF_LIFE", 42) ARTICLE_THEME_CHOICES = getattr( settings, "MAGAZINE_ARTICLE_THEME_CHOICES", [ ('futurism', _("Futurism")), ('nostalgia', _("Nostalgia")), ('sustainability', _("Sustainability")), ('wonder', _("Wonder")), ] )
from django.db import modelsfrom django.utils.translation import gettext_lazy as _class NewsArticle(models.Model): created_at = models.DateTimeField(_("Created at"), auto_now_add=True) title = models.CharField(_("Title"), max_length=255) body = models.TextField(_("Body")) theme = models.CharField(_("Theme"), max_length=20) class Meta: verbose_name = _("News Article") verbose_name_plural = _("News Articles") def __str__(self): return self.title3. 然后在admin.py中我们将导入并使用app_settings.py中的设置,如下:
from django import formsfrom django.contrib import adminfrom .models import NewsArticlefrom .app_settings import ARTICLE_THEME_CHOICESclass NewsArticleModelForm(forms.ModelForm): theme = forms.ChoiceField( label=NewsArticle._meta.get_field("theme").verbose_name, choices=ARTICLE_THEME_CHOICES, required=not NewsArticle._meta.get_field("theme").blank, ) class Meta: fields = "__all__"@admin.register(NewsArticle)class NewsArticleAdmin(admin.ModelAdmin): form = NewsArticleModelForm4. 如果希望重写给定项目中的ARTICLE_THEME_CHOICES设置,应在项目设置中添加MAGAZINE_ARTICLE_THEME_CHOICES:
from django.utils.translation import gettext_lazy as _ # ...MAGAZINE_ARTICLE_THEME_CHOICES = [ ('futurism', _("Futurism")), ('nostalgia', _("Nostalgia")), ('sustainability', _("Sustainability")), ('wonder', _("Wonder")), ('positivity', _("Positivity")), ('solutions', _("Solutions")), ('science', _("Science")),]### 实现原理...getattr(object, attribute_name[, default_value]) Python函数尝试通过object获取attribute_name属性并在未查找到时返回default_value。我们会从Django设置块中读取不同的设置,在不存在时使用默认值。注意我们也可以在models.py中定义theme字段的选项,但这里在后台管理代码中创建了一个自定义的ModelForm并其中设置了选项。这样做是避免在每次修改ARTICLE_THEME_CHOICES的时候产生一次新的数据库迁移。### 其它内容* *创建应用配置*一节* **第6章 模型管理**## 使用Docker容器处理Django, Gunicorn, Nginx和PostgreSQLDjango项目不仅有Python包的依赖,还对系统有很多要求,如web服务器、数据库、服务端缓存和邮件服务。在开发Django项目时,需要确保所有环境和所有开发者安装了相同的依赖。一种保持这些依赖同步的方式是使用Docker。通过Docker,可以对每个项目单独拥有不同版本的数据库、web 或其它服务。Docker是一种用于创建称之为容器的带配置、自定义虚拟机的系统。它允许我们精确地复制生产环境的设置。Docker通过称为Docker镜像的东西进行创建。镜像由如何构建容器的层(或指令)组成。可以有PostgreSQL镜像、Redis镜像、Memcached镜像,以及针对Django项目的自定义镜像,所有这些镜像可通过Docker Compose 整合成关联的容器。在本小节中,我们将使用项目样板来设置带有数据库的Django项目,由Nginx和Gunicorn提供服务、并通过Docker Compose对它们进行管理。### 准备工作首先,我们需要安装Docker引擎,可按照https://www.docker.com/get-started上的教程进行安装。这通常包含Compose工具,让我们可以管理需要多个容器的系统,对于隔离环境的Django也非常理想。如果要单独安装,可参见https://docs.docker.com/compose/install/中有关Compose的内容。**译者注:** 考虑到国内的网络环境可配置 Docker 仓库的加速:Preferences>Docker Engine,如添加
"registry-mirrors": [ "http://hub-mirror.c.163.com", ]### 如何实现...我们来共同探讨下Django和Docker样板:1. 将https://github.com/archatas/django_docker中的代码下载到本地,如~/projects/django_docker 目录中。 > ℹ️如果你选择的是其它目录,如myproject_docker,那么需要进行全局搜索并将django_docker替换为myproject_docker。2. 打开docker-compose.yml文件。需要创建3个容器:nginx, gunicorn和db。不必担心它看上去很复杂,我们将在后面详细讲解:
version: "3.7"services: nginx: image: nginx:latest ports: - "80:80" volumes: - ./config/nginx/conf.d:/etc/nginx/conf.d - static_volume:/home/myproject/static - media_volume:/home/myproject/media depends_on: - gunicorn gunicorn: build: context: . args: PIP_REQUIREMENTS: "${PIP_REQUIREMENTS}" command: bash -c "/home/myproject/env/bin/gunicorn --workers 3 --bind 0.0.0.0:8000 myproject.wsgi:application" depends_on: - db volumes: - static_volume:/home/myproject/static - media_volume:/home/myproject/media expose: - "8000" environment: DJANGO_SETTINGS_MODULE: "${DJANGO_SETTINGS_MODULE}" DJANGO_SECRET_KEY: "${DJANGO_SECRET_KEY}" DATABASE_NAME: "${DATABASE_NAME}" DATABASE_USER: "${DATABASE_USER}" DATABASE_PASSWORD: "${DATABASE_PASSWORD}" EMAIL_HOST: "${EMAIL_HOST}" EMAIL_PORT: "${EMAIL_PORT}" EMAIL_HOST_USER: "${EMAIL_HOST_USER}" EMAIL_HOST_PASSWORD: "${EMAIL_HOST_PASSWORD}" db: image: postgres:latest restart: always environment: POSTGRES_DB: "${DATABASE_NAME}" POSTGRES_USER: "${DATABASE_USER}" POSTGRES_PASSWORD: "${DATABASE_PASSWORD}" ports: - 5432 volumes: - postgres_data:/var/lib/postgresql/data/volumes: postgres_data: static_volume: media_volume:3. 打开并通读Dockerfile文件。有创建gunicorn容器所需要的一些层(或指令):
# pull official base image FROM python:3.8# accept argumentsARG PIP_REQUIREMENTS=production.txt# set environment variablesENV PYTHONDONTWRITEBYTECODE 1ENV PYTHONUNBUFFERED 1# install dependenciesRUN pip install --upgrade pip setuptools# create user for the Django project RUN useradd -ms /bin/bash myproject# set current userUSER myproject# set work directoryWORKDIR /home/myproject# 添加以下内容避免因 Volume 所带来的 root 权限问题RUN mkdir static && mkdir media# create and activate virtual environment RUN python3 -m venv env# copy and install pip requirementsCOPY --chown=myproject ./src/myproject/requirements /home/myproject/requirements/# 添加豆瓣源在国内加速RUN ./env/bin/pip3 install -i https://pypi.doubanio.com/simple/ -r /home/myproject/requirements/${PIP_REQUIREMENTS}# copy Django project filesCOPY --chown=myproject ./src/myproject /home/myproject/4. 复制build_dev_example.sh 脚本到build_dev.sh中并编辑其内容。有一些要传递给docker-compose脚本的环境变量:
#!/usr/bin/env bash DJANGO_SETTINGS_MODULE=myproject.settings.dev /DJANGO_SECRET_KEY="change-this-to-50-characters-long-random-string" /DATABASE_NAME=myproject /DATABASE_USER=myproject /DATABASE_PASSWORD="change-this-too" /PIP_REQUIREMENTS=dev.txt /docker-compose up --detach --build5. 在命令行工具中,为build_dev.sh添加执行权限并运行它来构建容器:
$ chmod +x build_dev.sh $ ./build_dev.sh6. 如果此时访问http://0.0.0.0/en/,应当会看到Hello, World!页面。 在访问http://0.0.0.0/en/admin/时,会看到如下内容:
OperationalError at /en/admin/ FATAL: role "myproject" does not exist这表示你需要在Docker容器中创建数据库用户及数据库。7. 我们通过SSH连接db容器并在Docker容器中创建数据库用户、密码及数据库本身:
$ docker exec -it django_docker_db_1 bash /# su - postgres /$ createuser --createdb --password myproject /$ createdb --username myproject myproject在被询问时,输入与build_dev.sh脚本中相同的数据库密码。(如未出现6中的报错则无需手动执行以上用户创建) 连续按两次Ctrl + D来登出PostgreSQL用户和Docker容器。现在访问http://0.0.0.0/en/admin/的话,会看到如下内容: ```ProgrammingError at /en/admin/ relation "django_session" does not exist LINE 1: ...ession_data", "django_session"."expire_date" FROM "django_se...这表示我们需要运行迁移来创建数据库模式。
$ docker exec -it django_docker_gunicorn_1 bash $ source env/bin/activate (env)$ python manage.py migrate (env)$ python manage.py collectstatic (env)$ python manage.py createsuperuser回复管理命令所询问的所有问题。按两次Ctrl + D登出Docker容器。如果现在导航至http://0.0.0.0/en/admin/,应当会看到Django后台管理页面,在这里可以通过所创建的超级用户来进行登录。
django_docker/├── Dockerfile├── LICENSE├── README.md├── build_dev_example.sh├── config│ └── nginx│ └── conf.d│ └── myproject.conf├── docker-compose.yml├── git-hooks│ ├── install_hooks.sh│ └── pre-commit└── src └── myproject ├── locale │ └── de │ └── LC_MESSAGES │ └── django.po ├── manage.py ├── myproject │ ├── __init__.py │ ├── apps │ │ └── __init__.py │ ├── settings │ │ ├── __init__.py │ │ ├── _base.py │ │ ├── dev.py │ │ ├── last-update.txt │ │ ├── production.py │ │ ├── staging.py │ │ └── test.py │ ├── site_static │ │ └── site │ │ ├── css │ │ │ └── style.css │ │ ├── img │ │ │ ├── favicon-16x16.png │ │ │ ├── favicon-32x32.png │ │ │ └── favicon.ico │ │ ├── js │ │ │ └── main.js │ │ └── scss │ │ └── style.scss │ ├── templates │ │ ├── base.html │ │ └── index.html │ ├── urls.py │ └── wsgi.py └── requirements ├── _base.txt ├── dev.txt ├── production.txt ├── staging.txt └── test.txt
Docker相关的主要配置在docker-compose.yml和Dockerfile中。Docker Compose是对Docker的命令行API进行的封装。build_dev.sh脚本运行Django项目,Gunicorn WSGI HTTP服务器的端口号为8000,Nginx的端口号为80(对静态和媒体文件提供服务并将请求代理到Gunicorn),PostgreSQL数据库的端口为5432。#/etc/nginx/conf.d/myproject.confupstream myproject { server django_docker_gunicorn_1:8000;}server { listen 80; location / { proxy_pass http://myproject; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; proxy_redirect off; } rewrite "/static//d+/(.*)" /static/$1 last; location /static/ { alias /home/myproject/static/; } location /media/ { alias /home/myproject/media/; } }
在第12章 部署中的在预发布环境中基于Nginx和Gunicorn部署和在生产环境中基于Nginx和Gunicorn部署小节中会学习更多有关Nginx和Gunicorn配置的知识。$ docker-compose down $ ./build_dev.sh
如果和所预期的效果不一致,可以通过docker-compose logs命令来检查日志:$ docker-compose logs nginx$ docker-compose logs gunicorn $ docker-compose logs db
通过SSH来连接其中的任一容器,可以使用下面的命令:$ docker exec -it django_docker_gunicorn_1 bash $ docker exec -it django_docker_nginx_1 bash$ docker exec -it django_docker_db_1 bash
可以使用docker cp命令将Docker容器的数据卷进行拷入和拷出文件、目录的处理:$ docker cp ~/avatar.png django_docker_gunicorn_1:/home/myproject/media/ $ docker cp django_docker_gunicorn_1:/home/myproject/media ~/Desktop/
如果希望更好地掌握Docker和Docker Compose,可以学习官方文档的内容,尤其是https://docs.docker.com/compose/部分。关键词:入门,发指