Rails-Treasure chest1 (自定义Model网址;多语言包; 时区设置, TimeZone类; 格式日期时间; 表单单选UI; 表单多选UI;Select2 Plugin)
- 自定义Model网址: 随机值网址SecureRandom.base58
- 多语言包, 包括默认语言设置和user自设置。
- 时区设置, TimeZone类 ,增加user自选时区功能
- 格式日期时间: xx.created_at.strftime("%Y-%m-%d %H:%M")
- 表单单选UI
- 表单单选UI和Select2 Plugin
- 表单多选UI和Select2 Plugin
自定义Model网址
- 方案一:网址上除了数字ID,可以再加上文字
- 方案二:不要用数据库的递增数字ID,而是用一个随机值产生的 ID✅
- 方案二(改):除了用乱数ID,也可以让用户自定义 ID(在二的基础上修改)
方案一:
在model层使用to_param(*args)方法,别名 ActionController::Parameters#to_query
返回一个接收者的string形式 ,适合用在一个URL 查询字符串。
def to_param
self.id #这是Rails默认形式
end
这是Rails默认的路径方法,会默认添加到event_path(@event)
等同于 event_path(@event.to_param),
因此to_params可以改成
"#{self.id}-#{self.name}", 返回一个"string",
返回结果作为参数"id=xxxxxx", url生成Parameters: {"id"=>"xxxxxx"}
to_query方法原理:
> params = ActionController::Parameters.new({
> name: "David",
> nationality: "Danish"
> })
=> <ActionController::Parameters {"name"=>"David", "nationality"=>"Danish"} permitted: >
> params.to_query
=> "name=David&nationality=Danish"
⚠️: Event.find(params[:id])中的参数会调用to_i,因此后面的非数字会被自动除去。如果写成"#{self.name}-#{self.id}", 就会报错了。因为"Smokey-1"返回的是0
方案二: 随机数✅✅✅
例子url: http://localhost:3000/events/FNRLRNgbKZWfVP7P
1. 新增一个字段friendly_id,带index, 是unique。
add_column :events, :friendly_id, :string
add_index :events, :friendly_id, :unique => true
2. 使用to_param方法: self.friendly_id
3. 所有相关Controller action 改用 @event = Event.find_by(friendly_id: params[:id])
4. model.rb中,增加一个before_validation :generate_XXX, on:[:create], 然后设置这个方法为friendly_id ||= SecureRandom.base58
方案二(增加自定义)
<div class="form-group">
<%= f.label :friendly_id %>
<%= f.text_field :friendly_id, :required => true, class: "form-control"%>
</div>
⚠️ input-Attribute: required代表必须填写。
配置中文语系
如果我们的网站不需要支援多国语系,你可能会觉得这样做有点辛苦,直接将中文写在样板上就好了。
但这个功能对于团队协作开发网站仍然非常有帮助,因为写程式的时候不一定会先确定文案规格,用 I18n 来处理的话,最后只需要让 PM 统一修改翻译词汇档即可。
gem 'rails-i18n' , gem 'devise-i18n'
使用i18n的Rails指导:https://guides.rubyonrails.org/i18n.html
方法:
1. config/application.rb
class Application < Rails::Application
config.i18n.default_locale = "zh-CN"
end
2. 新增 config/locales/zh-CN.yml
"zh-CN":
event_list: 活动列表
admin:
event_list: 活动列表管理
3. 修改en.yml
"en":
+ event_list: Event List
+ admin:
event_list: Admin Event List
4. 修改view中对应的位置,统统改为t("event_list")和t("admin.event_list")
t是i18n中的方法。
嵌套variable, 使用%{variable_name}
en:
hello: "HI, %{name}"
view中 使用 t("hello", :name => variable_name )
使用gem 'i18n'后:
config.i18n.default_locale = "zh-CN"
config.i18n.available_locales = ["zh-CN", :en,...] 选择加载的locale包, 不写这句就是全加载。
Devise 也有用到 I18n,可以安装 devise-i18n 这个 gem 有开源社区做好的中文翻译:
https://github.com/tigrish/devise-i18n
对model的字段进行翻译:
zh-CN:
activerecord:
attributes:
event:
name: "活动名称"
description: "描述"
用户自行切换多语言:
app/views/layouts/application.html.erb
+ <%= link_to "中文版", :locale => "zh-CN", :is => "dd" %>
+ <%= link_to "English", :locale => "en" %>
注意:locale是自行传入url的参数 http://localhost:3000/events?locale=zh-CN&ls=dd
app/controllers/application_controller.rb
+ before_action :set_locale
+
+ def set_locale
+ if params[:locale] && I18n.available_locales.include?( params[:locale].to_sym )
+ session[:locale] = params[:locale]
+ end
+
+ I18n.locale = session[:locale] || I18n.default_locale
+ end
语言系样板:
如果样板内大多是属于较为静态的内容,Rails 也提供了不同语系可以有不同样板,你只要将样板命名加上语系附档名即可
执行 rails g controller pages
编辑 config/routes.rb
get "/faq" => "pages#faq"
新增 app/views/pages/faq.zh-CN.html.erb
新增 app/views/pages/faq.en.html.erb
如此在英文版的时候就会使用 faq.en.html.erb
这个样板,中文版时使用 faq.zh-CN.html.erb
这个样板。
最后,编辑 app/views/layouts/application.rb
放上 FAQ 页面的连结:
app/views/layouts/application.rb
<%= yield %> <%= link_to "FAQ", faq_path %>
表单单选UI(固定选项,无Model)
情况:在表单上加一个单选UI, 选项是固定的几个,因此无需Model来存选项。
首先:新增一个字段来存储选项的状态。使用string.
- rails g migration AddStatusToEvent status:string
- 在model层上,定义一个常量数组,用于储存选项类型,并validates :status, inclusion: STATUS
然后:把数据库的status, 转化为user可看的中文。用Helper方法转化中文,或用i18n转化。
再后:在views/../_form.html.erb中增加select下拉菜单。在controller中,设置params白名单。
- 分支:少量选项可改用Radio Button UI: radio_button()
⚠️:可以加bootstrap4的美化。
表单的select的写法:
f.select :status, Event::STATUS.map{ |s| [t(s, :scope => "event.status"), s] }, {}, :class => "form-control"
表单的radio的写法:radion_button(method, tag_value, options = {} )
<div class="btn-group" data-toggle="buttons">
<% Event::STATUS.each do |status| %>
<label class="btn btn-default <%= (status == f.object.status)? 'active' : '' %>">
<%= f.radio_button :status, status %>
<%= t(status, :scope => "event.status")%>
</label>
<% end %>
</div>
=> <input type="radio" value="draft" name="event[status] id="event_status_draft">
⚠️:
- button样式可以用在其他元素上如<label>, 为了提供checkbox/radio按钮button样式,需要加上data-toggle="buttons" 到一个有.btn-group的元素。目的是给按钮加上$().button('toggle')这个jquery方法,这个方法会给button合适的外观。
- CSS的active, 代表选择的button, 需要developer手动添加到<label>
- f是<FormBuilder:0x0007...>表格对象; f.object是<Event::0x0007...>; f.object.status是当前event的状态。
- 如果status和event的status相同,则选中这个button. 当进入edit页面时,默认就是原先选择的状态。
i18n的写法:
t是translate(key, options={})的简写:
Lookup:
key可以是单独的key,也可以是a dot-separated key(string和symbols都可)
如:t('date.formats.short')
scope可以是单独的key, a dot-separated key或者an array of keys or dot-separated keys.
如:
I18n.t 'formats.short', :scope => 'date'
I18n.t 'short', :scope => 'date.formats'
I18n.t 'short', :scope => %w(date formats)
t(@event.status, :scope => "event.status")
在zh-CN.yml中:
event:
status:
draft: 草稿
private: 私密
public: 公开
插入interpolation:
view中使用 t("hello", :name => variable_name )
在en.yml中
en:
hello: "HI, %{name}"
表单单选 UI 和 Select2 Plugin
例子:Event需要被分类,但这个分类是可以编辑的,非固定的,因此需要新增一个Category Model。
- rails g model category name; Event增加一个category_id,加上index; 建立双方关联。
- 建立Category的 controller: rails g controller admin::categories
- 后台编辑页面增加一个目录select。对应请求参数加入白名单(controller#event_params)
- 前台show页面,显示分类。或者其他外观布景。
- gem 'select2-rails'是一个jQuery Plugin让select菜单看起来非常漂亮。
注意:
第四步,前台页面显示分类时,如果event没有分类会报告nilClass, 需要添加判断或者使用try()方法。
如:@event.category.try(:name)
表单多选UI
例子:给admin后台的user编组
步骤:
- 新建Group用于给users分组
- User和Group是多对多关系。新建一个关联Model Memebership
- 控制台输入一些group资料。
- 多选UI,需要使用checkbox. helper方法可以使用collection_check_boxes()
- controller#user_params的白名单,:group_ids => []
- 前台页面修改。为了避免N+1 query可以使用User.includes(:groups).all
- 也可以使用select 2插件
详解:
第四步,在edit页面,新增checkbox,
<%= f.label :group_ids %>
<%= f.collection_check_boxes(:group_ids, Group.all, :id, :name )%>
会生成原生html:(下面是部分代码)
<input type="hidden" name="user[group_ids][]" value="">
<input type="checkbox" value="1" name="user[group_ids][]" id="user_group_ids_1">
@user的group_ids,指它的一组group_id集合,作为参数传入controller处理,以此生成membership记录。
collection_check_boxes(method, collection, value_method, text_method, options = {}, &block)
method是 :group_ids 一个关联方法。
collection是 Group.all
value_method是 要存入的值, 因此这里是:id (group的id)
text_name是显示的文本内容, 因此这里是 :name (group的name)
这是生成的参数:
Parameters: { "utf8"=>"✓",
"authenticity_token"=>"fyyJ9Q...",
"user"=>{"email"=>"admin@example.org", "group_ids"=>["", "1", "2"]},
"commit"=>"Update",
"id"=>"1"}
调用Admin::UsersController#update方法
#查找user.id是1的记录:@user = User.find(params[:Id])
SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
#然后 @user = @user.update(user_params)
#根据参数中"group_ids"=>["", "1", "2"]查询group.id是1, 2的记录
SELECT "groups".* FROM "groups" WHERE "groups"."id" IN (1, 2)
#membership和group表格连接,然后找user_id = 1的数据,没有找到:
SELECT "groups".* FROM "groups" INNER JOIN "memberships" ON "groups"."id" = "memberships"."group_id" WHERE "memberships"."user_id" = ? [["user_id", 1]]
#因此,根据group_id和user_id, 插入2条memberships
INSERT INTO "memberships" ("user_id", "group_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["user_id", 1], ["group_id", 1], 。。。
INSERT INTO "memberships" ("user_id", "group_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["user_id", 1], ["group_id", 2], 。。。
Rails-Treasure chest1 (自定义Model网址;多语言包; 时区设置, TimeZone类; 格式日期时间; 表单单选UI; 表单多选UI;Select2 Plugin)的更多相关文章
- django时区设置(timezone)
django时区设置(timezone): 默认: TIMEZONE:'America/Chicago'(以前的版本,现在的版本默认的都是UTC时间.) Chicago时间,为UTC/GMT -6 小 ...
- QT内省机制、自定义Model、数据库
本文将介绍自定义Model过程中数据库数据源的获取方法,我使用过以下三种方式获取数据库数据源: 创建 存储对应数据库所有字段的 结构体,将结构体置于容器中返回,然后根据索引值(QModelIndex) ...
- iOS自定义model排序
在开发过程中,可能需要按照model的某种属性排序. 1.自定义model @interface Person : NSObject @property (nonatomic,copy) NSStri ...
- Qt自定义model
前面我们说了Qt提供的几个预定义model.但是,面对变化万千的需求,那几个model是远远不能满足我们的需要的.另外,对于Qt这种框架来说,model的选择首先要能满足绝大多数功能的需要,这就是说, ...
- 浅析在QtWidget中自定义Model
Qt 4推出了一组新的item view类,它们使用model/view结构来管理数据与表示层的关系.这种结构带来的功能上的分离给了开发人员更大的弹性来定制数据项的表示,它也提供一个标准的model接 ...
- Sails 自定义 model 方法
Sails 自定义 model 方法 在 Sails 中 model 提供了一些原生的静态方法,如 .create(), .update(), .destroy(), .find(), 等. 在实际业 ...
- Qt学习之路(45): 自定义model之一
前面我们说了Qt提供的几个预定义model.但是,面对变化万千的需求,那几个model是远远不能满足我们的需要的.另外,对于Qt这种框架来说,model的选择首先要能满足绝大多数功能的需要,这就是说, ...
- 浅析在QtWidget中自定义Model(beginInsertRows()和endInsertRows()是空架子,类似于一种信号,用来通知底层)
Qt 4推出了一组新的item view类,它们使用model/view结构来管理数据与表示层的关系.这种结构带来的功能上的分离给了开发人员更大的弹性来定制数据项的表示,它也提供一个标准的model接 ...
- Qt之如何自定义model
Qt之如何自定义model https://blog.csdn.net/wei375653972/article/details/86592209
随机推荐
- Yii数据库子查询嵌入select中,而不是where条件语句中
$subQuery = (new Query())->select('COUNT(*)')->from('user'); // SELECT `id`, (SELECT COUNT(*) ...
- HOJ 2252 The Priest(动态规划)
The Priest Source : 计算机学院第二届"光熙杯"程序设计大赛 Time limit : 3 sec Memory limit : 32 M Submitted : ...
- java web 打印(lodop)案例
应需求接触到lodop 打印. 首先在lodop官网下载相关文件(js.css等):http://www.lodop.net/download.html 在下载好的包里 除了html页面 其他的j ...
- TC/IP协议簇
TCP/IP: 数据链路层:ARP,RARP网络层: IP,ICMP,IGMP传输层:TCP ,UDP,UGP应用层:Telnet,FTP,SMTP,SNMP. OSI:物理层:EIA/TIA-232 ...
- AWR之-enq TX - row lock contention的性能故障-转
1 对这一个小时进行AWR的收集和分析,首先,从报告头中看到DB Time达到近500分钟,(DB Time)/Elapsed=8,这个比值偏高: Snap Id Snap Time Sessio ...
- Oracle性能优化之 Oracle里的优化器
优化器(optimizer)是oracle数据库内置的一个核心子系统.优化器的目的是按照一定的判断原则来得到它认为的目标SQL在当前的情形下的最高效的执行路径,也就是为了得到目标SQL的最佳执行计划. ...
- 设置webView头部不能滑动
设置webView头部不能滑动 _webView.scrollView.bounces=NO;
- AutoLayout性能不如frame
http://draveness.me/layout-performance.html 复杂视图, 数量超过30个,用autoLayout就比较卡顿了 发现首页类似朋友圈,卡顿的原因应该就是使用了au ...
- python基础之递归、二分法
一 递归 1. 必须有一个明确的结束条件2. 每次进入更深一层递归时,问题规模相比上次递归都应有所减少3. 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结 ...
- Romantic---hdu2669(扩展欧几里德模板)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2669 详解:扩展欧几里德 #include <iostream> #include < ...