1. 当前位置:网站首页 > Python

Django基本使用【1】


环境说明

Ubuntu20.04
Python3.8
Django3.2.8
djangorestframework-simplejwt 5.2.0
django-cors-headers 3.13.0

基本命令

# 生成数据库命令
python3 manage.py makemigrations
python3 manage.py migrate
# 启动命令
python3 manage.py runserver 0.0.0.0:8190
# 新建app命令
python3 manage.py startapp api
# 新建项目命令
django-admin startproject jw

项目路径


.
├── api
│   ├── admin.py
│   ├── apps.py
│   ├── __init__.py
│   ├── migrations
│   │   ├── 0001_initial.py
│   │   ├── 0002_alter_user_phone_no.py
│   │   ├── 0003_user_state.py
│   │   ├── __init__.py
│   │   └── __pycache__
│   │       ├── 0001_initial.cpython-38.pyc
│   │       ├── 0002_alter_user_phone_no.cpython-38.pyc
│   │       ├── 0002_alter_user_username.cpython-38.pyc
│   │       ├── 0003_alter_user_username.cpython-38.pyc
│   │       ├── 0003_user_state.cpython-38.pyc
│   │       └── __init__.cpython-38.pyc
│   ├── models.py
│   ├── __pycache__
│   │   ├── admin.cpython-38.pyc
│   │   ├── apps.cpython-38.pyc
│   │   ├── __init__.cpython-38.pyc
│   │   ├── models.cpython-38.pyc
│   │   ├── serializers.cpython-38.pyc
│   │   ├── urls.cpython-38.pyc
│   │   └── views.cpython-38.pyc
│   ├── serializers.py
│   ├── tests.py
│   ├── urls.py
│   └── views.py
├── log
│   ├── product_collect.log
│   ├── product_err.log
│   └── product_info.log
├── manage.py
├── product
│   ├── asgi.py
│   ├── __init__.py
│   ├── __pycache__
│   │   ├── __init__.cpython-38.pyc
│   │   ├── settings.cpython-38.pyc
│   │   ├── urls.cpython-38.pyc
│   │   └── wsgi.cpython-38.pyc
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── utils
├── error.py
├── exception.py
├── __init__.py
├── pagination.py
├── __pycache__
│   ├── error.cpython-38.pyc
│   ├── exception.cpython-38.pyc
│   ├── __init__.cpython-38.pyc
│   ├── pagination.cpython-38.pyc
│   └── renderer.cpython-38.pyc
├── renderer.py
└── rendererresponse.py
## setting.py
主要涉及到模块的加载及配置
```python
from pathlib import Path
import os
from datetime import timedelta

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent

STATIC_ROOT = BASE_DIR / 'staticfiles'
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-tqz+9yqxcfh(z_e&lxh*5uzj)^phfgdi6(qfg$78xso2xfx-5a'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = ['*']
AUTH_USER_MODEL = 'api.User'
SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(days=1),   # 设置token有效时间
    'REFRESH_TOKEN_LIFETIME': timedelta(days=2),  # 刷新token有效时间
    'ROTATE_REFRESH_TOKENS': False,
    'BLACKLIST_AFTER_ROTATION': False,
    'UPDATE_LAST_LOGIN': True,                    # 设置为True会在用户登录时,更新user表中的last_login字段

    'ALGORITHM': 'HS256',                         # 加密算法
    'SIGNING_KEY': SECRET_KEY,                    # 签名密钥
    'VERIFYING_KEY': None,                        # 验证密钥,用于验证生成令牌的内容
    'AUDIENCE': None,                             # 设置为None时,此字段将从token中排除,并且不会进行验证
    'ISSUER': None,                               # 设置为None时,此字段将从token中排除,并且不会进行验证
    'JWK_URL': None,                              # 设置为None时,此字段将从token中排除,并且在验证期间不使用
    'LEEWAY': 0,                                  # 用来给到期时间留一些余地

    'AUTH_HEADER_TYPES': ('Bearer',),             # 认证的标签头,类似jwt token中的jwt
    'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',     # 身份验证的授权标头名称
    'USER_ID_FIELD': 'id',
    'USER_ID_CLAIM': 'user_id',                   # 生成token中声明将用于存储用户标识符
    'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule',

    'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
    'TOKEN_TYPE_CLAIM': 'token_type',             # 用于存储token类型的声明名称
    'TOKEN_USER_CLASS': 'rest_framework_simplejwt.models.TokenUser',

    'JTI_CLAIM': 'jti',                           # 用于存储令牌的唯一标识符的声明名称

    'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp',
    'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5),
    'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1),
}

# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',                     # 站台管理系统
    'django.contrib.auth',                      # 认证系统
    'django.contrib.contenttypes',              # content type框架
    'django.contrib.sessions',                  # session框架
    'django.contrib.messages',                  # message框架
    'django.contrib.staticfiles',               # 静态文件管理框架
    'rest_framework',                           # restful插件
    'versatileimagefield',                      # 图片插件
    'django_filters',                           # 过滤插件
    'api',                                      # 接口app
    'corsheaders',                              # 跨域插件
    'rest_framework_simplejwt.token_blacklist'  # 认证插件
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'corsheaders.middleware.CorsMiddleware',                  # 用来解决跨域问题
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'product.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]
# 图像尺寸配置
VERSATILEIMAGEFIELD_RENDITION_KEY_SETS = {
    'product_headshot': [
        ('full_size', 'url'),
        ('thumbnail', 'thumbnail__100x100'),
        ('medium_square_crop', 'crop__400x400'),
        ('small_square_crop', 'crop__50x50')
    ]
}

WSGI_APPLICATION = 'product.wsgi.application'

# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'HOST': '127.0.0.1',
        'PORT': 3306,
        'USER': 'root',
        'PASSWORD': 'Liuyang123456.',
        'NAME': 'product_center'
    }
}

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

# Password validation
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]

# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/

LANGUAGE_CODE = 'zh-Hans'

TIME_ZONE = 'Asia/Shanghai'

USE_I18N = True

USE_L10N = True

USE_TZ = False

AUTHENTICATION_BACKENDS = (
    'django.contrib.auth.backends.ModelBackend',
)

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/

STATIC_URL = '/static/'

# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True  # 允许携带cookie

REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': [
        'django_filters.rest_framework.DjangoFilterBackend'
    ],
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ],
    # 全局配置异常模块
    'EXCEPTION_HANDLER': 'utils.exception.custom_exception_handler',
    # 修改默认返回JSON的renderer的类
    'DEFAULT_RENDERER_CLASSES': (
        'utils.renderer.CustomRenderer',
    ),
}

# 日志配置
BASE_LOG_DIR = os.path.join(BASE_DIR, "log")
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,  # 禁用已经存在的logger实例
    # 日志文件的格式
    'formatters': {
        'standard': {'format': '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]'
                               '[%(levelname)s][%(message)s]'},
        'simple': {'format': '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'},
        'collect': {'format': '%(message)s'}
    },
    # 过滤器
    'filters': {
        'require_debug_true': {'()': 'django.utils.log.RequireDebugTrue',},
    },
    # 处理器
    'handlers': {
        # 在终端打印
        'console': {
            'level': 'DEBUG',
            'filters': ['require_debug_true'],  # 只有在Django debug为True时才在屏幕打印日志
            'class': 'logging.StreamHandler',  #
            'formatter': 'simple'
        },
        # 默认的
        'default': {
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,自动切
            'filename': os.path.join(BASE_LOG_DIR, "product_info.log"),  # 日志文件
            'maxBytes': 1024 * 1024 * 50,  # 日志大小 50M
            'backupCount': 2,  # 最多备份几个
            'formatter': 'standard',
            'encoding': 'utf-8',
        },
        # 专门用来记错误日志
        'error': {
            'level': 'ERROR',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,自动切
            'filename': os.path.join(BASE_LOG_DIR, "product_err.log"),  # 日志文件
            'maxBytes': 1024 * 1024 * 50,  # 日志大小 50M
            'backupCount': 3,
            'formatter': 'standard',
            'encoding': 'utf-8',
        },
        # 专门定义一个收集特定信息的日志
        'collect': {
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,自动切
            'filename': os.path.join(BASE_LOG_DIR, "product_collect.log"),
            'maxBytes': 1024 * 1024 * 50,  # 日志大小 50M
            'backupCount': 3,
            'formatter': 'collect',
            'encoding': 'utf-8',
        }
    },
    'loggers': {
        # 默认的logger应用如下配置
        '': {
            'handlers': ['default', 'console', 'error'],  # 上线之后可以把console移除
            'level': 'DEBUG',
            'propagate': True,  # 向不向更高级别的logger传递
        },
        # 名为 collect的logger还单独处理
        'collect': {
            'handlers': ['console', 'collect'],
            'level': 'INFO',
        }
    },
}

第三方类

# error.py
from rest_framework.exceptions import APIException

class ParamsException(APIException):
    """
    serializers自定义错误响应
    """

    def __init__(self, error):
        self.detail = error
# exception.py
# 自定义异常处理
from rest_framework.views import exception_handler
from rest_framework.views import Response
from rest_framework import status

def custom_exception_handler(exc, context):
    # 先调用REST framework默认的异常处理方法获得标准错误响应对象
    response = exception_handler(exc, context)
    # 如果response响应对象为空,则设置message这个key的值,并将状态码设为500
    # 如果response响应对象不为空,则则设置message这个key的值,并将使用其本身的状态码
    if response is None:
        return Response({
            'message': '服务器错误:{exc}'.format(exc=exc)
        }, status=status.HTTP_500_INTERNAL_SERVER_ERROR, exception=True)

    else:
        return Response({
            'message': '{exc}'.format(exc=exc),
        }, status=response.status_code, exception=True)
# pagination.py
from rest_framework.pagination import PageNumberPagination

class MyPagination(PageNumberPagination):
    page_size = 2
    page_size_query_param = 'size'
    max_page_size = 30
# renderer.py
# 自定义返回处理

# 导入控制返回的JSON格式的类
from rest_framework.renderers import JSONRenderer

class CustomRenderer(JSONRenderer):
    # 重构render方法
    def render(self, data, accepted_media_type=None, renderer_context=None):
        if renderer_context:

            # print(renderer_context)
            # print(renderer_context["response"].status_code)
            # 响应的信息,成功和错误的都是这个
            # 成功和异常响应的信息,异常信息在前面自定义异常处理中已经处理为{'message': 'error'}这种格式
            # 如果返回的data为字典
            if isinstance(data, dict):
                # 响应信息中有message和code这两个key,则获取响应信息中的message和code,并且将原本data中的这两个key删除,放在自定义响应信息里
                # 响应信息中没有则将msg内容改为请求成功 code改为请求的状态码
                msg = data.pop('message', '请求成功')
                code = data.pop('code', renderer_context["response"].status_code)
            # 如果不是字典则将msg内容改为请求成功 code改为请求的状态码
            else:
                msg = '请求成功'
                code = renderer_context["response"].status_code

            # 自定义返回的格式
            ret = {
                'msg': msg,
                'code': code,
                'data': data,
            }
            # 返回JSON数据
            return super().render(ret, accepted_media_type, renderer_context)
        else:
            return super().render(data, accepted_media_type, renderer_context)

项目urls引入到下一级

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('api.urls')),
]

下一级urls文件

from django.urls import path
from . import views
from rest_framework_simplejwt.views import TokenRefreshView

urlpatterns = [
    path('login/', views.Login.as_view(), name='token_obtain_pair'),    # 登录
    path('login/refresh/', TokenRefreshView.as_view(), name='token_refresh'),  # 登录续约
    path('getUser/', views.UserView.as_view(), name='get_user'),        # 获取个人信息
    path('register/', views.RegisterView.as_view(), name='auth_register'),  # 注册
    path('changePassword', views.ChangePasswordView.as_view(), name='auth_change_password'),  # 修改用户密码
    path('update_profile/<int:id>/', views.UpdateProfileView.as_view(), name='auth_update_profile'),  # 修改个人信息

    path('getUserAll/', views.GetUserAll.as_view(), name='get_user_all'),    # 获取所有用户表 支持分页
    path('deleteUser/', views.DeleteUser.as_view(), name='delete_user'),    # 删除用户,软删除
    path('resetPassword/', views.ResetPassword.as_view(), name='reset_password'),    # 超级管理员重置密码

]

views文件

from django.shortcuts import render

from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated, AllowAny, IsAdminUser
from .serializers import MyTokenObtainPairSerializer, UserSerializer, RegisterSerializer, UpdateUserSerializer
from rest_framework_simplejwt.views import TokenObtainPairView
from rest_framework.response import Response
from rest_framework import status
from rest_framework import generics
from api.models import User
from utils.error import ParamsException
from utils.pagination import MyPagination

class Login(TokenObtainPairView):
    permission_classes = [AllowAny]
    serializer_class = MyTokenObtainPairSerializer

class UserView(APIView):
    permission_classes = (IsAuthenticated,)

    @staticmethod
    def get(request):
        user = request.user
        serializer = UserSerializer(user)
        return Response(data=serializer.data, status=status.HTTP_200_OK)

class RegisterView(generics.CreateAPIView):
    permission_classes = (IsAdminUser,)
    serializer_class = RegisterSerializer

class ChangePasswordView(APIView):
    permission_classes = (IsAuthenticated,)

    @staticmethod
    def put(request):
        data = request.data
        if not request.user.check_password(data['old_password']):
            raise ParamsException('旧密码不对')
        if data['password'] != data['confirm_password']:
            raise ParamsException('密码不一致')
        request.user.set_password(data['password'])
        request.user.save()
        return Response({"message": "修改成功,请重新登录"})

class UpdateProfileView(generics.UpdateAPIView):
    queryset = User.objects.all()
    permission_classes = (IsAuthenticated,)
    lookup_field = 'id'
    serializer_class = UpdateUserSerializer

class GetUserAll(APIView):
    permission_classes = (IsAdminUser,)

    def get(self, request):
        data_list = User.objects.filter(state=1).order_by('id')
        # 实例化分页器
        page_obj = MyPagination()
        # 调用分页方法分页
        page_query = page_obj.paginate_queryset(data_list, request, view=self)
        # 序列化
        ser = UserSerializer(page_query, many=True)
        return page_obj.get_paginated_response(ser.data)

class DeleteUser(APIView):
    permission_classes = (IsAdminUser,)

    @staticmethod
    def put(request):
        data = request.data
        User.objects.filter(id=data["id"]).update(state=0)
        return Response({"message": "删除用户成功"})

class ResetPassword(APIView):
    permission_classes = (IsAdminUser,)

    @staticmethod
    def put(request):
        data = request.data
        user = User.objects.get(id=data["id"])
        user.set_password(data['password'])
        user.save()
        return Response({"message": "重置密码成功"})

model文件

from django.db import models

from django.contrib.auth.models import AbstractUser
from django.contrib.auth.models import BaseUserManager

class CustomUserManager(BaseUserManager):

    def create_user(self, username, password, **extra_fields):
        if not username:
            raise ValueError('必须设置用户名!')
        user = self.model(username=username, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, username, password, **extra_fields):
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)
        extra_fields.setdefault('is_active', True)
        if extra_fields.get('is_staff') is not True:
            raise ValueError('Superuser must have is_staff=True.')
        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must have is_superuser=True.')

        return self.create_user(username, password, **extra_fields)

class User(AbstractUser):
    # 定义角色
    class Role(models.TextChoices):
        GENERAL = "普通用户"
        ADMIN = "管理员"
        SUPER = "超级管理员"
    id = models.AutoField(db_column='id', primary_key=True)
    is_active = models.BooleanField(db_column='is_active', default=True)
    role = models.CharField(db_column='role', max_length=20, choices=Role.choices, null=True)
    username = models.CharField(db_column='username', max_length=20, unique=True, blank=True, null=True)
    phone_no = models.BigIntegerField(db_column='phone_no', blank=True, null=True)
    state = models.BooleanField(db_column='state', default=True)
    USERNAME_FIELD = 'username'
    REQUIRED_FIELDS = []

    class Meta:
        db_table = 'user'

    objects = CustomUserManager()

serializers.py文件

from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from api.models import User
from rest_framework import serializers, validators

class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
    def create(self, validated_data):
        pass

    def update(self, instance, validated_data):
        pass

    username_field = User.USERNAME_FIELD

    default_error_messages = {
        'no_active_account': '用户名密码错误或账号未激活!'
    }

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['id', 'username', 'password', 'phone_no', 'last_login', 'role', 'is_superuser',
                  'is_active', 'date_joined']
        extra_kwargs = {'password': {'write_only': True}}

class RegisterSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['id', 'username', 'password', 'phone_no', 'last_login', 'role', 'is_superuser',
                  'is_active', 'date_joined', 'first_name']
        extra_kwargs = {
            'password': {'write_only': True},
            'username': {'validators': [validators.UniqueValidator(
                queryset=User.objects.all(),
                message='名称重复')]},
        }

    def create(self, validated_data):
        user = User.objects.create_user(**validated_data)
        return user

class UpdateUserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('is_superuser', 'id', 'username', 'is_active', 'last_name', 'phone_no', 'role')
        extra_kwargs = {
            'last_name': {'required': True},
        }

    def validate_username(self, value):
        user = self.context['request'].user
        if User.objects.exclude(id=user.id).filter(username=value).exists():
            raise serializers.ValidationError("该用户名已被使用")
        return value

    def update(self, instance, validated_data):
        new_data = {}
        for key in validated_data.keys():
            if validated_data[key] is not None and validated_data[key] != "":
                new_data[key] = validated_data[key]
        instance = super().update(instance, new_data)
        instance.save()
        return instance

本文最后更新于2022-7-12,已超过 3个月没有更新,如果文章内容或图片资源失效,请留言反馈,我们会及时处理,谢谢!
版权说明

本文地址:http://www.liuyangdeboke.cn/?post=28
未标注转载均为本站远程,转载请注明文章出处:

发表评论

联系我们

在线咨询:点击这里给我发消息

微信号:17721538135

工作日:9:00-23:00,节假日休息

扫码关注