How to nullify the fields that fail validation and save the domain? - grails

Suppose there is a domain as defined below.
class Book {
String title
Author author
}
Now, i save an instance of this domain. The author domain has some constraints. So, during save the validation for author fails now instead of not saving the whole domain i want to nullify the author(The author can be null) and save the title string as it was. In other words how do i nullify any number of fields whose validation failed and save rest of the properties values? Is there a convenient way to do so? Thanks!

This could be one of following :
In beforeInsert hook do something like below:
def beforeInsert() {
this.validate()
if(this.hasErrors()){
// get all errors and iterate through it and set the field to null for same
}
}
While saving the domain you could use
domain.save(validate:false)
Thanks!!

Related

Grails binding one to one associations

When you generate grails views, grails looks at your relationships and generates the right html for your form data to be automatically binded to the back end domain. For one to one associations grails creates a drop down list.
However, you might not want to present that property as a drop down list but something more custom (for example a text field with autocomplete). As soon as you do that the value that comes to the controller from that field, comes in as a String and you have to first:
Clear errors
Perform a findBy based on a given param and assign it to the property of the domain
I really want to avoid doing findBys in the controller as much as possible because it seems like I am doing logic/things that should not go there. The controller should delegate to the Service layer. It is not clear to me from the grails documentation how would I do that by using bindData which seems to work really well with String, date, Integer properties etc.. but I do not see how bindData is used for properties that are other domains.
I also really want to avoid passing the params object to the Service layer as it seems less reusable (or maybe not, correct me if I am wrong). I guess that I do not like how it looks semantically. I would prefer the first over the second:
#Transactional
class WithdrawService {
def addWithdraw(Withdraw withdraw) {
//perform business logic here
}
def createWithdraw(Map params){
//perform business logic here
}
}
Let's take the following example:
class Withdraw {
Person person
Date withdrawDate
}
and the parent lookup table
class Person {
String name
String lastName
static constraints = {
}
#Override
public String toString() {
return "$name $lastName"
}
}
In order for the bind to happen automatically without any extra work grails passes in the following request params to automatically bind the one to one:
person.id
a person map with the id.
[person.id:2, person:[id:2], withdrawDate:date.struct, withdrawDate_month:11, create:Create, withdrawDate_year:2015, withdrawDate_day:10, action:save, format:null, controller:withdraw]
What is the best way to go about this?
Pass two hidden fields that look exactly like this: person.id:2, person:[id:2] that get populated as a result of the Ajax call that populates the autocomplete?
In the controller do a Person.findBySomeKnownProperty(params.someKnownValue)
Or any other approach?

Grails/Gorm: how to filter a list of domain objects without affecting the database

Say we have something like the standard Book domain object and bookCategory object. In my controller I want to return a subset of list of books to the view. That subset is not achievable using a find query. When I try to filer the return object, it deletes relationships from the database!
I tried this:
class BookCategory{
String name
static hasMany = [books:Book]
}
class Book{
String title
}
def myController() {
def categories
categories = BookCategory.list()
def user = getCurrentUser()
categories.each { category ->
category.books.removeAll { book ->
!isBookBannedForThisUser(book.title, user)
}
[bookCategories: categories]
}
}
The problem is that it permanently removes these books from the categories for all users from the database!!!
I tried putting the method in a service and using a readonly transaction, but this did not help.
I assume that even if I copy all the categories and books into new list, they will still update the DB as they will still have the book IDs (which I need)
Saving to the database when you dont say save() is very dangerous. is there a way to disable this feature completely?
There is a fundamental flaw in your approach. Do not modify your domain instances if you don't intend to have the changes persisted. Doing so is going to cause you headaches.
Your domain model is suppose to be your system of record. Any changes to it are suppose to be persisted.
If you need to gather up data and manipulate it without having it reflected in your domain model then use a DTO (data transfer object) or similar pattern.
Simply calling .discard() will discard the changes you have made from being persisted when the session automatically flushes.
Instead of working against the framework, and disabling behavior, change your approach to be correct.

Operation save in Grails on a POGO

I'm beginner in Grails and I have a problem when I try to save a POGO
I have created 1 domain class
class Book {
String title
}
Then, I have generated the controller and view automatically.
Now, I want to be able to create a book with the code by clicking "create" (I know it is possible directly with the code generated but for my example I want to do it by the code). To do this, I have modified the method 'save(Book bookInstance)' in the controller like this
#Transactional
def save(Book bookInstance) {
def book = new Book(title:"New Grails Book").save()
But, when I go to the URL localhost:8080/myApp/book/create and then I click "Create", I have the error
message -> /myApp/WEB-INF/grails-app/views/book/save.jsp
description -> The requested resource is not available.
When I put this code in bootStrap, it is OK, so I don't understand why it is not in the controller
When you have a hasMany property in a domain class, Grails adds a Set property to the domain class with an AST transformation (so it's actually there in the bytecode, and it's visiable to Java) to represent the collection, and when you add a belongsTo a field of that type is added. So it's as if you had this code:
class Author {
Set<Book> books
static hasMany = [books: Book]
String name
}
and
class Book {
Author author
static belongsTo = [author: Author]
String title
}
The AST xform uses the map key as the field name, so you can use any valid field name, but the convention is to do what you did.
Properties are nullable:false by default, so your code doesn't save the Book instance because you didn't set the author property. When doing this explicitly you typically don't create the Book directly, but instead add it to the Author's collection using the dynamic addToBooks method. This sets the author field back-reference and when you save the author, the book is transitively validated and saved. This is all handled for you when you have code like new Book(params).save(), and you can do it directly, e.g.
Author author = ...
def book = new Book(title:"New Grails Book", author: author).save()
If you're using a generated controller and GSPs, there should be an author id in the params map, it'll likely be author.id, so that first line would be
Author author = Author.get(params['author.id'])
but you can add
println params
at the top of the action method to see all of the submitted params.
In general you don't want to look at the return value of the save call, since it will be null if there's a validation error and there's no way to retrieve the errors. So change
def book = new Book(...).save()
to
def book = new Book(...)
book.save()
and now you can call book.hasErrors(), book.getErrors(), book.errors, etc. to see if it was successful and if not, what went wrong.
But that's not the exact problem you're seeing, just one you will when you fix your problem. There's no save.gsp, and Grails also looks for save.jsp and confusingly includes that name in the not-found message. The save method is accessed via a POST request, typically from the form generated by the create action, and it either re-displays create.gsp with the submitted data and error messages when validation fails, or redirects to the view action when the save succeeds. There's no need for a save.gsp when using the generated code.

Projecting Grails Searchable plugin result set

I have a complex searchable configuration for a domain class and its associated domain classes. when I search for about 200 results (max:200) it takes too long to respond.
in the result set I have all fields (simple or association) specified for search in my domain class. I need to return only an id list and ignore other fields of domain class. is it possible? I want to do this for speeding up my search. this id list will be used for querying another no-sql db. it seems that fetching all of the fields is slowing down my search.
I think you can achieve what you want (let the property be searchable but not return it) by setting the property store to no.
For example:
class MyDomain {
String name
String email
static searchable = {
email index:'analyzed', store:'no'
name index:'analyzed'
}
}
In this domain I say that name and email are indexed and analyzed (so they can be searched) but the email property is not being stored, so it will be null when the object is returned. For other properties check: http://grails.org/Searchable+Plugin+-+Mapping+-+Searchable+Property

How do you validate a datatype in umbraco?

I've created a custom data type in umbraco and I would like to check the value before it is saved and provide error messages if it is not ok.
How do you enforce validation rules on a data type?
have you consider using ValidationProperty for this purpose?
Once you added the attribute, you can implement the property by adding your validation logic. This way you can make sure about data validation before you user save the record with new custom datatype.
E.g
[ValidationProperty("HasValidValue")]
public partial class MyDataEditor : System.Web.UI.UserControl,
umbraco.editorControls.userControlGrapper.IUsercontrolDataEditor
In your Custom datatype logic and then has something like following....
public string HasValidValue
{
get
{
if (CheckIfDataIsValid())
return "Valid";
else
return String.Empty;
}
}
This way you can do it at code level.
Thanks,
Jigar

Resources