Good day. I am new to grails and I have to design a music-shopping type app. I have a Cart domain and a User domain and what I'm trying to do is when I create a new User object I want to have a new Cart object created too that links to said User object in the User. I'm using static scaffolding so I want to do this in the User/save action. Here's my code:
class Cart {
String item
Integer quantity
BigDecimal price
String type
Integer typeId
User user
static constraints = {
type nullable:false, blank:false, inList:["Album", "Song", "Empty"]
typeId nullable:false, blank:false
}
}
class User {
String username
String password
String fName
String lName
String email
static constraints = {
username(nullable : false, blank : false, minSize : 1)
password(nullable : false, blank : false, minSize : 1)
fName(nullable : false, blank : false, minSize : 1)
lName(nullable : false, blank : false, minSize : 1)
email(nullable : false, blank : false, email : true)
}
}
class UserController {
//static scaffolded code (index, show, create, etc.)
#Transactional
def save(User userInstance){ //This whole method is also generated with scaffolding
if (userInstance == null) {
notFound()
return
}
if (userInstance.hasErrors()) {
respond userInstance.errors, view: 'create'
return
}
userInstance.save flush: true
//This is what I've been trying to do but it doesn't work and I don't know why :(
def myCart = new Cart(user:userInstance, item:'empty item', quantity:0, price:0, total:0, type:'Empty', typeId:0).save(flush:true)
//Rest of the generated code
}
//More generated code
}
Any help would be appreciated. Thanks!
You have 2 bugs in Cart class.
First: update typeID to typeId
Second: type should be String, not BigDecimal
I think this link explains well on how to do cascading in Grails.
Cascading determines what type of actions, when applied to a domain instance, also apply to the relations of that instance.
According to how the cascading goes, I think it makes more sense that you have a Cart object in User class and use belongsTo to let Grails know the Cart is saved or deleted with the User.
Related
I am facing some issues with writing custom validators (Commands) in grails 3.3.3. Specifically, I am trying to validate POST request whose body is composed of a list of items. This is what I have...
The Command:
class VoteCommand implements Validateable {
List<VoteItem> postList = [].withLazyDefault { new ListItem() }
static constraints = {
postList nullable: false
}
class ListItem implements Validateable {
String tag
String some_id
static constraints = {
some_id nullable: false, blank: false
tag nullable: false, blank: false
tag inList: Tag.values() as List
}
}
}
AND the Payload:
{
"noteVotesButWorks": [
{
"tag": "good"
},
{
"tag": "bad"
}
]
}
This payload passes the validation check in my controller action.
def save(VoteCommand command) {
println(command.errors) //grails.validation.ValidationErrors: 0 errors
if (command.hasErrors()) {
respond params.errors, view: 'create'
} else {
withFormat {
'*' { render status: CREATED }
}
}
}
After making the POST request to this action, I get a 201 and grails.validation.ValidationErrors: 0 errors printed to stdout.
Please, can someone here give me some pointers?
Please, can someone here give me some pointers?
Your payload includes the key noteVotesButWorks. The data binder is going to create an instance of VoteCommand and then look to see if there is a noteVotesButWorks property on that instance, and there isn't, so the data binder doesn't really have anything to do. Your VoteCommand instance is then validated, which passes because your only constraint is postList nullable: false, which passes because postList is not null.
That all is working as designed. You probably want the key in your payload map to match the name of the List property in VoteCommand.
Separate from all of that, there is no good reason to include .withLazyDefault { new ListItem() } in your property initialization. You don't really have to initialize the property at all. The data binder will do that for you.
I don't think you want nullable: false for postList. An empty list is not null. I think you want minSize: 1.
I want to use grails hibernate filter plugin to add a filter on of my domain class.
http://grails.org/plugin/hibernate-filter
Domain classes:
class Movie {
String name
String genre
String yearOfRelease
boolean deleted
}
class EditRequest {
String reason
String requester
Date requestDate
String status //can be 'PENDING', 'ONHOLD', OR 'COMPLETE'
static belongsTo = [
movie: Movie,
requester: User
]
}
There could be multiple edit request for a movie.
I have an API where I need to display all edit requests for all non-deleted movies.
How do I add hibernateFilter for non-deleted movies in my EditRequest domain class
I tried below in my EditRequest class, but non of them works.
1.
static hibernateFilters = {
deletedMovieFilter(condition:'deleted=false', default:true)
deletedMovieFilter(collection:'movie', default: true)
}
2.
static hibernateFilters = {
deletedMovieFilter(condition: 'deleted = false')
deletedMovieFilter(collection: 'movie', joinTable: true)
}
I have Many-To-Many relationship between RentalUnit and Review(there may be review for guests staying in multiple rental units). There is cascading on Delete from RentalUnit to Review but none cascading from Review to RentalUnit
While working with tests, i found following inconsistency in GORM session
def review2 = new Review(rentalUnits: [rentalUnit], ...., isApproved: false).save(flush: true)
review2.addToRentalUnits(rentalUnit2)
The 'rentalUnit2' object will have association to the 'review2' whereas the 'rentalUnit' does not.
How do i ensure consistent session while pass RentalUnit object at initialization or via addTo*?
p.s. Here is complete code
class Review {
String submittedBy
String content
String dateReceived
boolean isApproved
final static DateFormat DATEFORMAT = DateFormat.getDateInstance(DateFormat.MEDIUM)
static belongsTo = RentalUnit
static hasMany = [rentalUnits: RentalUnit]
static mapping = {
rentalUnits cascade: "none"
}
static constraints = {
submittedBy blank: false, size: 3..50
content blank: false, size: 5..255
dateReceived blank: false, size: 11..12, validator: {
try{
Date date = DATEFORMAT.parse(it)
return DATEFORMAT.format(date) == it
}catch(ParseException exception){
return false
}
}
rentalUnits nullable: false
}
}
class RentalUnit {
String name
String nickname
Address address
static hasMany = [reviews:Review]
static mapping = {
reviews cascade: "all-delete-orphan"
}
static constraints = {
name blank: false, unique: true
nickname blank: false
}
}
Your answer is in your question - use addToRentalUnits. It does three things; it initializes the collection to a new empty one if it's null (this will be the case for new non-persistent instances, but not for persistent instances from the database which will always have a non-null (but possibly empty) collection), adds the instance to the collection, and sets the back-reference to the containing instance. Simply setting the collection data just does the first two things.
I have a form to create a place. Depending of the country, the province (state, region) field is required or not.
When is not required, I want to be null, not empty string. I have code that makes all empty form fields, null:
def newparams = [:]
place = new Place()
params.each() { k, v ->
if (v instanceof String && place.hasProperty(k)) {
if (!v.trim().length()) {
newparams[k] = null
} else {
newparams[k] = v
}
}
}
place = new Place(newparams)
place.validate()
Now, in the place domain, I have a validator on the province:
province validator: {val, obj -> if (obj.country in obj.requiresRegionCountries() && !obj.province) return [province.required]}
With this rule, I always get "province can't be null" even if it is required or not.
I think this is because the nullable validator that is set default to false.
If I am adding nullable: true, then even if province is required, the custom validator is skipped and it is possible to save with empty province (I think that is because it gets instantiated with null)
Now, I need somehow my custom validator and also ability to specify the nullable in my validator, something like this:
province validator: {val, obj ->
if (obj.country in obj.requiresRegionCountries() && !obj.province) {
nullable: false
return [province.required] }
else {
nullable: true
}
}
How can I achieve this in Grails 2.0.3?
After lots of research and feedback I found out 2 solutions that are working. One is in controller. Do not add any validation in model and add them dynamically from controller:
class PlacesController {
def create() {
def place = new Place(params.address)
if (place.country in placesThatRequiresProvinceArray) {
place.constrains.province.nullable = false
} else {
place.constrains.province.nullable = true
}
}
The other solution is the one proposed by Tri in this thread, but put the custom validator before the nullable constraint (else the custom validator will not be called for null values):
static constraints = {
province (validator: {val, obj ->
if (obj.country == 'Canada' && !val)
return ['province.required']
}, nullable: true)
}
I can't tell with the code you've pasted but if your problem is that the default validation doesn't allow province to be null, have you tried explicitly allowing province to be null? You are allowed multiple validators for each field. So back in your original code, just specify the nullable validator as well:
province nullable: true, validator: {val, obj ->
if (obj != null && obj.country in obj.requiresRegionCountries() && !obj.province)
return [province.required]
}
EDIT:
In the custom validator, might also want to guard against the obj being null in the if condition.
EDIT2: Demo project showing the above validation working on grails 2.0.4
class Place {
String country
Province province
static constraints = {
province (nullable: true, validator: {val, obj ->
if (obj.country == 'Canada' && !val) return ['province.required']
})
}
}
Controller...
class MainController {
def index() {
def place = new Place(country: 'Canada')
if (!place.validate()) {
render "need province<br/>" + place.errors
} else {
render "cool"
}
So the idea is that I have a dummy controller where I can invoke the index action which is hardcoded to create a Place domain instance similar to your example. Notice I only defined the country string so I can key my logic on that for the custom validation. I didn't define the province when creating the Place instance so it should be null. Under that scenario, the response page will print the following...
Output snippet ...
need province
grails.validation.ValidationErrors: 1 .... does not pass custom validation]
If I remove the nullable: true constraint from Place, then the error is the null value as expected...
Output snippet ...
need province
grails.validation.ValidationErrors: 1 .... cannot be null]
:D
I was following a tutorial in a book, and I did follow it thoroughly.
However, come the part where I am supposed to write an integration test, it suddenly failed saying: Cannot invoke method addToPosts() on null object right after I ran the test. I wonder, what could be wrong... :| Please help! :) Below is the code for the test:
void testFirstPost() {
def user = new User(userID: 'joemillan', password:'youaretheonly',
homepage: 'www.geeee.com').save()
def post = new Post (content: 'hellloo oasdo sjdosa daodkao ')
user.addToPosts(post)
assertEquals 1, User.get(user.id).posts.size()
}
Here is the user class:
class User {
String userID
String password
String homepage
Profile profile
static hasMany=[posts:Post, tags:Tag]
static constraints = {
userID (unique: true, size: 6..20)
password (size: 6..20, validator:{password,userID-> return password !=userID.userID}) //validator = The password must not match the username.
homepage (url:true, nullable: true)
profile (nullable: true)
}
}
Here is the Post class:
class Post {
String content
Date dateCreated
static constraints = {
content (blank:false)
}
static belongsTo = [user:User]
static hasMany = [tags:Tag]
static mapping = {
sort dateCreated: "desc"
}
}
save() returns null if validation fails, and "www.geeee.com" isn't a valid URL. It would work with "http://www.geeee.com".
But you should split the creation and saving into 2 steps so you can check it:
def user = new User(userID: 'joemillan', password:'youaretheonly',
homepage: 'www.geeee.com')
user.save()
assertFalse user.hasErrors()
or use failOnError if you are confident that that part should succeed and only want to test the other parts, e.g.
def user = new User(userID: 'joemillan', password:'youaretheonly',
homepage: 'www.geeee.com').save(failOnError: true)