我目前正在开发一个应用程序,该系统要求在系统中注册的每个学生在注册时都具有唯一的注册号,并且每次注册都会自动递增。对于2019年注册的第一个学生,注册号的格式应为“ 01/19”,对于第890个学生的注册号应为“ 890/19”等。
用于注册的数据是从kobo data collection工具上传的,因此它在非常短的时间间隔内到达我的端点,因为一次可以提交近200次提交。
我意识到,在1000条记录中,有接近27个重复的注册号。这就是我实现注册号生成逻辑的方式
def registration_changeset(student, attrs) do
student |> changeset(attrs) |> Accounts.add_registration_number()
end
然后,“帐户”上下文具有以下用于添加注册号的代码
def add_registration_number(changeset) do
struct = changeset.data.__struct__
registration_number =
case struct |> last() |> Repo.one() do
nil -> _create_new_registration_number()
resource -> _increment_registration(resource.registration_number)
end
put_change(changeset, :registration_number, registration_number)
end
在上述情况下,我押注最后一个创建的学生具有最新的注册号
是否有更好的方法来实现这一点?
答案 0 :(得分:1)
这里需要一些同步代码:)
创建一个专门用于以下目的的过程:产生数字。 GenServer.handle_call/3
已经可以满足您的任何需求,进程邮箱是完美的队列,OTP可以为您做所有事情。
defmodule MyApp.RegNumHelper do
@moduledoc false
use GenServer
def start_link(opts),
do: GenServer.start_link(__MODULE__, opts, name: name)
def add_registration_number(changeset),
do: GenServer.call(__MODULE__, {:reg_num, changeset})
@impl true
def init(opts), do: {:ok, opts}
@impl true
def handle_call({:reg_num, changeset}, _from, state) do
# your logic assigning changeset
{:reply, changeset, state}
end
end
这种方法还有另一个优点:由于该过程已经是有状态的,因此实际上您不需要每次都查询数据库。只需在流程开始时查询它,然后将当前数字保存到state
中即可。
答案 1 :(得分:0)
最简单的解决方案是,您可以将自动递增的ID设为注册号的第一部分,然后将两位数字的年本身存储为字段。然后,在模型中,您将简单地拥有一个由“ get_registration_number”组成的方法。这样可以解决重复问题。
不利的一面是,您将拥有890/19作为2019年的最后一个学生,然后有891/20作为2020年的第一个学生,而不是01/20。但是您的示例似乎也无法处理这种情况,并且隐含(但不清楚)这是必要条件。
如果是这样,您可以创建一个像以前一样自动递增的列(例如id_num),而不是让ID作为第一部分,然后在年份更改时将该列的下一个值重置为1。如果您想变得偏执,可以说'id_num'列和'two_digit_year'列一起构成一个唯一键。
TL; DR:最简单的方法是让数据库处理它。