Django类视图中如何设置GET与POST方法共用的属性
问题原因
Django类视图(CBV)的机制是每收到一次HTTP请求就会实例化一个全新的视图对象:GET请求和POST请求是两次完全独立的请求,对应两个互不关联的类实例。你在GET方法里给self.table赋值,只作用于GET请求对应的临时实例,POST请求到来时生成的新实例根本不存在这个属性,自然会抛错。setattr不生效也是同一个原因——你修改的是上一个请求的实例属性,和当前POST请求的实例没有任何关系。
实现方式
方案1:重写dispatch统一加载(单请求内复用首选)
dispatch是CBV的请求分发入口,所有GET/POST等请求方法执行前都会先经过这个方法。在这里统一拉取需要的表数据并挂载到实例属性上,后续不管走get还是post逻辑都能直接访问,且同一个请求内只会查询一次数据库,不会产生重复开销。
from django.utils.decorators import method_decorator from django.contrib.auth.decorators import login_required from django.views import View # 装饰器直接加在dispatch上,不用给get、post重复加 @method_decorator(login_required(login_url='login', redirect_field_name=None), name='dispatch') class MyClass(View): def dispatch(self, request, *args, **kwargs): # 统一拉取公共数据,整个请求周期内仅执行一次 con = create_engine('mysql+mysqlconnector://user:password@localhost:8000/schema') tabs = get_tables(con) self.target_table = tabs['table_That_I_need'] self.all_tabs = tabs return super().dispatch(request, *args, **kwargs) def get(self, request): # 直接访问实例属性即可,无需重复查库 context = {'data': self.all_tabs} return render(request, 'markowitz/markowitz.html', context) def post(self, request): # 直接拿提前加载好的表数据,不会报属性不存在的错误 form = MarkowitzForm(request.POST, self.target_table) if form.is_valid(): # 业务逻辑 pass return render(request, 'markowitz/results.html')
方案2:缓存公共数据(全局多请求复用首选)
如果需要的表数据是所有用户通用、更新频率极低的基础数据,没必要每次请求都查库,可以配合Django缓存机制存储,第一次查询后存入缓存,后续所有请求直接从缓存读取,性能更高。
from django.core.cache import cache from django.utils.decorators import method_decorator from django.contrib.auth.decorators import login_required from django.views import View @method_decorator(login_required(login_url='login', redirect_field_name=None), name='dispatch') class MyClass(View): def _get_target_table(self): # 优先读缓存 table_data = cache.get('target_db_table') if not table_data: # 缓存失效时再查库,查完写入缓存,超时时间根据数据更新频率设置即可 con = create_engine('mysql+mysqlconnector://user:password@localhost:8000/schema') tabs = get_tables(con) table_data = tabs['table_That_I_need'] cache.set('target_db_table', table_data, timeout=3600) return table_data def get(self, request): target_table = self._get_target_table() context = {'data': target_table} return render(request, 'markowitz/markowitz.html', context) def post(self, request): target_table = self._get_target_table() form = MarkowitzForm(request.POST, target_table) if form.is_valid(): # 业务逻辑 pass return render(request, 'markowitz/results.html')
优化提示
- 不建议在视图方法里频繁创建新的数据库连接,优先复用Django ORM或者数据库连接池,能进一步降低性能开销
- 类视图的方法装饰器统一加在dispatch方法上即可,无需给每个请求方法单独添加,减少重复代码
内容的提问来源于stack exchange,提问作者Pefori
相关产品推荐
相关产品推荐

