Complex relationships in Ruby - display data across many tables - ruby-on-rails

I have designed an application consisting of 20+ tables, and utilizing Ruby on Rails.
I am using an authentication system for users to login. I want to display to the user, when he logs in, information related to him that can be found across many tables, between these tables there are all the possible relationships. How can I accomplish this in RoR? Should I consider using views, multiple joins? I did not managed to get information from one model further than 2 tables looking in the tables design using classical approaches like "has many", ":through" etc.
Can you please share your knowledge?
Thanks!

I assume that your User model has relationships with all the tables that contain related information.
Assuming that, you can have an action in UsersController and a corresponding view. In the action you can find out who is the currently connected user, and then get all the data you need from the related models. Return these data to the view and render them as you like.

I think your question is too general, but here are some thoughts...assuming you are making a conventional Rails app
"Should I consider using views?"
Views are what users see in conventional rails apps. So if you want to display data to your users, use views to display data that is made available in the controllers. Each action in the controller will grab data from the models, and have a corresponding view.
So to display books that belong to a user, you might have a Books controller. After a user logs in, you have an "index" action in the "Books" controller that says #user_books = #user.books. In this case, #user would be the user object from your authentication system. A user would have many books, and it is possible that a book would have many users. Then in your views/books/index.html.erb file you have something like:
<ul>
<% #user_books.each do |b| %>
<li> <%= b.name %> </li>
<% end %>
</ul>
to print a list of book names.
Joins are done automagically by Rails, so you don't have to worry about it quite yet. But if your app gets really big and queries are complicated, you may have to rewrite the SQL statements to improve scalability....but that is obviously a good problem to have.
Hope this helps!

Related

Rails find_by_email multiple tables

I am working on a rails application and i have 3 different user types. These users are potentially very different, so i created models for each of them. Now, they should be able to login thru a single form. So basically i want to say something like 'find_by_email("some_email")', but search over all three tables. It seems, though, that Rails expect you to call 'find_by' with a specific model, like Admin.find_by(). Any suggestions?
Try something like this and assuming that that the email is unique across all the tables
[Model1, Model2, Model3].each do |model|
break if model.find_by_email("email#email.com").present?
end
Hopefully this is early in your development, but the current structure may not be the best possible route. How many different columns are NOT shared by each of the user types? You may want to use a "user role" system, and have that simply be an extra column on your user table.
Than, you can use something like CanCan to manage those roles and what/where they may access.

RoR query condition: does not contain any of a list of attributes

I feel like there should be a simple answer for this question, but I haven't been able to manage.
I have 3 models: User, Course, and Textbook. My model associations are working just fine, but I will include them if necessary. In my application, I want a view to display the courses for which the user owns textbooks and all other courses for which the user does NOT own the textbooks. In pseudocode, it should look like
#courses_with_books = current_user.textbooks
#courses_without_books = current_user.courses.where(:id != #courses_with_books.course_ids)
I can complete that second line using complicated loops, but the processes seemed silly so I've been researching a way to gather the information I need with a single query and have yet come up with nothing. Thanks in advance :D
In general, if you want to check that some attribute is not inside of some given collection, try 'NOT IN':
current_user.courses.where("id NOT IN ?", #courses_with_books.course_ids)
For more on Active Record Queries, check out the Active Record Query Interface Guide.
rathrio is on the right path. I think for the first part you could do
#courses_with_books = current_user.textbooks.collect(&:course)
If you're trying to display their titles, do
<% #courses_with_books.each do |cwb| %>
<%= cwb.title %>
<% end %>

Filtering Rails database

I have created a database of researchers and the papers they have written in Rails. I need to be able to filter it by author in such a way that information is drawn from the database onto a page in the persons name (for example if there is a researcher called Dr. A. Researcher, I need to be able to go to his page, and that page will be populated with papers that he/she has written automatically). I have been working on this for a while now, and have gone round in circles so many times, Im not exactly sure what I am looking for (though I think its probably going to be AJAX based). Filtering the database seems quite straightforward, but I cant seem to find any information about sending the results of that filter to a specific page.
As I say, Im not totally sure what Im looking for here, so someone may have had a similar issue that has already been answered. If that is the case, I apologise. Any help anyone has about this would be very gratefully received.
Cheers
Seems like you would just want to use the show action.
In your controllers/researchers.rb controller add
def show
#reseacher = Reseacher.find(params[:id])
end
or possibly
def show
#reseacher = Reseacher.find(params[:id], include: [:papers])
end
then in your views/show.html.erb view you can just add
<% #reseacher.papers.each do |p| %>
<%= p.title %>
<%= p.otherattribute %>
<% end %>
Also you will need to ensure your relationships are setup correctly in your model
in models/researcher.rb
add
has_many :papers
to the Researcher Class
and in models/papers.rb
add
belongs_to :researcher
to the Paper Class
localhost:3000/reseachers/1 where 1 is the id of the researcher should then return the papers for that researcher

Collection_select has always only 1 item, not an array? In ruby on rails

I am implementing in Ruby on Rails and I am trying to work with the collection_select, I'm a newbie. I just want to do, I have a list with groups and a list with roles. These are both models. So, I list my groups, and next to that, I have a dropdown list with the role for the group. each group has 1 role.
I implemented some code already, but the collection_select always only remembers the last item. So I want a list with groups, connected with the desired role. But, now I only have 1 item. This is my view:
<% #groups.each do |group| %>
<li>
<%= collection_select('group', 'role_id', #roles, 'id', 'name') %>
</li>
<% end %>
I don't really know what to do now? Someone who knows what I am doing wrong?
Thanks
So, I assume that you're doing a form? What model does the form belong to?
To help debug this sort of thing, usually it'd be a good idea to check your development.log file to see what parameters the form is passing to the controller. Something like:
Parameters: {"commit"=>"Save", "action"=>"update", "_method"=>"put",
"id"=>"6168", "group"=>{"role_id"=>"2", ...}, "controller"=>"groups"}
Now, usually a Rails controller is expecting a form with the data for a single model. If you're wanting to update multiple models or rows at the same time, you're going to have to get creative.
First thing to do might be to try returning an array of groups. Your form at the moment is not using an array. I doubt that these Rails helpers will help you though. Helpers like these are designed to update one ActiveRecord object at once.
It's possible you may need to rethink the design of your app to better fit the Rails way, or roll your own form and iterate over the array that it passes through. Doing it the Rails way is the recommended option, it just might take some brain bending from your end to figure that part out. If you need help, maybe provide more information on what you're actually trying to achieve.

One controller with multiple models? Am I doing this correctly?

My web app, up until this point, has been fairly straight forward. I have Users, Contacts, Appointments and a few other things to manage. All of these are easy - it's just one model per section so I just did a scaffold for each, then modified the scaffolded code to fit my need. Pretty easy...
Unfortunately I am having a problem on this next section because I want the 'Financials' section of my app to be more in depth than the other sections which I simply scaffolded. For example, when the user clicks the 'Contacts' link on the navigation bar, it just shows a list of contacts, pretty straight forward and is in line with the scaffold. However, when the user clicks the 'Financials' link on the navigation bar, I want to show the bank accounts on the left of the page and a few of the transactions on the right.
So the financials tab will basically work with data from two models: transactions and bank_accounts. I think I should make the models (transactions & bank_accounts) and then make a controller called Financials, then I can query the models from the Financials controller and display the pages in app/views/financials/
Am I correct in this app layout? I have never worked with more than the basics of scaffolding so I want to ensure I get this right!
Thank you!
If you are comfortable with scaffolding then i would suggest that you generate a scaffold for both
transactions: script/generate scaffold transaction financial_id:integer ...
bank_accounts: script/generate scaffold bank_account financial_id:integer ...
and financials script/generate scaffold financials ...
In your transactions model, add this:
class Transaction < ActiveRecord::Base
belongs_to :financial
end
In your bank_account model, add this:
class Bank_account < ActiveRecord::Base
belongs_to :financial
end
In your financial model, add this:
class Financial < ActiveRecord::Base
has_many :transactions
has_many :bank_accounts
end
Now from your financials controller you can use something like this:
def index
#financial = Financial.find(params[:id])
#This fetches all bank_accounts related to financial
#bank_accounts = #financial.bank_accounts
#This fetches all transactions related to financial
#transactions = #financial.transactions
end
In your views you can view all the bank accounts belonging to a particular financial by just doing this:
<% #bank_accounts.each do |bank_account| -%>
<%=h bank_account.something_here %> <!-- something_here is the column name corresponding to your bank_account table. -->
<%=h bank_account.something_here %> <!-- something_here is the column name corresponding to your bank_account table. -->
<%=h bank_account.something_here %> <!-- something_here is the column name corresponding to your bank_account table. -->
.
.
.
<% end -%>
In your views you can view all the transactions belonging to a particular financial by adding something similar:
<% #transactions.each do |transaction| -%>
<%=h transaction.something_here %> <!-- something_here is the column name corresponding to your bank_account table. -->
<%=h transaction.something_here %> <!-- something_here is the column name corresponding to your bank_account table. -->
<%=h transaction.something_here %> <!-- something_here is the column name corresponding to your bank_account table. -->
.
.
.
<% end -%>
Remember, while creating a new transaction/bankaccount use the id belonging to the particular financial. Hope this helps. Cheers! :)
It sounds to me as if you want two views:
However, when the user clicks the 'Financials' link on the navigation bar, I want to show the bank accounts on the left of the page and a few of the transactions on the right.the transactions on the right.
Keep in mind that one screen doesn't always mean one view in MVC. Conceptually, a view simply renders model data so the user can do something meaningful with it through controller actions. Rails muddies this up a bit by having you create a file in a views/ directory for each screen, so it's easy to think that one screen means one view. However, you can also call layouts within views in Rails, and these layouts are themselves views.
Putting transactions and accounts in separate views means each would have a controller to handle the user's interaction with each segment of data. The account controller, for example, would handle the event where the user selects an account to view additional information about an account. This would result in fetching transactions to show the user. The user could then interact with a transaction in an attempt to reconcile, void, or contest it. This would invoke logic in the transaction controller.
While this does a good job of removing the coupling from your controllers, this may seem like it couples the two views. However, the coupling is one-way: the account list includes logic specific to the transaction view (i.e. updating it when you select an account). The transaction view remains reusable in other areas of the application. For the most part, the account listing remains reusable as well, especially if you isolate it into a separate layout. The only difference you'd need for this specific screen is the handling of when a user clicks on an account.
If you merge everything into a financials view, it's still possible to keep things loosely-coupled, but it becomes more difficult to do so. The additional layer takes extra effort and may subconsciously guide you to merge everything up together.
Of course, either way works, and you could easily argue that the final design is an exercise that is best left to the developer's preference. I simply offer my interpretation of how one would most-accurately stick to the patters Rails attempts to guide developers to using.
I don't have a huge amount of experience with Ruby on Rails, or MVC work in general, but I think you're going about it the right way. You generally want a model object (and by extension, database table) for data-oriented resources... That is, your models should correspond to your "nouns". Your controller's actions are your "verbs", and to me it makes perfect sense to categorize them how you like.
In this case, I would say that putting a bunch of "verbs" under financials, which interact with two model object types as needed, makes sense IF AND ONLY IF it makes sense to you for you to organize it like this. (Bear in mind that you can also evaluate how well organized and intuitive the corresponding URLs feel to you, though I'm sure most MVC purists would add that you should never just rely on examining the URLs!)
So in summary, I think you're doing everything right here, as long as it makes sense to you. Yes, you'll have to write your views and controller actions yourself, though you can start using ruby script/generate controller financials action1 action2 [...]; that at least gives you skeletons of the view files and empty controller actions.

Resources