Active record is a great library of rails for manipulating databases. However, in the use process, many situations that we do not understand why it can be like that. But obviously only you have the wrong code, but the code does not run wrong. Here are a few such cases that I have encountered. Maybe you already know the redundant, but also not superfluous.
1, Scope return nil
There is a simple scope as follows:
1 2 3 4 5 6 | <span class="token keyword">class</span> <span class="token class-name">User</span> <span class="token operator"><</span> <span class="token constant">ApplicationRecord</span> scope <span class="token symbol">:test_nil</span> <span class="token punctuation">,</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token keyword">do</span> <span class="token keyword">nil</span> <span class="token keyword">end</span> <span class="token keyword">end</span> |
What do you think User.test_nil
will return? nil? Of course not. Scope if nil is returned, then all records will be returned. Similar to User.all
. But naturally you return nil do? Functions like find_by
, find_by_#{attribute}
will return nil if not found. In most cases, everything will work fine, but if you can’t find it, you’ll get it. Lesson learned: Do not use find_by
in scope.
2, where nil
Sometimes I see some newbie rails code like this.
1 2 | <span class="token constant">User</span> <span class="token punctuation">.</span> <span class="token function">where</span> <span class="token punctuation">(</span> <span class="token string">"age = ? and rank = ?"</span> <span class="token punctuation">,</span> age <span class="token punctuation">,</span> rank <span class="token punctuation">)</span> |
I’m really discouraged with something like this, It’s true that it still works fine during normal times, but what if it’s rank nil? Isn’t code a little more secure? If you don’t know, the comparison with true null in sql would be
1 2 | <span class="token keyword">select</span> <span class="token operator">*</span> <span class="token keyword">from</span> users <span class="token keyword">where</span> age <span class="token operator">=</span> <span class="token number">30</span> <span class="token operator">and</span> rank <span class="token operator">is</span> <span class="token boolean">null</span> <span class="token punctuation">;</span> |
Just simple fix into.
1 2 | <span class="token constant">User</span> <span class="token punctuation">.</span> <span class="token function">where</span> <span class="token punctuation">(</span> age <span class="token punctuation">:</span> age <span class="token punctuation">,</span> rank <span class="token punctuation">:</span> rank <span class="token punctuation">)</span> |
That is, the active record will automatically correct the query to be is null
if needed. If really need to use net sql. Be sure to pay attention to the type of DBMS you use and consider sanitize
if needed. If I have time, I will say more later.
3, includes (). Limit ()
Sometimes you will encounter this in paging. First, you code a simple paragraph to retrieve data, paging (by kaminari is still fine), show out. But you found code sticking n + 1 query because it needs some other information when showing. You fix it, simply, use includes. And this is where the problem arises. For example, for 2 models as below.
1 2 3 4 5 6 7 8 | <span class="token keyword">class</span> <span class="token class-name">User</span> <span class="token operator"><</span> <span class="token constant">ApplicationRecord</span> has_many <span class="token symbol">:posts</span> <span class="token keyword">end</span> <span class="token keyword">class</span> <span class="token class-name">Post</span> <span class="token operator"><</span> <span class="token constant">ApplicationRecord</span> belongs_to <span class="token symbol">:user</span> <span class="token keyword">end</span> |
Then query
1 2 | <span class="token constant">User</span> <span class="token punctuation">.</span> <span class="token function">joins</span> <span class="token punctuation">(</span> <span class="token symbol">:posts</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">includes</span> <span class="token punctuation">(</span> <span class="token symbol">:posts</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">limit</span> <span class="token punctuation">(</span> <span class="token number">5</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">offset</span> <span class="token punctuation">(</span> <span class="token number">5</span> <span class="token punctuation">)</span> |
Just by thinking, you will think that it will give you 5 users and bring all those posts. But no, joins will create a new style table.
Users | Posts |
---|---|
User1 | Post1 |
User1 | Post2 |
User1 | Post3 |
User2 | Post4 |
User2 | Post5 |
User3 | Post6 |
So limit will only retrieve 2 users. And because the call starts from User, it only returns 2 users but you can’t understand why it is. Maybe you see it rarely but once you do not know why always, watching the new data forever. In addition, if the number of rows is small, it does not work, because then the active record will split into 2 sql statements, pluck id and then select by print with the second query so it will not be okay. Everything only comes out when it comes to products or somewhere that generates a lot of data.