How do I get a group count with multiple columns in the group with a virtual attribute - ruby-on-rails

Keeping is simple assume I have a model "Loan" that has 2 attributes "Type" and "Status". Type as possible values of "Home", "Auto", "Building" and Status has possible values of "Open", "Pending", "Closed" and nil.
Without manually writing the SQL (using group and count?) how do I get the results of:
select type, status, count(*) from loans group by 1, 2;
I could do
Load.group(:type, :status).count()
But there are situations where status might be nil. In that case I want to replace the nil with "Unknown"
I tried adding a virtual attribute to the model:
def usable_status
status.nil? ? 'Unknown' : status
end
So I could then do
Load.group(:type, :usable_status).count()
But count didn't seem to recognize the virtual attribute since it is trying to pass it directly to the database.
Ideas?

Well, that's pretty easy to achieve by combining the rails styling with the SQL functionality:
Loan.group(:type, "COALESCE(status, 'Unknown')").count()

Related

Check if record exists where child has value

I'm building an interface where people can submit tags for the content of various websites so each Submission has a parameter domain and has_one :tag.
The trick is that I would like to check to see if that tag has previously been submitted for that domain before saving.
Something like:
Submission.where(domain: submission_params[:domain], tag.tag_text: submission_params[:tag][:tag_text]).exists?
In order to be able to query the associated table, you should JOIN it. In your case joins is what you're looking for (INNER JOIN):
Submission.joins(:tag)
.where(
submissions: { domain: submission_params[:domain] },
tags: { tag_text: submission_params[:tag_attributes][:tag_text] }
).exists?

ModelState.IsValid is false because CreatedBy field is null

I have an entity with, amongst others, the fields: CreatedBy, CreatedAt, ChangedAt
Now in the view, I do not want the user to fill these fields.
My approach was to fill them on the HTTP POST Action, before ModelState.IsValid check is made and before saving the data to the database.
However, ModelState.IsValid keeps returning false no matter what. What is the right way to implement this? Should I take the validation (ModelState.IsValid) from the POST action?
One problem, many solutions (from best to worst, in my opinion).
First solution
The best way would be to use a ViewModel (a class containing only the fields which must be edited by user and / or validated).
In this case, the CreatedBy, CreatedAt, ChangedAt fields would not appear in your ViewModel class.
Second solution
If you don't want ViewModel, you can put the "not mutable by user" fields as hidden fields in your view
#Html.HiddenFor(m => m.CreatedAt)
of course, hidden fields can be changed by user, so it's always an open door to unwanted datas...
Third solution
Clean ModelState from undesired errors before checking if it's valid (should become really boring if you've many fields concerned).
if (ModelState.ContainsKey("CreatedAt")) {
ModelState["CreatedAt"].Errors.Clear();
}
//then test for ModelState.IsValid()
Other solutions
WhiteList, BlackList for model binding, or... anything I forgot !
Many projects like to separate the View Model from the Domain Model. This allows you to create a View-Model class specifically tailored for the data you want to render and/or receive in a certain action while keeping the domain model correct/consistent.
In your View-Model class you would either not define any such property as the created date (since it is not supposed to be posted but is determined in the action). Or if you use one and the same View-Model for rendering and posting, and you want to render the date, you can make the date nullable (see Alexey's answer) on the View-Model while keeping it mandatory on the domain model.
DateTime is not nullable. If you want to keep those fields as null during model binding, if there is no values for them use:
Nullable<DateTime>
or shortcut:
DateTime?

ASP.NET MVC 3 - Filtering list with multiple fields

I have a list of Assets displayed in a HTML table, the data model for these is quite complicated; they each have a Manufacturer/Model pair, Location and Status just to name a few.
To filter by one field, it is simple something along the lines of
#Html.ActionLink(item.LocationName, "Index",
new { LocationID = item.LocationID.ToString() }, null)
This will produce a URL looking like
hxxp://Localhost/Assets/Index?LocationID=3
The problem arises when trying to filter by both multiple different fields and multiple similar fields, I cannot seem to find a way to produce URLs like
hxxp://Localhost/Assets/Index?LocationID=3,4
or
hxxp://Localhost/Assets/Index?LocationID=3&Status=Active
I may just be trying to treat MVC like Webforms but I don't see any way of passing all these combinations of filtered fields back to my controller to produce a table that shows the data the end user wants to see.
You can also try
Sprint.Filter

Insert link in String field if substring matches

I have a "comments" String field in my domain. On each save or update of the field I want to check if the field contains a sub string that matches a String from another field of the same domain (or different domain for that matter). I need to run through all the instances of that field to see if there is a match. If it matches I want to transform it into a link inside the comments field to a show action for that entry matching the sub string.
So for example, a comments field for a product with a serial number would note if the product has been replaced by another one by giving it's serial number in the comments field. Like: "This product was replaced by SN1234". I want to automatically transform SN1234 into a link to show the product with serial number SN1234.
What is the best way to go about this ? In the controller, in the GSP ? How ?
As long as the column you're trying to match on is indexed, you'll just need to do a query for the match and if found, modify your comment to include the URL. Controller or Service doesn't really matter for the lookup (although I would probably put it in a service). You'll want to be sure the search is not transactional so it will be as fast as it can be. No way I would do any of this in a GSP.
To insert the link, you could a simple find and replace. Once you know which text you want turned into the link, pseudo code follows:
def comment = "This product was replaced by SN1234"
def match = "SN1234"
def link = g.link(action: "show", controller: "product", id: "${product.id}", match)
comment = comment.replace(match, link)
Which you would then end up with
"This product was replaced by <a href='/product/1234'>SN1234</a>"
There may be more efficient ways to do this, but this is a good place to start.
You can use GORM events to do it in your domain. So whenever the domain is inserted/updated you can check that your field has been changed. Then you can insert your link.
def beforeInsert() {
yourMethod()
}
def beforeUpdate() {
if (isDirty('yourField')) {
yourMethod()
}
}

Binding Custom List/Dictionary to a Dropdown in MVC View

I have a bunch of tables. Each table has a "Status" column. The column has either the character A or I. A= Active and I= Inactive.
I have a create view corresponding to each table. I want to display a dropdownbox that shows two values- Active and Inactive and then map them accordingly.
I know I can do following in each view where I need the dropdown for status
#Html.DropDownListFor(model => model.status, new SelectList(new[] { new { ID = "A", Desc = "Active" }, new { ID = "I", Desc = "Inactive" } }, "ID", "Desc"))
however if tomorrow I decide to have add one more status i will have to change each and every view.
The other option is to create a dictionary and pass it through a view model sort of as explained in this article
however that means I have to create a viewmodel for each of my models just to accomodate the statuslist.
is there any other way I can achieve this?
I recommend using a View Model for each unique View. Long term it makes the UI easier to work with and maintain.
The View Model approach can be taken to an extreme and become counterproductive in my experience. For example, when creating a VM for a Customer Entity, I do not recreate a CustomerVM that has all the same properties of the Customer Entity. Instead, I just create a Customer Property on the CustomerVM that holds the entire Customer Entity. Some may disagree with this approach because I may be exposing more info to the View than it needs, if the view is not displaying all of the Customer Entity info. It's true, but I like solutions that are fast and easy to implement and maintain.
You never know what is going to be necessary in the future. Over time I have found this approach to be the most flexible solution.
So, then you could create a Base View Model for all views that have common look-up lists and have your new View Models inherit from this Base VM.
It's one way to do it. :)
Following link might be helpful for binding the dropdown list in mvc
http://www.c-sharpcorner.com/UploadFile/deveshomar/ways-to-bind-dropdown-list-in-Asp-Net-mvc/

Resources