Rails清理并验证用户对where子句的输入

时间:2016-12-23 22:29:10

标签: ruby-on-rails ruby activerecord

我有以下代码用于根据用户的搜索过滤帖子的结果。我如何确保参数存在,有效和消毒?

Post.where("title LIKE ? AND cost >= ? AND cost <= ? AND status = 'open'", "%#{search_params[:keywords]}%",
                    "#{search_params[:min] && !search_params[:min].empty? ? search_params[:min] : 0}",
                    "#{search_params[:max] && !search_params[:max].empty? ? search_params[:max] : 999999999}");

2 个答案:

答案 0 :(得分:0)

像往常一样,有很多方法可以解决这个问题。

我认为最好的是use a form model。帮助实现此模式的好宝石有virtusreform,但您也可以使用plain ActiveModel

这个想法是表单对象进行验证,如果它无效,控制器可以呈现验证错误。

如果您在网上搜索“Rails表单模型模式”或类似内容,您会发现更多文章。这是另一个看起来写得很好的人:https://webuild.envato.com/blog/creating-form-objects-with-activemodel-and-virtus/

在相关的说明中,您可能希望将此复杂查询封装在作用域中,或者 - 如果您的应用程序将要增长 - 甚至是查询对象。有关在更复杂的Rails应用程序中有用的一些更高级的模式,请参阅this article

关于输入的清理,只要您使用参数绑定而不是手动字符串连接,Rails就会自动清理输入以防止SQL注入。如果你想做更多 - 比如从搜索查询中删除停用词或者某事。像这样,您可能会更好地使用预先存在的搜索框架,例如sphinxpg_searchtextacular

答案 1 :(得分:0)

我会通过将几个范围与某些条件组合来解决这个问题:

import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np
import math

number_of_planes = 100

cos_thetas = np.empty(number_of_planes)
phis = np.empty(number_of_planes)
for i in range(0,number_of_planes):
    phi = np.random.uniform(0,2*math.pi)
    theta = math.acos(2*np.random.uniform(0.5,1) - 1)

    phis[i] = phi
    cos_thetas[i] = math.cos(theta)

thicknesses = np.random.rand(number_of_planes, number_of_planes)

sns.set_style("darkgrid")
fig = plt.figure()
ax = fig.add_subplot(111, aspect='equal')

thick_df = pd.DataFrame(thicknesses*1000, columns=phis, index=cos_thetas)

#print thick_df

thick_df = thick_df.sort_index(axis=0, ascending=False)
thick_df = thick_df.sort_index(axis=1)

cmap = sns.cubehelix_palette(start=2.9, light=0.9, as_cmap=True, reverse=True)

yticks = np.linspace(0,1,6)

x_end = 6
xticks = np.arange(x_end+1)

m, n = 10, 10
row_groups = np.arange(len(thick_df.index)) // m
col_groups = np.arange(len(thick_df.columns)) // n

grpd = pd.DataFrame(thick_df.values, row_groups, col_groups)

val = pd.to_numeric(grpd.stack(), 'coerce').groupby(level=[0, 1]).mean().unstack().values
idx = thick_df.index.to_series().groupby(row_groups).mean().values
col = thick_df.columns.to_series().groupby(col_groups).mean().values

new_thick_df = pd.DataFrame(val, idx, col)

sns.heatmap(new_thick_df, linewidth=0, xticklabels=xticks, yticklabels=yticks[::-1], square=True, cmap=cmap, ax=ax)
#new_thick_df.plot.scatter(thick_df.columns.argmin(), thick_df.index.argmin(), ax=ax, c='r', s=100)
#One problem here is that thick_df.columns.argmin() gives an integer position instead of the column label
ax.scatter(thick_df.columns.argmin(), thick_df.index.argmin(), marker='*', s=100, color='yellow')  

ax.set_xticks(xticks*ax.get_xlim()[1]/(2*math.pi))
ax.set_yticks(yticks*ax.get_ylim()[1])
ax.set_xlabel(r'$\rm{\phi}$', fontsize=16)
ax.set_ylabel(r'$\rm{\cos\ \theta}$', fontsize=16)
plt.figtext(0.865, 0.5, r'$\rm{thickness\ (kpc)}$', fontsize=15, rotation=270, horizontalalignment='left', verticalalignment='center')

plt.show()

在您的控制器中使用此范围:

# in your models/post.rb
scope :min_cost, ->(cost) {
  cost = 0 if cost.blank?
  where('cost >= ?', cost)
}
scope :max_cost, ->(cost) {
  cost = 999_999_999 if cost.blank?
  where('cost <= ?', cost)
}
scope :cost_between, ->(min, max) { min_cost(min).max_cost(max) }
scope :open, -> { where(status: 'open') }
scope :search, ->(title) { where("title LIKE ?", "%#{title}%") if title }