Django 基础
一、初始化
- 新建目录
mkdir django_demo && cd django_demo
- 用 pipenv,创建虚拟环境
pip install pipenv
# 切到虚拟环境
pipenv shell
这时会生成一个 Pipfile 文件,记录了虚拟环境的状态,类似于 package.json
- 安装 django
# 虚拟环境下安装时换源,修改 Pipfile 文件中的 [[source]] 为
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
# 在虚拟环境中安装
pipenv install django
# 安装 DFR 和 CORS,用于 api
pipenv install django-rest-framework django-cors-headers
# 原来的安装方式
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple django
# 配置全局清华源
pip install pip -U # 升级 pip
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
- 使用脚手架创建项目
django-admin startproject mysite
# 进入项目目录,创建子应用
cd mysite
python manage.py startapp polls
- 配置数据库后,进行数据库迁移,并创建管理用户
# 在 mysite/settings.py 中,修改数据库配置,这里默认使用 sqlite3
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# 有表结构改动时,创建数据库迁移
# python manage.py makemigrations polls
# 执行数据库迁
python manage.py migrate
# 创建管理用户
python manage.py createsuperuser
- 启动项目
# 默认127.0.0.1 是一个回环地址,不能通过外部访问,只能自己访问,如果想在docker外访问就要加上0.0.0.0
python manage.py runserver ((0.0.0.0):(port))
访问 localhost:8000/admin 就能看到如下界面了
二、全局配置
(一). 基础配置
- 在 INSTALLED_APPS 中添加应用 rest_framework、corsheaders 和 polls,
polls
是刚创建的子应用
INSTALLED_APPS = [
'rest_framework',
'corsheaders',
'polls',
...
]
- 在 MIDDLEWARE 中添加 corsheaders.middleware.CorsMiddleware,注册跨域请求中间件,放在最前面
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
...
]
- 设置 CORS_ORIGIN_WHITELIST,添加跨域请求白名单
CORS_ORIGIN_WHITELIST = [
'http://localhost:8080',
'http://localhost:8081',
]
- 设置 LANGUAGE_CODE 为 zh-hans,设置为中文
LANGUAGE_CODE = 'zh-hans'
- 设置 MEDIA_URL 和 MEDIA_ROOT,资源文件的访问
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
(二). 修改数据库配置
# 安装驱动
pipenv install mysqlclient
# 在 mysite/settings.py 中,修改数据库配置
DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.mysql',
# 'NAME': 'cool_admin',
# 'USER': 'root',
# 'PASSWORD': 'root',
# 'HOST': 'hostip',
# 'PORT': '3386'
# }
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'default',
'USER': 'default',
'PASSWORD': 'secret',
'HOST': 'hostip',
'PORT': '5432',
}
}
(三). 使用 simpleui 美化界面
# 安装
pipenv install django-simpleui
# 在 mysite/settings.py 中,INSTALLED_APPS 中添加 'simpleui'
INSTALLED_APPS = [
'simpleui',
...
]
# 在 setting 中添加主题色
SIMPLEUI_DEFAULT_THEME = 'green'
# 设置 logo
SIMPLEUI_LOGO = 'https://avatars.githubusercontent.com/u/28778856?s=200&v=4'
# 关闭广告和分析
SIMPLEUI_HOME_INFO = False
SIMPLEUI_ANALYSIS = False
# 根据教程处理细节
https://zhuanlan.zhihu.com/p/372185998
(四). 使用 Django Filter 过滤
(五). 使用 Django Debug Toolbar 调试
# 安装
pipenv install django-debug-toolbar
# 在 settings.py 中,INSTALLED_APPS 中添加 'debug_toolbar'
INSTALLED_APPS = [
'debug_toolbar',
...
]
# 在 settings.py 中,MIDDLEWARE 中添加 'debug_toolbar.middleware.DebugToolbarMiddleware'
MIDDLEWARE = [
'debug_toolbar.middleware.DebugToolbarMiddleware',
...
]
# 在 settings.py 中,添加配置
DEBUG = True
INTERNAL_IPS = [
'127.0.0.1',
'localhost',
]
# 在 mysite/urls.py 中,添加配置
urlpatterns = [
...
path('__debug__/', include('debug_toolbar.urls')),
]
三、实现一个 polls 应用
如果不使用 rest_framework,只需要 1、2、7 步即可
1. 创建模型
在 polls/models.py 中,创建模型
from django.db import models
class Question(models.Model):
question_text = models.CharField(max_length=200, verbose_name='问题文本')
pub_data = models.DateTimeField('date published')
# 定义元素据,用于在后台管理中显示
class Meta:
verbose_name = '问题'
verbose_name_plural = '问题'
# 定义 __str__ 方法,用于字符串显示
def __str__(self):
return self.question_text
class Choice(models.Model):
# 处理关联关系时,注意处理 related_name
question = models.ForeignKey(Question, related_name='choices', on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
class Meta:
verbose_name = '选项'
verbose_name_plural = '选项'
def __str__(self):
return self.choice_text
2. 注册模型
在 polls/admin.py 中,注册模型
from django.contrib import admin
from .models import Question, Choice
admin.site.register(Question)
admin.site.register(Choice)
3. 定义序列化器
定义序列化器 polls/serializers.py, 序列化器是 Django Rest Framework 提供的功能,能够非常方便地将 Django 数据模型序列化成相应的 JSON 数据格式。
from rest_framework import serializers
from .models import Question
class PollSerializer(serializers.ModelSerializer):
# 在 api 视图中使用关联关系,PrimaryKeyRelatedField 只会返回关联对象的主键
articles = serializers.PrimaryKeyRelatedField(many=True, queryset=Article.objects.all())
# 但是如果直接传其他的序列化器,就会返回序列化后的数据
answer = AnswerSerializer(many=True)
# 处理关联关系时,还应注意要在model定义时,设置related_name
class Meta:
model = Question
# fields = ['id', 'question_text', 'pub_data', 'articles']
fields = '__all__'
depth = 1 # 找查外键的深度,articles 就会加入到返回中
4. 创建视图
在 polls/views.py 中,创建视图,使用 Django Rest Framework 提供的模型视图集,实现了增删改查的功能
from rest_framework import viewsets
from .serializers import PollSerializer
from .models import Question
# Create your views here.
class PollViewSet(viewsets.ModelViewSet):
serializer_class = PollSerializer
queryset = Question.objects.all().order_by('pub_data')
5. 创建路由
在 polls/urls.py 中,用 DefaultRouter 自动创建路由
from django.urls import path, include
from rest_framework import routers
from . import views
router = routers.DefaultRouter()
router.register('polls', views.PollViewSet)
urlpatterns = [
path('', include(router.urls)),
]
6. 注册路由
- 在 mysite/urls.py 中,注册应用的路由
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('polls.urls')),
]
7. 创建并迁移数据库
创建并迁移数据库
# python manage.py makemigrations 全部应用/指定应用
python manage.py makemigrations polls
python manage.py migrate
8. 启动项目
/api/polls/
查看 restful api 的页面
/admin/polls/
查看后台管理页面
9. 添加权限
在 polls/views.py 中,添加权限
from rest_framework import permissions
# 在 ViewSet 中添加 permission_classes 属性
class PollViewSet(viewsets.ModelViewSet):
serializer_class = PollSerializer
queryset = Question.objects.all()
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
在 mysite/urls.py 中,添加权限
urlpatterns = [
path('api-auth/', include('rest_framework.urls')),
...
]
10. 生成依赖文件
pipenv run pip freeze > requirements.txt
四、部署
使用 wsgi 部署
- 安装 WSGI 服务器,gunicorn,uWSGI 等
pipenv install gunicorn
- 在 mysite 目录下,创建 wsgi.py 文件。脚手架会自动创建
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
application = get_wsgi_application()
- 在 mysite 目录下,创建 Procfile 文件,指定启动命令
web: gunicorn mysite.wsgi
或使用 shell 启动,gunicorn myproject.wsgi:application
- 在 mysite/settings.py 中配置
# 修改 DEBUG 为 False,设置 ALLOWED_HOSTS
DEBUG = False
ALLOWED_HOSTS = ['*']
# 设置静态文件的访问
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
# 设置数据库
# 设置日志
- 配置 Nginx 或其他 Web 服务器以反向代理 Gunicorn 服务器。
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
使用 asgi 部署
- 安装 ASGI 服务器,Daphne、Uvicorn、Hypercorn 等。
pipenv install daphne
- 在 mysite 目录下,创建 asgi.py 文件。脚手架会自动创建
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
application = get_asgi_application()
- 在 mysite 目录下,创建 Procfile 文件,指定启动命令
web: daphne mysite.asgi:application
或使用 shell 启动,daphne myproject.asgi:application
-
在 mysite/settings.py 中配置
-
配置 Nginx 或其他 Web 服务器以反向代理 ASGI 服务器。
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
五、数据库及迁移
1. 数据库迁移命令
# 创建迁移文件
python manage.py makemigrations
# 查看迁移文件
python manage.py sqlmigrate polls 0001
# 查看迁移状态
python manage.py showmigrations polls
# 回滚迁移
python manage.py migrate polls zero
# 回滚到指定迁移
python manage.py migrate polls 0001
# 执行迁移
python manage.py migrate polls
2. 模型关联关系
一对一 用 OneToOneField
一对多 用 ForeignKey
多对多 用 ManyToManyField
五、DRF 关于数据处理
(一). 序列化器
1. 重写增删查改方法
from rest_framework import serializers
from .models import Question
class PollSerializer(serializers.ModelSerializer):
# 在 api 视图中使用关联关系,PrimaryKeyRelatedField 只会返回关联对象的主键
articles = serializers.PrimaryKeyRelatedField(many=True, queryset=Article.objects.all())
# 但是如果直接传其他的序列化器,就会返回序列化后的数据
answer = AnswerSerializer(many=True)
# 处理关联关系时,还应注意要在model定义时,设置related_name
class Meta:
model = Question
# fields = ['id', 'question_text', 'pub_data', 'articles']
fields = '__all__'
depth = 1 # 找查外键的深度,articles 就会加入到返回中
# 重写 create 方法,重写创建逻辑
def create(self, validated_data):
articles_data = validated_data.pop('articles')
question = Question.objects.create(**validated_data)
for article_data in articles_data:
Article.objects.create(question=question, **article_data)
return question
# 重写 update 方法,重写更新逻辑
def update(self, instance, validated_data):
articles_data = validated_data.pop('articles')
articles = (instance.articles).all()
articles = list(articles)
instance.question_text = validated_data.get('question_text', instance.question_text)
instance.pub_data = validated_data.get('pub_data', instance.pub_data)
instance.save()
for article_data in articles_data:
article = articles.pop(0)
article.article_text = article_data.get('article_text', article.article_text)
article.save()
return instance
# 重写 to_representation 方法,修改返回的数据
def to_representation(self, instance):
ret = super().to_representation(instance)
ret['articles'] = ArticleSerializer(instance.articles.all(), many=True).data
res['current_url'] = self.context['request'].build_absolute_uri()
return ret
# 重写 to_internal_value 方法,用于处理关联关系
def to_internal_value(self, data):
articles_data = data.pop('articles')
validated_data = super().to_internal_value(data)
validated_data['articles'] = articles_data
return validated_data
2. 使用 SerializerMethodField 添加额外的数据
class ModalInfoSerializer(serializers.ModelSerializer):
# 添加的数据
external_data = serializers.SerializerMethodField()
class Meta:
model = ModalInfo
fields = '__all__'
# 请求外部的数据 external_data 为字段名, obj 是上次返回的数据
def get_external_data(self, obj):
try:
# test_url = 'http://10.10.2.201:8336/api/menu/list'
response = requests.get(obj.data_url)
return response.json()
except Exception as e:
return {'code': 500, 'msg': '获取数据失败', 'data': e.__str__()}
(二). 视图
1. 添加额外的接口
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
class MarkInfoViewSet(viewsets.ModelViewSet):
queryset = MarkInfo.objects.all()
serializer_class = MarkInfoSerializer
# 添加额外的接口 /hello_world
@action(detail=False, methods=['get'])
def by_workshop(self, request):
workshop_id = request.query_params.get('workshop_id')
if not workshop_id:
return Response({'message': '请传入workshop_id'})
# return Response(info_list) # 如果处理可以返回数据
# queryset 转 dict
markinfos = MarkInfo.objects.filter(workshop=workshop_id)
info_list = [model_to_dict(info) for info in markinfos]
return Response(dict_key_to_camel(info_list))