如何在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
相关产品推荐
相关产品推荐

