Django 怎么做同一个 url,对应不同的 view 函数?

2022-08-29 16:16:31 +08:00
 elboble

类似于那种定时抢购的逻辑。

时间没到之前是个介绍页面,时间到了之后进入真正的 view 处理函数。

触发条件不一定是时间,也可能是后台控制的。就是要做到程序不重启,同一 url 对应的 view 变了。

我是这样做的,加了个全局变量,然后在 url 对应个 view 函数里做判断,后台改变这个全局变量,执行不同的 view 函数。

IS_RELEASE 是全局变量,entrance 函数是入口函数,release 函数是改变入口的视图函数。

IS_RELEASE = False

def entrance(request):
  global IS_RELEASE
  if IS_RELEASE:
   return index2022(request)
  else:
  return index2(request)

@login_required
def release(request):
global IS_RELEASE
if IS_RELEASE:
IS_RELEASE = False
else:
IS_RELEASE = True
return redirect("/conf/")

这个方法在本地调试没问题,但是上到服务器后,出现执行 release 函数后 IS_RELEASE 没按预期改变,或者有秒级的延迟,我猜是不是有 cdn 的影响,但是这个都应该是动态页面,每个请求都要回服务器处理的。

2524 次点击
所在节点    Django
14 条回复
676529483
2022-08-29 16:22:18 +08:00
你服务器肯定不是 runserver 启动的吧?走 gunicorn 、uwsgi 等等,都是多进程的,全局变量只是进程唯一的,可以改成从一个地方去拿,比如 redis 、mysql
elboble
2022-08-29 16:24:20 +08:00
@676529483 是的 supervisor 启动的
676529483
2022-08-29 16:25:32 +08:00
@elboble supervisor 只是进程管理,启动命令可以贴下
elboble
2022-08-29 16:40:35 +08:00
[uwsgi]
chdir = /home/xxxx/project/xxxx
module = xxxx.wsgi
home = /home/xxxx/venv/venv3.9
master = true
processes = 2
socket = 0.0.0.0:8909
vacuum = true
zxzflower
2022-08-29 16:53:46 +08:00
用 redis 或者数据库 做个访问开关,你只要有多个 worker ,你的方法就会混乱的,甚至 worker 还会 restart
bulay
2022-08-29 16:57:37 +08:00
如果是单台服务器直接用 django.core.cache,本地缓存.
AndyVTEX
2022-08-29 17:45:35 +08:00
是啊,肯定是 redis 或者数据库开关呀,环境变量是什么骚操作
lanlanye
2022-08-29 20:04:43 +08:00
建议你把要渲染的模板做成 Model ,需要时直接去 DjangoAdmin 里改……
elboble
2022-08-29 23:42:09 +08:00
感谢各位大佬,用 redis 存了状态,基本实现了功能,上服务器也没问题了。

还有几点请教:
1 ,用 uwsgi 起多个 worker ,实际上是多个 django 的实例在跑,这样如果用 uwsgi 来启动 django ,djaongo 中是不存在只运行一次的代码。
我这个问题中,即使用 redis 也有个初始值的坑。最简单的是在程序之外,程序启动之前,用 redis-cli 手动设定初始值。如果不用这个方法,就想是不是能在 django 初始化的地方,运行一次 redis set 初始值。通过上面的分析,django 用 uwsgi 启动是不存在这样只执行一次的代码,至少每个 worker 启动 django 所有的代码都要跑一遍,有几个 worker 就要跑几遍。
后来我想通过 redis 直接 get 键取值,但是 redis 的 get 不像 json 的 get 读取空时能返回一个默认值,这个真没想到,自己简单包装了一个能返回默认值的,来代替原始的 conn.get()。这样就不需要预先设置 redis 了。
conn = redis.Redis(connection_pool=POOL)
IS_RELEASED_KEY = 'xxxx_is_released'
def rget(key):
ret = conn.get(key)
if ret == None:
return '0'
else:
return ret

2 ,取得 redis 的 conn 是在 view.py 中,但是不在任何一个 view 函数中,这个代码是不是每个 worker 只执行一次,有几个 woker 就有几个不同的 conn ,还是每次请求都会建新的 conn ?
zmaplex
2022-08-31 16:45:15 +08:00
一个小功能还是不要上 redis 了吧,在 django 中类似全局变量效果,请直接用 django 内置的
cache 。
elboble
2022-08-31 23:36:14 +08:00
@zmaplex redis 本来就开了,给 celery 用的,这个比建个表 migrate 方便点。
gaogang
2022-09-05 18:01:59 +08:00
@elboble uwsgi 启多个 worker 不是 preload 的方式,所以这种写法的话,每个 worker 中在使用到 view.py 时会创建一个 conn
programMrxu
179 天前
为什么这里的值不会改变呢?我在本地跑了两个线程,去修改一个全局变量,他的值是会发生变化的。
elboble
179 天前
@programMrxu 我理解是 uwsgi 接受每个请求,是单独开个 django 进程,所以即使全局变量也不能共享,“全局”仅在 django 进程内。所以用系统级的 redis ,杀鸡用牛刀了。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/876228

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX