I am doing a sample applications in grails I created a Sponsor domain class, then I generated its corresponding controller which is
class SponsorController
{
...
def update(Long id, Long version){
def sponsorInstance = Sponsor.get(id)
if (!sponsorInstance) {
flash.message = message(code:'default.not.found.message',args:message(code:'sponsor.label',
default: 'Sponsor'),
id
])
redirect(action: "list")
return
}
if (version != null) {
if (sponsorInstance.version > version) {
sponsorInstance.errors.rejectValue("version", "default.optimistic.locking.failure",
[
message(code: 'sponsor.label', default: 'Sponsor')] as Object[],
"Another user has updated this Sponsor while you were editing")
render(view: "edit", model: [sponsorInstance: sponsorInstance])
return
}
}
sponsorInstance.properties = params
if (!sponsorInstance.save(flush: true)) {
render(view: "edit", model: [sponsorInstance: sponsorInstance])
return
}
flash.message = message(code: 'default.updated.message', args: [
message(code: 'sponsor.label', default: 'Sponsor'),
sponsorInstance.id
])
redirect(action: "show", id: sponsorInstance.id)
}
when I generated a controllers it generated list,show,save,create,edit and update methods. I understood all the methods code but I am little confused in update's method code in which it took two parameters id and version so my question is what is version here and what is the purpose of using version here
version is added to each domain class by default and is used to implement optimistic locking. Consider the following scenario
User A retrieves an instance of domain class
User B retrieves an instance of domain class
User A updates the instance
User B updates the instance
If we simply allow user B's instance to persist he'll effectively overwrite the changes made by A. In some applications this might be OK, but in others, we might want to tell user B that the object has changed since he read it, and show him the changes instead of overwriting user A's update.
The version property is how this scenario (known as a "dirty write") is detected, i.e. it is used to check that the version of an object that was read is the same version that is currently in the database. Each time an update is made to an object, the version column is incremented.
Related
I have problem with save data in DB.
This is my orders_controller:
def create
order = meal.build_order(order_params)
respond_with order
end
Function in orders factory:
o.create = function(order) {
return $http.post('/orders.json', order).success(function(data){
console.log(data);
o.orders.push(data);
});
};
Call orders.create in ordersCtrl (in angular controller)
if (!angular.isUndefined($scope.meal)) {
orders.create({
meal_id: $scope.meal.id,
status: "ordered",
});
}
What can be wrong with that code? When I've wrote order.save in Rails controller - order has been saved.
From the docs:
build_association(attributes = {})
Returns a new object of the associated type that has been instantiated with attributes and linked to this object through a foreign key, but has not yet been saved.
So basically when you use build_order it is not saving it (and this is how rails works). You could use create_order.
I recently created a new controller in a pretty big app, (note: we just upgraded from Grails 2.0.3 to 2.3.7) and the created tests have all been failing inexplicably. Most of my errors consist of the model being [:] after a controller call. And therefore any methods on that model being null. I get stuff like:
groovy.lang.MissingMethodException:
No signature of method:
TransitionController.save() is applicable for argument types: (Transition)
values: [null] Possible solutions: save(), wait(), show(), any(),
wait(long), raw(java.lang.Object) at TransitionControllerSpec.Test the
save action correctly persists an instance(TransitionControllerSpec.groovy:45)
I have tried explicitly assigning the model by doing:
def model = controller.delete()
def model = controller.update()
//....
But I get the same result, an empty model map and null values if I try to access it.
As per this article (https://jira.grails.org/browse/GRAILS-8462) I could also try accessing the model by doing:
def model = controller.modelAndView.model
But this did not work for me either, producing the same results.
Any ideas on what might be happening?
Edit: Here are the first couple of tests
package com.hey.how.are.ya
import grails.test.mixin.*
import spock.lang.*
import org.junit.*
#TestFor(TransitionController)
#Mock(Transition)
class TransitionControllerSpec extends Specification {
def populateValidParams(params) {
assert params != null
params['reason'] = 'just cuz'
}
void "Test the index action returns the correct model"() {
when:"The index action is executed"
controller.index()
then:"The model is correct"
!model.transitionInstanceList
model.transitionInstanceCount == 0
}
void "Test the create action returns the correct model"() {
when:"The create action is executed"
controller.create()
then:"The model is correctly created"
model.transitionInstance!= null
}
//...grails generated tests here
}
Edit: Here's an interesting case! If I do:
void "Test the create action returns the correct model"() {
when:"The create action is executed"
def model = controller.create()
def inst = new Transition()
println "${model}"
println "${model.transitionInstance}"
then:"The model is correctly created"
model.transitionInstance != null
}
Then I get this as output:
[transitionInstance:null]
null
But the test passes. This only happens with create. What is going on??
Edit: Adding code for create and save
def create() {
[transitionInstance: new Transition(params)]
}
def save() {
def transitionInstance = new Transition(params)
if (!transitionInstance.save(flush: true)) {
render(view: "create", model: transitionInstance: transitionInstance])
return
}
flash.message = message(code: 'default.created.message', args: [message(code: 'transition.label', default: 'Transition'), transitionInstance.id])
redirect(action: "show", id: transitionInstance.id)
}
Edit: I can't spend too much time on this, but I'm pretty sure the problem is discrepancies between the controller generated and the test created. For what it's worth I was running version 2.0.2 of the scaffolding plugin and grails 2.3.7. I'm gonna throw out the tests created by the command and start from scratch, thanks for the help!
I had the same issue but solved it like following:
Inside the controller method:
render view: "_myTemplate", model: [instance: myInstance]
So the controller, still renders the template and inside the test spec I can test
assert(model.instance = myExpectedInstance)
Can I do something like this inside controller action
def update(){
if(certain validation fails )
//flash message to be visible
[domainInstance:originalDomainInstance,flash.message : "message(code:'default.some.code.label', default:'Please provide your validation err msg.' )"]
}
The above throws syntax error.
I am still trying to fix the syntax. Is it alrite to send the flash message in this way ?
Grails controller returns model which is in simple case just map (map with domainInstance in your case). flash is property which is available in controllers.
def update(){
if(certain validation fails ) {
flash.message = message(code: 'default.some.code.label', default: 'Please provide your validation err msg.')
}
[domainInstance: originalDomainInstance]
}
If you would to pass message in model map the syntax should look like below:
[domainInstance: originalDomainInstance, myMessage: message(code: 'default.some.code.label', default: 'Please provide your validation err msg.')]
Useful links: flash and models and views in Grails.
I'm trying to build a grails (2.1.0) application on top of a legacy database. It has a ton of tables, and I'd like very much to use only dynamic scaffolding. The issue is that some of the tables have a string as primary key, but template code in src/templates/scaffolding/Controller.groovy for e.g. show is
def show(Long id) {
def ${propertyName} = ${className}.get(id)
if (!${propertyName}) {
flash.message = message(code: 'default.not.found.message', args: [message(code: '${domainClass.propertyName}.label', default: '${className}'), id])
redirect(action: "list")
return
}
[${propertyName}: ${propertyName}]
}
For the string keys, this seems to turn the string into null, and the get fails with the error $Domain not found with id null.
If I run a generate-controller and change the signature to def show(String id), it works as expected.
So, is there a way to inspect the domain class at "dynamic scaffolding time" and write the method accordingly?
Within the controller template you have a domainClass variable that gives you access to the GrailsDomainClass representing the class for which you are generating the controller, so you can do something like this (and likewise for edit, update and delete):
def show(${domainClass.identifier.type.name} id) {
which should generate def show(java.lang.Long id) or def show(java.lang.String id) as appropriate.
I've seen various answers to similar questions, but not for this specific case:
I have a Grails gsp with several fields and a submit button. I want to enter a value in the first field, click submit, and have the controller return values into the remaining fields (including the original entry field). I want to do this with a simple page refresh (no AJAX) and it does not involve a database. I have it working fine with two gsp pages that are identical but have different names. I just call the first one, enter the value, and have the submit action point to the second. But I would like to be able to do it with a single gsp. A simplified version of my controller looks like this:
class CalculatorController {
def index = {
redirect(action: "input")
}
def input= {}
def calculateValues = {
def inputString = params.enterValue
def value2 = 'You entered something on the page'
[field1: inputString, field2: value2]
}
}
you need to have all of logic inside of the same closure if you want to use only one gsp. Test if the params are passed in and return the correct vals
Actually you don't need to have all of your logic in the same closure. Simply specify the view template to use in calculateValues...
def calculateValues = {
def inputString = params.enterValue
def value2 = 'You entered something on the page'
render(view: 'input', model: [field1: inputString, field2: value2])
}
By using the render method you can have multiple controller action reuse the same gsp view.