You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Django User模型中添加计算属性name并减少API调用

实现Django中User模型的计算属性name并优化外部API调用

要实现需求并避免不必要的API调用,核心思路是优先批量获取数据+本地缓存,下面分步骤落地:

1. 封装API调用工具函数(优化请求逻辑)

先写工具函数处理API请求,加入缓存和批量获取逻辑,彻底避免重复请求:

# users/utils.py
from django.core.cache import cache
import requests

# 批量获取所有用户名并缓存
def fetch_all_user_names():
    cache_key = "all_user_names"
    cached_names = cache.get(cache_key)
    if cached_names:
        return cached_names
    
    # 调用批量API
    try:
        resp = requests.get("https://your-api-domain/api/users/name", timeout=5)
        resp.raise_for_status()
        user_names = resp.json()
        # 缓存1小时,可根据业务调整过期时间
        cache.set(cache_key, user_names, 3600)
        return user_names
    except requests.exceptions.RequestException:
        return {}

# 获取单个用户名称(优先从批量缓存取,无则调用单接口并缓存)
def fetch_single_user_name(user_id):
    user_id_str = str(user_id)
    # 先尝试从批量缓存获取
    all_names = fetch_all_user_names()
    if user_id_str in all_names:
        return all_names[user_id_str]
    
    # 批量缓存无数据时,调用单用户API
    cache_key = f"user_name_{user_id}"
    cached_name = cache.get(cache_key)
    if cached_name:
        return cached_name
    
    try:
        resp = requests.get(f"https://your-api-domain/api/users/{user_id}/name", timeout=5)
        resp.raise_for_status()
        name = resp.json().get("name", "未知")
        cache.set(cache_key, name, 3600)
        return name
    except requests.exceptions.RequestException:
        return "未知"

2. 给User模型添加计算属性name

如果用默认Django User模型,通过代理模型扩展(不新增数据库表);如果是自定义User模型,直接加@property即可:

# users/models.py
from django.contrib.auth.models import User
from .utils import fetch_single_user_name

class CustomUser(User):
    class Meta:
        proxy = True

    @property
    def name(self):
        return fetch_single_user_name(self.id)

3. Admin页面配置:展示name列并避免重复API调用

Admin列表页如果直接用模型的name属性,会触发N次API请求(每个用户一次),所以提前批量获取数据:

# users/admin.py
from django.contrib import admin
from .models import CustomUser
from .utils import fetch_all_user_names

@admin.register(CustomUser)
class CustomUserAdmin(admin.ModelAdmin):
    list_display = ("id", "username", "email", "display_name")

    def get_queryset(self, request):
        # 查询用户列表前,提前批量获取所有用户名并缓存
        self._cached_user_names = fetch_all_user_names()
        return super().get_queryset(request)

    def display_name(self, obj):
        # 从提前缓存的批量数据中取值,无需再次调用API
        return self._cached_user_names.get(str(obj.id), "未知")
    
    display_name.short_description = "姓名"  # 列表页列名

4. 表单页面配置:将name设为只读字段

自定义ModelForm,把name设为只读字段,初始化时填充对应值:

# users/forms.py
from django import forms
from .models import CustomUser
from .utils import fetch_single_user_name

class UserUpdateForm(forms.ModelForm):
    # 定义只读的name字段
    name = forms.CharField(
        label="姓名",
        required=False,
        widget=forms.TextInput(attrs={"readonly": "readonly"})
    )

    class Meta:
        model = CustomUser
        fields = ("username", "email", "name", "is_active")  # 按需添加其他字段

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 编辑已有用户时,填充name字段值
        if self.instance.pk:
            self.fields["name"].initial = fetch_single_user_name(self.instance.pk)

在视图中使用该表单即可,示例:

# users/views.py
from django.shortcuts import get_object_or_404, render
from .models import CustomUser
from .forms import UserUpdateForm

def user_detail(request, user_id):
    user = get_object_or_404(CustomUser, pk=user_id)
    form = UserUpdateForm(instance=user)
    return render(request, "user_detail.html", {"form": form})

额外优化建议

  • 缓存失效:如果外部API的用户名称会更新,可在业务触发更新时(比如用户信息同步操作)主动清除相关缓存键
  • 异常兼容:API调用时加入超时和异常捕获,避免外部API故障导致页面崩溃
  • 批量分页:如果用户量极大,可考虑分页批量获取API数据,避免单次请求返回数据过多

内容的提问来源于stack exchange,提问作者user1427972

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 22:15:39