mongodb 配置django确实有点麻烦

Django 与 MongoDB 的主要冲突点

1. ORM 理念的根本不同

python
# Django ORM(关系型思维)
class User(models.Model):
    username = models.CharField(max_length=100)
    email = models.EmailField(unique=True)
    # 外键关系 - MongoDB 不需要这个!
    profile = models.ForeignKey('Profile', on_delete=models.CASCADE)

# MongoDB 文档模型(嵌套文档思维)
{
    "_id": ObjectId("..."),
    "username": "john",
    "email": "john@example.com",
    "profile": {  # 直接嵌套,不需要外键
        "bio": "...",
        "avatar": "..."
    },
    "posts": [  # 数组嵌套
        {"title": "...", "content": "..."}
    ]
}

2. 官方支持的缺失

3. 配置麻烦示例

python
# settings.py - 使用 Djongo 的配置示例
DATABASES = {
    'default': {
        'ENGINE': 'djongo',
        'NAME': 'mydb',
        'CLIENT': {
            'host': 'mongodb://localhost:27017',
            'username': 'root',
            'password': 'password',
            'authSource': 'admin',
            'authMechanism': 'SCRAM-SHA-1'
        }
    }
}

# 问题1:Django Admin 无法正常工作
# 问题2:ManyToMany 关系无法使用
# 问题3:聚合查询受限
# 问题4:事务支持有限(需要 MongoDB 4.0+)

4. Django Admin 的尴尬

Django Admin 是为关系型数据库设计的:

python
# admin.py
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    list_display = ['name', 'price']
    # 以下功能在 MongoDB 中可能无法正常工作:
    # - 外键下拉选择
    # - ManyToMany 字段
    # - 跨表搜索
    # - 外键自动完成

5. 查询语法的扭曲

python
# Django ORM 查询(被翻译成 MongoDB 查询)
products = Product.objects.filter(
    price__gte=100,
    category__name='Electronics'  # 这个 join 会被转换成 $lookup,性能可能很差
).order_by('-created_at')

# 实际上 MongoDB 更适合这样查询:
# 应该使用嵌入式文档或应用层 join

6. 迁移系统的困扰

python
# Django 的迁移系统是为关系型数据库设计的
python manage.py makemigrations
python manage.py migrate

# 在 MongoDB 中:
# - 不需要 ALTER TABLE
# - 不需要添加外键约束
# - 不需要索引迁移(Django 的迁移可能不适用)

FastAPI + MongoDB 的优势对比

1. 配置简单直接

python
# FastAPI + Motor(异步驱动)
from motor.motor_asyncio import AsyncIOMotorClient

client = AsyncIOMotorClient("mongodb://localhost:27017")
db = client.mydatabase

# 就这么简单!不需要复杂的 settings.py 配置

2. 数据模型灵活

python
# FastAPI + Pydantic
from pydantic import BaseModel, Field
from typing import Optional, List
from datetime import datetime

class Product(BaseModel):
    id: Optional[str] = Field(None, alias="_id")
    name: str
    price: float
    # MongoDB 风格的数据结构
    attributes: dict = {}  # 任意键值对
    tags: List[str] = []
    reviews: List[dict] = []  # 嵌套评论数组
    created_at: datetime = Field(default_factory=datetime.now)
    
    class Config:
        allow_population_by_field_name = True  # 允许使用 _id 而不是 id

3. 查询直接高效

python
# FastAPI + MongoDB 原生查询
from bson import ObjectId

# 简单查询
product = await db.products.find_one({"name": "Laptop"})

# 聚合管道 - MongoDB 的强项
pipeline = [
    {"$match": {"category": "Electronics"}},
    {"$group": {"_id": "$brand", "avg_price": {"$avg": "$price"}}},
    {"$sort": {"avg_price": -1}}
]
results = await db.products.aggregate(pipeline).to_list(100)

# 地理空间查询
nearby_stores = await db.stores.find({
    "location": {
        "$near": {
            "$geometry": {
                "type": "Point",
                "coordinates": [longitude, latitude]
            },
            "$maxDistance": 5000
        }
    }
}).to_list(50)

4. 完整的异步支持

python
# FastAPI 的异步端点
@app.get("/products/{product_id}/recommendations")
async def get_recommendations(product_id: str):
    # 并行执行多个 MongoDB 查询
    product_task = db.products.find_one({"_id": ObjectId(product_id)})
    similar_task = db.products.find({
        "category": product["category"],
        "_id": {"$ne": ObjectId(product_id)}
    }).limit(10).to_list()
    
    # 使用 asyncio.gather 并行执行
    product, similar_products = await asyncio.gather(product_task, similar_task)
    
    return {
        "product": product,
        "recommendations": similar_products
    }

5. 文档数据库的自然使用方式

python
# 充分利用 MongoDB 的文档特性

# 1. 嵌入式文档
class Order(BaseModel):
    items: List[dict]  # 整个订单在一个文档中
    shipping_address: dict
    billing_address: dict
    # 不需要关联查询!

# 2. 数组操作
await db.products.update_one(
    {"_id": ObjectId(product_id)},
    {"$push": {"reviews": review_data}}  # 直接推入数组
)

# 3. 部分更新
await db.users.update_one(
    {"_id": ObjectId(user_id)},
    {"$set": {"profile.bio": new_bio}}  # 更新嵌套字段
)

迁移建议

如果你已经有 Django 项目想改用 MongoDB,可以考虑:

方案1:混合架构(推荐)

text
┌─────────────────────────────────────────┐
│           Django (管理后台)              │
│  - Django Admin(复杂后台管理)          │
│  - 用户认证、权限管理                   │
│  - 使用 PostgreSQL 或 MySQL             │
├─────────────────────────────────────────┤
│           FastAPI (业务API)             │
│  - 主业务逻辑、移动端API                │
│  - 高性能、异步处理                     │
│  - 使用 MongoDB(存储业务数据)          │
└─────────────────────────────────────────┘

方案2:Django REST Framework + MongoDB(不推荐)

python
# 使用 MongoEngine 作为 Django 的 ODM
INSTALLED_APPS = [
    'mongoengine.django.mongo_auth',  # 替换 Django 的 auth
    # ...
]

# 但会有很多限制,不推荐生产环境使用

方案3:完全迁移到 FastAPI

python
# 逐步迁移步骤:
# 1. 先用 FastAPI 实现新功能
# 2. 逐步将 Django 视图改为 API 代理
# 3. 最终完全替换 Django

总结

如果你的项目已经确定使用 MongoDB,那么远离 Django 是明智的选择!

Django 与 MongoDB 的组合就像:

FastAPI + MongoDB 的优势:

  1. ✅ 配置简单直接

  2. ✅ 异步原生支持

  3. ✅ 数据模型更自然

  4. ✅ 查询性能更好

  5. ✅ 自动文档生成

  6. ✅ 现代开发体验

Django + MongoDB 的痛点:

  1. ❌ 配置复杂且 hacky

  2. ❌ 同步模式限制性能

  3. ❌ ORM 与文档模型不匹配

  4. ❌ 许多功能无法使用

  5. ❌ 社区支持有限

  6. ❌ 学习曲线反而更陡

给你的建议: 坚持选择 FastAPI!它不仅让你与 MongoDB 的合作更愉快,还能让你享受到现代 Python 开发的诸多好处。如果需要 Django Admin 那样的后台管理,可以考虑: