Django4 QuerySet不返回最新数据 需重启服务如何解决
更新房产(House)属性时出现异常:将State字段修改为Illinois后,更新值无法在如下查询结果中正常展示:
all_properties = models.Property.objects.all()
即便新增房产记录,重载UpdateView后all_properties的查询结果依然不包含新数据,只有终止并重启Django服务才能查询到最新写入的数据。需要找到配置方法让UpdateView每次加载时都展示最新数据。
定义的房产模型包含20余个属性,用于搭建类Airbnb的房产listing站点,模型字段覆盖面积、卧室数、城市、州等信息。
编辑房产属性时需要为文本输入框实现自动补全功能:例如表单中State是文本字段,新增第二套房产时,State输入框需要基于系统中已存的房产数据给出补全建议——输入C时,若数据库中存在State为California的房产,就需要展示该选项。
使用UpdateView实现房产编辑页,需要将所有自动补全字段数据传入视图,供前端文本框调用,视图代码如下:
class PropertyUpdateView(LoginRequiredMixin, UpdateView): context_object_name = 'property' model = models.Property form_class = forms.PropertyForm template_name = 'desertland/admin/property_update.html' extra_context = get_autocomplete_fields() def get_success_url(self): messages.success(self.request, 'The property was updated successfully.') return reverse('property_update', kwargs={'pk': self.object.id})
自动补全数据通过extra_context传入,对应的get_autocomplete_fields()方法实现如下:
def get_autocomplete_fields(): ac_keys = ['state', 'city', 'county', 'zip_code', 'zoning', 'power', 'water_district', 'water', 'access', 'vetting'] autocomplete_data = {} temp_list = {} all_properties = models.Property.objects.all() # 为自动补全字段初始化空字典 for key in ac_keys: temp_list[key] = [] autocomplete_data[key] = [] for property in all_properties: for key in ac_keys: # 不存在对应key时初始化 if autocomplete_data.get(key) is None: autocomplete_data[key] = [] val = getattr(property, key) # 从房产数据中提取去重后的值生成数组 if val and val not in temp_list[key]: temp_list[key].append(val) autocomplete_data[key].append({'value': val, 'label': val}) return {'autocomplete_data': autocomplete_data}
该函数逻辑正常,可正确生成符合要求的字典数据。
数据库模型定义如下:
class Property(models.Model): id = models.AutoField(primary_key=True) slug = models.SlugField(unique=True, db_index=True) name = models.TextField(null=False, blank=False) ... 其余属性 ... def __str__(self): return self.name def save(self, *args, **kwargs): self.slug = slugify(self.name) super(Property, self).save(*args, **kwargs) def get_absolute_url(self): return reverse("property_update", kwargs={'id': self.pk})
发布问题前已完成的排查项:
- 数据更新后会立即写入数据库
- 通过
ListView或UpdateView访问单条对象时,可正常展示更新后的值(输入框、数据列表中均可正常显示) - 只有通过
models.Property.objects.all()查询的结果,需要重启Django服务才会同步最新数据 - 新建对象后,
models.Property.objects.all()返回的结果计数保持不变,即便新对象已持久化到数据库也无法被查询到 - 项目未启用任何缓存框架
- 在settings.py中设置
TIMEOUT=0未产生任何效果
问题出在extra_context的赋值方式:把extra_context定义为类属性时,get_autocomplete_fields()会在Django服务启动、类加载时执行一次,查询结果直接固化在类属性里,后续所有请求复用的都是服务启动时生成的这份静态数据,根本不会重新查询数据库,自然拿不到更新、新增的内容,只有重启服务重新加载类才会重新执行查询。
删除类层面的extra_context = get_autocomplete_fields()赋值,重写类视图的get_context_data方法,在每次处理请求时实时查询最新的自动补全数据,修改后的视图代码如下:
class PropertyUpdateView(LoginRequiredMixin, UpdateView): context_object_name = 'property' model = models.Property form_class = forms.PropertyForm template_name = 'desertland/admin/property_update.html' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context.update(get_autocomplete_fields()) return context def get_success_url(self): messages.success(self.request, 'The property was updated successfully.') return reverse('property_update', kwargs={'pk': self.object.id})
当前get_autocomplete_fields的实现会把全量房产数据加载到内存中遍历去重,当房产数据量增大后性能会明显下降,可以直接用数据库层面的去重查询替代内存遍历,不需要把所有房产对象捞到内存里循环处理,示例逻辑如下:
def get_autocomplete_fields(): ac_keys = ['state', 'city', 'county', 'zip_code', 'zoning', 'power', 'water_district', 'water', 'access', 'vetting'] autocomplete_data = {} for key in ac_keys: # 直接在数据库层做去重、空值过滤 distinct_vals = models.Property.objects.exclude(**{f"{key}__isnull": True}) .exclude(**{key: ""}) .values_list(key, flat=True) .distinct() autocomplete_data[key] = [{"value": val, "label": val} for val in distinct_vals] return {'autocomplete_data': autocomplete_data}
修改后查询效率会有明显提升。
内容的提问来源于stack exchange,提问作者saurabhj

