ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Oscar 2.0.1 发布解读:仪表盘权限校验、商品查询性能重构与优惠日期显示修复

Oscar 2.0.1 发布解读:仪表盘权限校验、商品查询性能重构与优惠日期显示修复 后端电商【免费下载链接】django-oscarDomain-driven e-commerce for Django项目地址https://gitcode.com/gh_mirrors/dj/django-oscar点击查看免费下载Oscar 2.0.1 是 django-oscar 2.0 系列的首个 bugfix 版本发布于 2019-08-09集中修复了三个直接影响生产使用的问题fork 化仪表盘应用dashboard app下的导航权限校验、商品列表查询中选项Option统计带来的数据库性能开销以及仪表盘优惠列表页开始/结束日期的显示缺陷。读完本文你将掌握这三个修复点的底层实现原理、对应的源码位置与测试用例并能据此在自建 Oscar 项目中验证与跟进修复。版本背景2.0 系列的第一个补丁版Oscar 2.0 是对 django-oscar 的一次大版本升级而 2.0.1 作为紧随其后的补丁版不引入新功能只做定向修复。官方发布说明docs/source/releases/v2.0.1.rst将全部改动归入 Bug fixes 一节共三项修复oscar.dashboard.nav.default_access_fn使其能正确检查 fork 化仪表盘应用上配置的权限重构ProductQuerySet.base_queryset()用布尔注解取代计数注解消除检查产品选项时的昂贵查询修复仪表盘优惠offer列表模板中开始/结束日期的显示。对于升级到 2.0 系列的用户这三项修复分别对应权限安全、列表性能、后台可用性三个维度值得逐一理解其实现。修复一fork 化仪表盘应用的导航权限校验问题本质权限从哪个 AppConfig 读取Oscar 的仪表盘导航由 src/oscar/apps/dashboard/nav.py 中的default_access_fn驱动。该函数接收用户与 URL 名称返回该用户是否有权访问对应导航项def default_access_fn(user, url_name, url_argsNone, url_kwargsNone): ... if url_name is None: # its a heading return True url reverse(url_name, argsurl_args, kwargsurl_kwargs) url_match resolve(url) url_name url_match.url_name app_config_instance _dashboard_url_names_to_config()[url_name] permissions app_config_instance.get_permissions(url_name) return check_permissions(user, permissions)其核心逻辑分三步先用reverseresolve将 URL 名称解析为实际路由再通过_dashboard_url_names_to_config()找到该路由所属的仪表盘AppConfig最后读取该 config 上配置的权限并交给check_permissions来自 src/oscar/views/decorators.py判定。2.0.1 修复的问题恰恰出在第二步当开发者按 Oscar 的 fork 机制参见 docs/source/topics/fork_app.rstfork 了某个 dashboard 子应用如oscar.apps.dashboard.offers并覆写权限时旧实现可能没有从 fork 后的AppConfig上取权限导致新配置的权限不生效。修复后权限查找严格绑定到实际承载该 URL 的AppConfig实例。_dashboard_url_names_to_configURL 名称到配置的映射支撑这一查找的是带lru_cache缓存的辅助函数lru_cache(maxsize1) def _dashboard_url_names_to_config(): dashboard_configs ( config for config in apps.get_app_configs() if isinstance(config, OscarDashboardConfig) ) urls_to_config {} for config in dashboard_configs: for url in config.urls[0]: name getattr(url, name, None) if not name: continue if name in urls_to_config: if urls_to_config[name] ! config: raise ImproperlyConfigured( {} exists in both {} and {}!.format(name, config, urls_to_config[name]) ) urls_to_config[name] config return urls_to_config它遍历所有已加载的、继承自OscarDashboardConfig定义于 src/oscar/core/application.py的应用配置把每个 URL 名称映射到其所属 config。由于apps.get_app_configs()返回的是 Django 应用注册表中 fork 之后的最终版本因此只要 fork 后的子类被正确注册INSTALLED_APPS指向 fork 后的路径导航权限就会从 fork 后的 config 上读取——这正是本次修复生效的关键前提。若两个 dashboard 应用注册了同名 URL 名称该函数会抛出ImproperlyConfigured避免权限归属歧义。测试佐证tests/integration/dashboard/test_nav.py 为default_access_fn提供了四组行为契约无 URL 名称导航标题项直接放行test_default_access_fn_no_url_name断言返回True员工用户可访问dashboard:indextest_default_access_fn_staff断言为True非员工用户被拒绝test_default_access_fn_non_staff_user断言为False无效或非仪表盘 URL 名称分别抛出NoReverseMatch与KeyErrortest_default_access_fn_invalid_url_name、test_default_access_non_dashboard_url_name。同文件中的DashboardNavTestCase进一步验证get_nodes菜单节点生成对员工与非员工用户的差异输出。这些用例共同保证了修复后的权限判定在常规与异常路径上都行为可预期。修复二base_queryset()从计数注解到存在性注解重构动机Count 子查询的代价商品列表页、搜索结果页普遍依赖ProductQuerySet.base_queryset()一次性预取常用关联数据。在 2.0.1 之前该方法会用Count分别统计商品类product class和商品自身关联的选项数量并把结果注解为num_product_class_options、num_product_options两个字段。由于Count需要实际聚合每一行的关联行数在选项数量较多、列表页行数较大的场景下会产生昂贵的子查询与分组开销——而多数业务代码真正关心的只是有没有选项这一个布尔事实。修复后的实现Exists子查询修复后的实现位于 src/oscar/apps/catalogue/managers.py 的ProductQuerySet.base_queryset()def base_queryset(self): ... Option get_model(catalogue, Option) product_class_options Option.objects.filter( productclassOuterRef(product_class) ) product_options Option.objects.filter(productOuterRef(pk)) return ( self.select_related(product_class) .annotate( has_product_class_optionsExists(product_class_options), has_product_optionsExists(product_options), ) .prefetch_related( children, product_options, product_class__options, stockrecords, images, ) )改动要点注解语义变更num_product_class_options/num_product_options计数→has_product_class_options/has_product_options布尔存在性。发布说明明确指出新注解分别表示商品类是否关联选项与商品自身是否关联选项。查询策略变更Count聚合改为Exists相关子查询配合OuterRef。数据库执行EXISTS时一旦找到首条匹配记录即可短路返回不再需要扫描并统计全部匹配行这正是性能收益的来源。预取链保持不变select_related(product_class)与对children、product_options、product_class__options、stockrecords、images的prefetch_related均保留继续发挥列表页 N1 查询的兜底作用。兼容性提醒依赖旧注解的代码需同步这是本次修复中唯一带 API 语义变化的改动。如果自建项目中存在依赖num_product_class_options/num_product_options注解的查询或模板升级到 2.0.1 后需改为使用新的has_*布尔注解反过来只关心是否有选项的既有调用可以无痛迁移且能直接受益于性能提升。测试佐证tests/integration/catalogue/test_options.py 完整覆盖了新旧行为test_product_has_options_per_product_class/test_product_has_options_per_product分别验证商品类选项与商品自身选项会反映到Product.has_optionstest_queryset_per_product_class商品类挂选项后base_queryset()查询出的商品has_options与has_product_class_options均为Truetest_queryset_per_product商品自身挂选项后has_options与has_product_options均为Truetest_queryset_both商品类与商品同时挂选项时options属性合并返回且不重复count() 1随后再追加两个选项验证即时可见性count()变为 2、3。这套用例同时守护了注解语义与options 合并去重两条行为契约可作为升级回归测试的参考。修复三仪表盘优惠列表的日期显示问题与修复第三个修复针对仪表盘优惠列表模板开始日期start datetime与结束日期end datetime列原先在某些情况下显示异常。修复后的模板 src/oscar/templates/oscar/dashboard/offers/offer_list.html 中两列分别以start_datetime、end_datetime为排序键渲染表头并在空值时显示占位符-th{% anchor start_datetime _(Start date) %}/th th{% anchor end_datetime _(End date) %}/th ... td{{ offer.start_datetime|default:- }}/td td{{ offer.end_datetime|default:- }}/td{% anchor %}来自 src/oscar/templatetags/sorting_tags.py负责生成可点击的排序链接|default:-过滤器则保证未设置起止时间的优惠数据库中允许为NULL以-呈现而非空白。日期字段在数据流中的角色该模板由 src/oscar/apps/dashboard/offers/views.py 的列表视图渲染template_name oscar/dashboard/offers/offer_list.html其查询逻辑与日期字段直接相关(Q(start_datetime__ltenow) | Q(start_datetime__isnullTrue)) (Q(end_datetime__gtenow) | Q(end_datetime__isnullTrue)) # 进行中 Q(start_datetime__gtnow) | Q(end_datetime__ltnow) # 已结束/未开始即是否可用的状态判定完全依赖start_datetime/end_datetime与当前时刻的比较且两字段均允许为空视为不限时间。因此列表页中这两个日期列既是运营人员判断优惠有效期的直接入口也是视图层状态分组的依据其显示修复对后台可用性意义直接。对应的表单字段定义在 src/oscar/apps/dashboard/offers/forms.pystart_datetime与end_datetime均为forms.DateTimeField新建优惠时开始时间默认预填当前时刻self.fields[start_datetime].initial timezone.now()创建时还会校验开始时间必须早于结束时间。理解这一表单-视图-模板链路有助于在 fork 优惠应用时保持日期行为的完整一致性。升级与验证建议对于运行 2.0 系列的 Oscar 项目升级 2.0.1 时建议按以下顺序验证权限链路若 fork 过任何 dashboard 子应用并自定义了get_permissions升级后重点回归仪表盘各导航项的可见性可复用 tests/integration/dashboard/test_nav.py 的思路补充针对 fork 应用的用例。商品查询全库搜索num_product_class_options、num_product_options的引用模板、视图、自定义查询替换为has_product_class_options、has_product_options随后对商品列表页做一次查询计数对比如 Django Debug Toolbar确认EXISTS子查询生效。优惠列表打开仪表盘优惠列表页检查包含未设置起止时间的优惠在内Start date/End date两列均正确显示并可排序。完整改动清单与版本说明可查阅 docs/source/releases/v2.0.1.rst相关实现与测试分布在 src/oscar/apps/dashboard/nav.py、src/oscar/apps/catalogue/managers.py、src/oscar/templates/oscar/dashboard/offers/offer_list.html 及其配套测试中可作为进一步阅读的起点。赞分享后端电商【免费下载链接】django-oscarDomain-driven e-commerce for Django项目地址https://gitcode.com/gh_mirrors/dj/django-oscar点击查看免费下载相关推荐PDF补丁丁免费开源的PDF全能工具箱轻松编辑合并提取PDF文档PDF补丁丁免费开源的PDF全能工具箱轻松编辑合并提取PDF文档 PDF补丁丁是一款功能强大的开源PDF工具箱专为处理各种PDF文档需求而设计。无论你是需桌面应用文档RabbitMQ 3.12.8 维护版发布解读核心修复、Raft 配置上限校验与 Shovel 启动性能优化RabbitMQ 3.12.8 维护版发布解读核心修复、Raft 配置上限校验与 Shovel 启动性能优化 RabbitMQ 3.12.8 是 3.12.x后端消息队列消息路由Vitess v18.0.5 发布说明查询路由修复、VReplication 健康检查与性能优化全解读Vitess v18.0.5 发布说明查询路由修复、VReplication 健康检查与性能优化全解读 导读 本文基于 Vitess 官方仓库中 change数据库分布式数据库云原生后端数据存储上一篇FidelityFX FSR源码剖析边缘自适应上采样与锐化的工程实现下一篇JetBrains CC GUI插件性能优化解决流式响应重复问题的完整方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进