Implementing an application authentication mechanism similar to Tencent or Alibaba Cloud in django-ninja.
Django-Ninja API authentication mechanism? Isn't it just another authentication system based on AppClient, supporting query parameters, headers, and Bearer Token authentication, plus IP whitelisting and scopes permission control? The code is complex enough, convoluted enough?
目录37 个章节
Preface
This article introduces how to use the Django-Ninja API authentication mechanism based on the AppClient model.
This is also a derivative of the mid-platform project mentioned previously.
I will probably write another article related to the mid-platform project.
This series of articles is destined to be read by few people, as it is still somewhat niche.
However, I still need to write it. Even without readers, I want to document it so that I can use it in the future when needed.
PS: This article is based on the use of the DjangoStarter framework, assuming the reader is familiar with the project structure and infrastructure of DjangoStarter version 3.0 or above.
Due to space limitations, this article cannot include all the code. Interested readers can reply "API authentication" in the background of the WeChat public account to get the complete code.
Overview
This authentication mechanism provides the following features:
- ==Multiple authentication methods==: Supports query parameters, request headers, and Bearer Token
- ==IP Whitelist==: Supports individual IP addresses and CIDR network ranges
- ==Scope-based permission control==: Fine-grained permission management based on scopes
- ==Status check==: Automatically verifies the enabled status of AppClient
- ==Security==: Includes a complete authentication and authorization flow
It goes best with the official documentation~
https://django-ninja.dev/guides/authentication/
Usage
To give you an intuitive experience
I'll introduce the usage first
Then I'll explain the implementation.
1. Create AppClient
First, create an AppClient either in Django Admin or through code:
from apps.app.models import AppClient
app_client = AppClient.objects.create(
app_id="my-app",
app_name="My App",
secret_key="your-secret-key-here",
allowed_ips="192.168.1.0/24,10.0.0.1", # Optional: IP restriction
scopes="project:read,project:write,user:read", # Optional: scope
status=AppClient.Status.ACTIVE
)
2. Using Authentication in the API
from ninja import Router
from core.authentication import (
api_key_auth,
api_key_header_auth,
api_key_bearer_auth,
require_app_client_scopes,
AppClientScopes,
)
router = Router()
Authentication with Query Parameters
@router.get("/data", auth=api_key_auth) def get_data(request): app_client = request.auth return {"data": "success", "client": app_client.app_name}
Using Request Header Authentication
@router.get("/info", auth=api_key_header_auth) def get_info(request): return {"info": "success"}
Using Bearer Token Authentication
@router.get("/status", auth=api_key_bearer_auth) def get_status(request): return {"status": "success"}
### 3. Adding Permission Control
```python
# Requires Specific Permissions
@router.get("/projects", auth=app_client_api_key)
@require_app_client_scopes([AppClientScopes.PROJECT_READ])
def list_projects(request):
return {"projects": []}
# Requires Multiple Permissions
```python
@router.post("/projects", auth=app_client_api_key)
@require_app_client_scopes([AppClientScopes.PROJECT_WRITE, AppClientScopes.USER_READ])
def create_project(request):
return {"message": "Project created successfully"}
Manual Permission Check
@router.get("/custom-check", auth=app_client_api_key) def custom_permission_check(request): from core.authentication import AppClientScopeChecker
if AppClientScopeChecker.has_any_scope(request, [AppClientScopes.PROJECT_READ, AppClientScopes.PROJECT_WRITE]):
return {"access": "granted"}
else:
return {"access": "denied"}
## Authentication Methods
### 1. Query Parameter Authentication (APIKeyQuery)
**Usage:**
```bash
GET /api/endpoint?api_key=your-secret-key
Code Example:
from core.authentication import api_key_auth
@router.get("/data", auth=api_key_auth)
def get_data(request):
return {"data": "success"}
2. API Key Header Authentication (APIKeyHeader)
Usage:
GET /api/endpoint
X-API-Key: your-secret-key
Code Example:
from core.authentication import api_key_header_auth
@router.get("/data", auth=api_key_header_auth)
def get_data(request):
return {"data": "success"}
3. Bearer Token Authentication (Bearer Token 认证)
Usage:
GET /api/endpoint
Authorization: Bearer your-secret-key
Code Example:
from core.authentication import api_key_bearer_auth
@router.get("/data", auth=api_key_bearer_auth)
def get_data(request):
return {"data": "success"}
Permission Scopes System
To clarify upfront, the scopes approach is simply the easiest to implement, but it doesn't mean it's the most appropriate.
After all, Django already has its own built-in permission system. I've considered integrating that system, which would allow for convenient editing of permissions for each application in the admin panel.
However, since that implementation would be slightly more complicated, and for the sake of getting things launched quickly, I initially went with creating my own scopes system.
I definitely plan to try ==integrating the Django permission system== in the future!
Permission Format
Permissions use the resource:action format. For example:
project:read- Project read permissionsproject:write- Project write permissionsuser:*- All user-related permissions*- Super administrator permissions
Predefined Permission Constants
from core.authentication import AppClientScopes
Project Permissions
AppClientScopes.PROJECT_READ # "project:read" AppClientScopes.PROJECT_WRITE # "project:write" AppClientScopes.PROJECT_DELETE # "project:delete" AppClientScopes.PROJECT_ALL # "project:*"
User Permissions
AppClientScopes.USER_READ # "user:read" AppClientScopes.USER_WRITE # "user:write" AppClientScopes.USER_DELETE # "user:delete" AppClientScopes.USER_ALL # "user:*"
Super Privileges
AppClientScopes.SUPER_ADMIN # "*"
### Permission Checking Methods
#### 1. Decorator Approach (Recommended)
```python
from core.authentication import require_app_client_scopes, AppClientScopes
@router.get("/projects", auth=app_client_api_key)
@require_app_client_scopes([AppClientScopes.PROJECT_READ])
def list_projects(request):
return {"projects": []}
2. Manual Check Approach
from core.authentication import AppClientScopeChecker
@router.get("/data", auth=app_client_api_key)
def get_data(request):
# Check if all specified permissions are present
if AppClientScopeChecker.check_scopes(request, [AppClientScopes.PROJECT_READ]):
return {"data": "success"}
# Check if any one of the permissions is present
if AppClientScopeChecker.has_any_scope(request, [AppClientScopes.PROJECT_READ, AppClientScopes.PROJECT_WRITE]):
return {"data": "partial access"}
# Get all client permissions
scopes = AppClientScopeChecker.get_client_scopes(request)
return {"error": "Insufficient permissions", "your_scopes": scopes}
IP Whitelist
Configure IP Restrictions
Configure the allowed IP addresses in the allowed_ips field of AppClient:
# Single IP
app_client.allowed_ips = "192.168.1.100"
# Multiple IPs
app_client.allowed_ips = "192.168.1.100,10.0.0.1,203.0.113.5"
# CIDR Subnet
app_client.allowed_ips = "192.168.1.0/24,10.0.0.0/8"
# Mixed Configuration
app_client.allowed_ips = "192.168.1.100,10.0.0.0/8,203.0.113.0/24"
# No IP restrictions (leave empty or None)
app_client.allowed_ips = None
IP Acquisition Logic
The system will acquire the client IP address according to the following priority:
- The first IP from the
HTTP_X_FORWARDED_FORheader (applicable in proxy/load balancing environments) REMOTE_ADDR(the IP of the direct connection)
Error Handling
Authentication Failed
When authentication fails, an HTTP 401 error will be returned:
{
"detail": "Unauthorized"
}
Insufficient Permissions
When a permission check fails, an HTTP 403 error is returned:
{
"detail": "缺少权限: project:write"
}
Custom Error Handling
from core.authentication import AppClientPermissionError
@router.get("/custom", auth=app_client_api_key)
def custom_endpoint(request):
if not some_condition:
raise AppClientPermissionError("自定义权限错误信息")
return {"data": "success"}
Test API
The project provides complete test APIs that can be used to verify authentication and permission features:
# Test API Key Authentication
GET /api/app/test/api-key?api_key=your-secret-key
# Test Header Authentication
GET /api/app/test/api-key-header
X-API-Key: your-secret-key
# Testing Bearer Token Authentication
GET /api/app/test/bearer
Authorization: Bearer your-secret-key
# Test Permission Scopes
GET /api/app/test/scopes/project-read?api_key=your-secret-key
GET /api/app/test/scopes/project-write?api_key=your-secret-key
GET /api/app/test/scopes/multiple?api_key=your-secret-key
## Best Practices
Here are some recommendations for best practices
Regardless of the technology or framework used, these can be referenced during actual development
### 1. Key Management
- Use strong keys (recommended 32 characters or more)
- Rotate keys regularly
- Do not hard-code keys in the code
- Use environment variables or a secure configuration management system
### 2. Permission Design
- Adhere to the **Least Privilege Principle**
- Use **fine-grained permission scopes**
- Regularly review and clean up unnecessary permissions
- Create different **AppClients** for different application scenarios
### 3. IP Restrictions
- Enable IP whitelist in the production environment
- Use CIDR ranges instead of individual IPs (for easier scaling)
- Consider CDN and proxy IP forwarding
### 4. Monitoring and Logging
```python
import logging
logger = logging.getLogger(__name__)
@router.get("/sensitive-data", auth=app_client_api_key)
@require_app_client_scopes([AppClientScopes.PROJECT_READ])
def get_sensitive_data(request):
app_client = request.auth
logger.info(f"AppClient {app_client.app_id} accessed sensitive data")
return {"data": "sensitive information"}
Integrate into Existing API
Adding Routes
Add the app route in config/apis.py:
from apps.app.apis import router as app_router
api.add_router('app', app_router)
Hybrid Authentication
This is a new functionality provided by django-ninja
It can support multiple authentication methods simultaneously within the same API:
from ninja.security import django_auth
from core.authentication import api_key_auth
Simultaneously Supporting Django User Authentication and AppClient Authentication
@router.get("/user-data", auth=[django_auth, api_key_auth]) def get_data(request): return {"user": request.user.username}
### Update Main API Configuration
In addition to this, you can configure global authentication and other features in the main API configuration.
```python
from django.contrib.admin.views.decorators import staff_member_required
api = NinjaAPI(
title=f'{settings.DJANGO_STARTER["project_info"]["name"]} APIs',
description=settings.DJANGO_STARTER["project_info"]["description"],
renderer=JSONRespRenderer(),
csrf=True,
auth=[django_auth, api_key_auth, api_key_header_auth, api_key_bearer_auth],
urls_namespace='api',
docs=Swagger(settings={"persistAuthorization": True}),
docs_decorator=staff_member_required,
)
Here is a brief introduction:
My configuration:
- Uses
auth=[django_auth, api_key_auth, api_key_header_auth, api_key_bearer_auth]to support multiple authentication methods (there is also a JWT one, but I haven't considered it here). - Uses
docs_decorator=staff_member_required,to configure that accessing the Swagger documentation requires a logged-instaff_member(职员成员).
Implementation
OK, finally, let me introduce the specific implementation (I doubt anyone has scrolled this far…)
Due to space constraints, this article cannot include all the code. Interested readers can reply with "API authentication (API鉴权)" in the background of our official account to get the complete code.
Model
First is the AppClient model, with its code located in src/apps/app/models.py.
In addition to field definitions, several methods have been added for functionalities such as ==getting the list of permission scopes== and ==getting the list of allowed IP addresses==.
According to DDD principles, this model can be considered a ==rich domain model== 😃
import uuid
from django.db import models
from django.core.validators import RegexValidator
from django_starter.db.models import ModelExt
class AppClient(ModelExt):
"""Application Client Model for managing third-party application access permissions"""
class Status(models.TextChoices):
ACTIVE = 'active', '启用'
INACTIVE = 'inactive', '禁用'
id = models.BigAutoField(
primary_key=True,
verbose_name='主键ID',
help_text='系统自动生成的唯一标识符',
db_comment='物理主键,自增ID'
)
guid = models.UUIDField(
default=uuid.uuid4,
unique=True,
editable=False,
verbose_name='全局唯一标识',
help_text='系统自动生成的UUID,用作逻辑主键',
db_comment='逻辑主键,GUID'
)
app_id = models.CharField(
max_length=100,
unique=True,
verbose_name='应用标识',
help_text='用于标识调用方系统的唯一ID,建议使用有意义的命名',
db_comment='应用标识,用于标识调用方系统',
validators=[
RegexValidator(
regex=r'^[a-zA-Z0-9_-]+$',
message='应用标识只能包含字母、数字、下划线和连字符'
)
]
)
app_name = models.CharField(
max_length=200,
verbose_name='应用名称',
help_text='调用方应用的显示名称或备注信息',
db_comment='应用名称,调用方的名称或备注信息'
)
secret_key = models.CharField(
max_length=100,
verbose_name='密钥',
help_text='用于API签名验证的密钥,请妥善保管',
db_comment='密钥,用于签名验证',
)
allowed_ips = models.TextField(
blank=True,
null=True,
verbose_name='允许访问的IP',
help_text='允许访问的IP地址列表,多个IP用逗号分隔,留空表示不限制',
db_comment='允许访问的 IP 列表,逗号分隔'
)
scopes = models.TextField(
blank=True,
null=True,
verbose_name='权限范围',
help_text='应用的权限范围,格式如:project:read,project:write',
db_comment='权限范围,如 project:read,project:write'
)
status = models.CharField(
max_length=10,
choices=Status.choices,
default=Status.ACTIVE,
verbose_name='状态',
help_text='应用的启用状态',
db_comment='启用/禁用状态'
)
class Meta:
db_table = 'app_client'
verbose_name = '应用客户端'
verbose_name_plural = '应用客户端'
ordering = ['-create_time']
indexes = [
models.Index(fields=['app_id']),
models.Index(fields=['status']),
models.Index(fields=['create_time']),
]
def __str__(self):
return f'{self.app_name} ({self.app_id})'
def is_active(self):
"""Check if the application is in an active state"""
# todo 为什么这个 is_deleted 会变成 str 啊??
# return self.status == self.Status.ACTIVE and not self.is_deleted
return self.status == self.Status.ACTIVE
def get_scopes_list(self):
"""Get the list of permission scopes"""
if not self.scopes:
return []
return [scope.strip() for scope in self.scopes.split(',') if scope.strip()]
def get_allowed_ips_list(self):
"""Get the list of allowed IP addresses"""
if not self.allowed_ips:
return []
return [ip.strip() for ip in self.allowed_ips.split(',') if ip.strip()]
def save(self, *args, **kwargs):
"""Override save method to add custom logic"""
# Ensure app_name is not empty
if not self.app_name:
self.app_name = self.app_id
super().save(*args, **kwargs)
Test Interfaces
Add some test interfaces for easier debugging later
The code is located in src/apps/app/apis/client/apis.py
from typing import List
from ninja import Router
from django.http import HttpRequest
from core.authentication import (
api_key_auth,
api_key_header_auth,
api_key_bearer_auth,
require_app_client_scopes,
AppClientScopes,
AppClientScopeChecker,
)
from apps.app.models import AppClient
from .schemas import AppClientSchema, AppClientCreateSchema
router = Router(tags=['AppClient API'])
@router.get("/test/api-key", auth=api_key_auth)
def test_api_key_auth(request: HttpRequest):
"""测试API Key认证(查询参数方式)
使用方式: GET /api/app/test/api-key?api_key=your_secret_key
"""
app_client = request.auth
return {
"message": "API Key认证成功",
"app_id": app_client.app_id,
"app_name": app_client.app_name,
"scopes": app_client.get_scopes_list(),
}
@router.get("/test/api-key-header", auth=api_key_header_auth)
def test_api_key_header_auth(request: HttpRequest):
"""测试API Key认证(请求头方式)
使用方式: GET /api/app/test/api-key-header
Headers: X-API-Key: your_secret_key
"""
app_client = request.auth
return {
"message": "API Key Header认证成功",
"app_id": app_client.app_id,
"app_name": app_client.app_name,
"scopes": app_client.get_scopes_list(),
}
@router.get("/test/bearer", auth=api_key_bearer_auth)
def test_bearer_auth(request: HttpRequest):
"""测试Bearer Token认证
使用方式: GET /api/app/test/bearer
Headers: Authorization: Bearer your_secret_key
"""
app_client = request.auth
return {
"message": "Bearer Token认证成功",
"app_id": app_client.app_id,
"app_name": app_client.app_name,
"scopes": app_client.get_scopes_list(),
}
@router.get("/test/scopes/project-read", auth=api_key_auth)
@require_app_client_scopes([AppClientScopes.PROJECT_READ])
def test_project_read_scope(request: HttpRequest):
"""测试项目读取权限
需要权限: project:read
"""
return {
"message": "项目读取权限验证成功",
"required_scope": AppClientScopes.PROJECT_READ,
"client_scopes": AppClientScopeChecker.get_client_scopes(request),
}
@router.get("/test/scopes/project-write", auth=api_key_auth)
@require_app_client_scopes([AppClientScopes.PROJECT_WRITE])
def test_project_write_scope(request: HttpRequest):
"""测试项目写入权限
需要权限: project:write
"""
return {
"message": "项目写入权限验证成功",
"required_scope": AppClientScopes.PROJECT_WRITE,
"client_scopes": AppClientScopeChecker.get_client_scopes(request),
}
@router.get("/test/scopes/multiple", auth=api_key_auth)
@require_app_client_scopes([AppClientScopes.PROJECT_READ, AppClientScopes.USER_READ])
def test_multiple_scopes(request: HttpRequest):
"""测试多个权限要求
需要权限: project:read AND user:read
"""
return {
"message": "多权限验证成功",
"required_scopes": [AppClientScopes.PROJECT_READ, AppClientScopes.USER_READ],
"client_scopes": AppClientScopeChecker.get_client_scopes(request),
}
@router.get("/test/scopes/any", auth=api_key_auth)
def test_any_scope(request: HttpRequest):
"""测试任意权限检查
检查是否具有项目或用户相关的任意权限
"""
has_project_access = AppClientScopeChecker.has_any_scope(
request, [AppClientScopes.PROJECT_READ, AppClientScopes.PROJECT_WRITE]
)
has_user_access = AppClientScopeChecker.has_any_scope(
request, [AppClientScopes.USER_READ, AppClientScopes.USER_WRITE]
)
return {
"message": "权限检查完成",
"has_project_access": has_project_access,
"has_user_access": has_user_access,
"client_scopes": AppClientScopeChecker.get_client_scopes(request),
}
@router.get("/info", auth=api_key_auth)
def get_client_info(request: HttpRequest):
"""获取当前认证的AppClient信息"""
app_client = request.auth
return {
"app_id": app_client.app_id,
"app_name": app_client.app_name,
"status": app_client.status,
"scopes": app_client.get_scopes_list(),
"allowed_ips": app_client.get_allowed_ips_list(),
"create_time": app_client.create_time,
}
@router.get("/clients", auth=api_key_auth)
@require_app_client_scopes([AppClientScopes.SUPER_ADMIN])
def list_app_clients(request: HttpRequest) -> List[dict]:
"""列出所有AppClient(需要超级管理员权限)
需要权限: *
"""
clients = AppClient.objects.filter(is_deleted=False)
return [
{
"app_id": client.app_id,
"app_name": client.app_name,
"status": client.status,
"scopes": client.get_scopes_list(),
"create_time": client.create_time,
}
for client in clients
]
Management Commands
For convenience, I've added two more commands
As the name suggests, ==Create a new AppClient== and ==List all==
src/apps/app/management/commands/create_app_client.pysrc/apps/app/management/commands/list_app_clients.py
Due to space limitations, I can't include all the code here. If you're interested, you can reply with "API Authentication (API鉴权)" in the backend of the official account to get the complete code.
Core Logic
Regarding the core logic of authentication, I have created a separate core package to store it.
The directory structure is as follows:
core
├─ exceptions
│ ├─ __init__.py
│ └─ permissions.py
├─ authentication
│ ├─ __init__.py
│ ├─ README.md
│ ├─ permissions.py
│ ├─ app.py
│ └─ api_key.py
└─ __init__.py
The most critical code consists of these authentication instances in src/core/authentication/app.py.
The code contains detailed comments, making it easy to understand at a glance.
If you find this still a bit complex, I will later introduce an extremely simple implementation.
class AppClientAuthMixin:
"""AppClient authentication mixin class, providing common authentication logic"""
def _get_client_ip(self, request: HttpRequest) -> str:
"""Get the client's real IP address"""
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0].strip()
else:
ip = request.META.get('REMOTE_ADDR')
return ip
def _validate_ip_access(self, app_client: AppClient, request: HttpRequest) -> bool:
"""Validate IP access permissions"""
allowed_ips = app_client.get_allowed_ips_list()
if not allowed_ips: # If no IP restrictions are set, allow all IPs
return True
client_ip = self._get_client_ip(request)
for allowed_ip in allowed_ips:
try:
# Support single IPs and CIDR ranges
if '/' in allowed_ip:
network = ipaddress.ip_network(allowed_ip, strict=False)
if ipaddress.ip_address(client_ip) in network:
return True
else:
if client_ip == allowed_ip:
return True
except (ipaddress.AddressValueError, ValueError):
# Ignore invalid IP formats
continue
return False
def _authenticate_app_client(self, request: HttpRequest, key: str) -> Optional[AppClient]:
"""General logic for authenticating an AppClient"""
try:
app_client = AppClient.objects.get(secret_key=key)
# Check if the application is active
if not app_client.is_active():
return None
# Validate IP access permissions
if not self._validate_ip_access(app_client, request):
return None
# Add permission scopes to the request for subsequent permission checks
request.app_client_scopes = app_client.get_scopes_list()
return app_client
except AppClient.DoesNotExist:
return None
class AppClientApiKey(AppClientAuthMixin, APIKeyQuery):
"""AppClient API Key authentication based on query parameters"""
param_name = "api_key"
def authenticate(self, request: HttpRequest, key: str) -> Optional[AppClient]:
return self._authenticate_app_client(request, key)
class AppClientApiKeyHeader(AppClientAuthMixin, APIKeyHeader):
"""AppClient API Key authentication based on request headers"""
param_name = "X-API-Key"
def authenticate(self, request: HttpRequest, key: str) -> Optional[AppClient]:
return self._authenticate_app_client(request, key)
class AppClientBearer(AppClientAuthMixin, HttpBearer):
"""AppClient authentication based on Bearer Token"""
def authenticate(self, request: HttpRequest, token: str) -> Optional[AppClient]:
return self._authenticate_app_client(request, token)
Create authentication instances, available for direct use in the API
api_key_auth = AppClientApiKey() api_key_header_auth = AppClientApiKeyHeader() api_key_bearer_auth = AppClientBearer()
## Minimal Implementation
This complete authentication logic may seem overly complex to you
No worries, I've also created a minimal version. It removes permissions, IP restrictions and other extraneous elements, only validating whether the api-key is valid.
The implementation code is in `src/core/authentication/api_key.py`
The custom `ApiKey` class inherits from `ninja.security.APIKeyQuery` class, indicating that the `api_key` parameter needs to be included in the **GET query params**.
```python
from ninja.security import APIKeyQuery
from apps.app.models import AppClient
class ApiKey(APIKeyQuery):
param_name = "api_key"
def authenticate(self, request, key):
try:
c = AppClient.objects.get(secret_key=key)
print(f'api key authenticated: {c.secret_key}, app id: {c.app_id}')
return c
except AppClient.DoesNotExist:
pass
Usage is also straightforward:
from apps.app.models import AppClient
from core.authentication.api_key import ApiKey
@router.get("/data", auth=ApiKey())
def get_data(request):
assert isinstance(request.auth, AppClient)
return {"data": "success"}
When making client requests, include the secret_key of the AppClient in the ==GET query params==.
If authentication succeeds, ninja will automatically add the AppClient instance to request.auth, allowing you to directly access this instance in the endpoint code.
Summary
This article actually ended up over 20,000 words long 🕶
And this is after removing a lot of code...
One more time for those interested: you can reply with API Authentication in the back end of our public account to get the complete code.
Such a long article, probably no one will have the patience to read all the way to the end, right~
Peace out, peace out~
_EOF_
Implementing an application authentication mechanism similar to Tencent or Alibaba Cloud in django-ninja.
- 本文作者:
- 程序设计实验室
- 版权声明:
- 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
相关文章
轻松搞定Django分页功能
view代码 from django.core.paginator import Paginator def announcement(request): ctx = { 'global': GlobalCtx, 'announcements': models.Announcement.objects.all(), } paginator
2022-05-27 · Django · 约 1 分钟
DjangoRestFramework框架三种分页功能的实现 - 在DjangoStarter项目模板中封装
前言 继续Django后端开发系列文章。刚好遇到一个分页的需求,就记录一下。 Django作为一个“全家桶”型的框架,本身啥都有,分页组件也是有的,但默认的分页组件没有对API开发做优化,所以DjangoRestFramework这个专门写API的框架又把Django的分页组件包装了一层,集成在viewsets里的时候会更方便。 不过我们不可能一直用viewsets,有一部分API还是要用自由度更
2022-08-26 · Django · 约 1 分钟
使用Django-Channels实现websocket通信+大模型对话
这个只是简单的demo,实际上生产还得考虑很多问题,本文就是为 channels 的应用开了个头,后续有新的研究成果会持续更新博客~
2024-08-14 · Django · 约 1 分钟
Django快速开发实践:Drf框架和xadmin配置指北
步骤 既然是快速开发,那废话不多说,直接说步骤: 安装Djagno 安装Django Rest Framework 定义models 定义Rest framework的serializers 定义Rest framework的viewsets 配置Rest framework的router 配置管理后台admin 根据需要写template和对应的view 经过这些步骤就能得到一个具备完整的后端接
2022-09-06 · Django · 约 1 分钟
讨论
共 0 条加载评论中…