零基础,直接上手 Django

Django 基础

一、初始化

  1. 新建目录
mkdir django_demo && cd django_demo
  1. 用 pipenv,创建虚拟环境
pip install pipenv
# 切到虚拟环境
pipenv shell

这时会生成一个 Pipfile 文件,记录了虚拟环境的状态,类似于 package.json

  1. 安装 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
  1. 使用脚手架创建项目
django-admin startproject mysite

# 进入项目目录,创建子应用
cd mysite
python manage.py startapp polls
  1. 配置数据库后,进行数据库迁移,并创建管理用户
# 在 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
  1. 启动项目
# 默认127.0.0.1 是一个回环地址,不能通过外部访问,只能自己访问,如果想在docker外访问就要加上0.0.0.0
python manage.py runserver ((0.0.0.0):(port))

访问 localhost:8000/admin 就能看到如下界面了
在这里插入图片描述

二、全局配置

(一). 基础配置

  1. 在 INSTALLED_APPS 中添加应用 rest_framework、corsheaders 和 polls, polls 是刚创建的子应用
INSTALLED_APPS = [
    'rest_framework',
    'corsheaders',
    'polls',
    ...
]
  1. 在 MIDDLEWARE 中添加 corsheaders.middleware.CorsMiddleware,注册跨域请求中间件,放在最前面
MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
    ...
]
  1. 设置 CORS_ORIGIN_WHITELIST,添加跨域请求白名单
CORS_ORIGIN_WHITELIST = [
    'http://localhost:8080',
    'http://localhost:8081',
]
  1. 设置 LANGUAGE_CODE 为 zh-hans,设置为中文
LANGUAGE_CODE = 'zh-hans'
  1. 设置 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. 注册路由

  1. 在 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 部署

  1. 安装 WSGI 服务器,gunicorn,uWSGI 等
pipenv install gunicorn
  1. 在 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()
  1. 在 mysite 目录下,创建 Procfile 文件,指定启动命令
web: gunicorn mysite.wsgi

或使用 shell 启动,gunicorn myproject.wsgi:application

  1. 在 mysite/settings.py 中配置
# 修改 DEBUG 为 False,设置 ALLOWED_HOSTS
DEBUG = False

ALLOWED_HOSTS = ['*']

# 设置静态文件的访问
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')

# 设置数据库

# 设置日志
  1. 配置 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 部署

  1. 安装 ASGI 服务器,Daphne、Uvicorn、Hypercorn 等。
pipenv install daphne
  1. 在 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()
  1. 在 mysite 目录下,创建 Procfile 文件,指定启动命令
web: daphne mysite.asgi:application

或使用 shell 启动,daphne myproject.asgi:application

  1. 在 mysite/settings.py 中配置

  2. 配置 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))

相关参考文章:
官方文档
快速体验
快速体验 api

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值