user defined form validation in grails - validation

I want to validate three form fields.
The usual method is like this:
class User {
String name
String password
String personalInfo
static constraints = {
name(size: 4..20, unique:true, blank:false)
password(size:8..20, blank:false)
personalInfo(size: 1000, nullable:true)
}
}
but in my gsp i'll be having all three textfields in the same name
eg:
<td>Name:</td><td><g:textfield name="property"/></td>
<td>Password:</td><td><g:textfield name="property"/></td>
<td>PersonalInfo:</td><td><g:textfield name="property"/></td>
How to validate this form????

It is a really bad practice to give the same name to input fields in a form that are going to be mapped to different properties in the same domain class.
But if you cant, the fields will be sent as a list to your controller. So you can extract values in the controller like this:
def parameterList = params.property as List
def name = parameterList[0]
def password = parameterList[1]
def personalInfo = parameterList[2]
Then you can create your User object
def user = new User(name:name, personalInfo:personalInfo, password:password)
if (user.save()){ } else{ }
As the best practice you must name your fields in the form differently

Related

Grails Command Objects custom Validate Message Codes

When using command objects like:
class UserCommand {
String name
static constraints = {
name blank: false, unique: true, minSize: 3
}
}
you can use them to validate objects without making them persistent. In my case I would validate for a persistent class User.
In controller:
def save(UserCommand cmd) {
if(!cmd.validate()) {
render view: "create", model: [user: cmd]
return
}
def user = new User()
user.name = cmd.name
user.save()
redirect uri: '/'
}
in messages.properties:
user.username.minSize.error=Please enter at least three characters.
userCommand.username.minSize.error=Please enter at least three characters.
When using custom validation messages you have to write the message codes for each error twice. One for the User class and another for the UserCommand class.
Is there a way how I can have only one message code for each error?
I might be wrong here but if you're using just the stock Grails constraints, the only way to share a validation message is to simply rely on the default.x.x.message key/values in messages.properties. Otherwise messages are looked up via the following key form:
className.propertyName.errorcode...=
You can however use a custom validator and override what message key gets returned for the validation error.
class User {
...
static constraints = {
...
name blank: false, unique: true, validator: { value, user ->
if(!value || value.length() < 3)
return 'what.ever.key.in.messages.properties'
}
}
}
Then you can keep it all DRY by sharing constraints between classes via a global constraint or as #dmahapatro mentioned, with the use of an importFrom in your UserCommand like so,
class UserCommand {
...
static constraints = {
importFrom User
...
}
}
If you have more complicated validation, you can create your own constraints classes. Here are some resources:
http://www.zorched.net/2008/01/25/build-a-custom-validator-in-grails-with-a-plugin/
http://blog.swwomm.com/2011/02/custom-grails-constraints.html
using unique constraint in CommandObject makes no sense, because uniqueness of what would it check?
you can validate domain objects without persisting them exactly the same way as command objects - using validate() method
you can put a User object in command object, set constraints only for the domain class, and then validate User object being a part of command object
class User {
String name
static constraints = {
name blank: false, unique: true, minSize: 3
}
}
class UserCommand {
User user
static constraints = {
user validator: { it.validate() }
}
}
user.username.minSize.error=Please enter at least three characters.

Grails validation no Constraints block

The usual way of constraint validating domain objects in Grails is by defining a static constraints closure in the domain object, after which one can call myObject.validate().
Is it possible in some way to pass what should be validated to the validate call and not define a constraints block?
I guess it could be done with Spring validator instances, but is there an easier way where you still have the "goodies" from Grails (max, email, etc.)?
You can pass a list of fields into .validate(["fieldA", "fieldB"]) and it will only validate those fields. You still need a constraints block to define what the constraints are for those fields.
From Grails User Guide:
"The validate method accepts an optional List argument which contains the names of the properties to be validated. When a List of names is specified, only those properties will be validated."
To respond to #eugene82:
#Validateable
class MyDomainValidator {
String email
static constraints = {
email(email:true)
}
}
MyDomain m = new MyDomain()...
MyDomainValidator mv = new MyDomainValidator()..
mv.properties = m.properties
mv.validate()
I imagine such an approach: Create a #Validateable groovy class with static constraints inside with any Grails goodies you need. Let it own all fields like your domain does (e.g. by extending the domain), and provide a way to copy all fields from a domain instance to a new instance of that object, e.g. using a constructor. Than call validate() on that class.
class MyDomain {
String email
MyDomain() {}
MyDomain(email) {
email = this.email
}
}
#Validateable
class MyDomainCustomValidator extends MyDomain {
MyDomainValidator() {}
MyDomainValidator(domain) {
super(domain.email)
}
static constraints = {
email email: true
}
}
def d = new MyDomain('e#mail.com')
new MyDomainCustomValidator(d).validate()

How to pass value from one action to another action having different views in mvc3

Hi I am developing an application in MVC3.
and i am stuck at one place.
I have 2 fields in my view which are not a part of my class.
i am just using them to populate the dropdown
The 2 fields are State and District.
But i want to show the selected value of the two fields in another View.
I tried it using ViewBag but this doesnot work and gives me Null.
this is inside Create get method:
int stateid = Convert.ToInt32(formcollection["ProvincialState.ProvincialStateID"]);
int districtid = Convert.ToInt32(formcollection["District.DistrictID"]);
ProvincialState state = new AddressService().FetchStateByStateId(stateid);
District district = new AddressService().FetchDistrictByDistrictId(districtid);
ViewBag.State = state.ProvincialStateName;
ViewBag.District = district.DistrictName;
and this is inside Details View:
string data1 = ViewBag.State;
string data2 = ViewBag.District;
ViewBag.State = data1;
ViewBag.District = data2;
I cannot use post method of Create coz i need to show this data only on another view.
or if is their any method thru which i can show the data in the same view.
ViewBag is like ViewData. In order to have information between pages you should store that info in session. You can do that in 2 ways:
Use a session variable like:
this.Session["SessionVariable"] = value;
Or use the this.TempData object:
this.TempData["SessionVariable"] = value;
The difference between this two is that the TempData object will be deleted from the session when it is read.
TempData is just a wrapper of the session.
You can send this fields to your action in addition to model, and then store it in session for example:
public class SomeController: Controller
{
[HttpPost]
public ActionResult DoSomething(MyModel model, int state, int district)
{
// validating model...
// ...
Session["state"] = state;
Session["district"] = district;
// some other logic...
}
}

Filter select drop down using Grails security ACL

I am implementing ACL security using the spring-security-acl plugin. I have the following domain classes:
package test
class Subitem {
String name
static belongsTo = [employer: Employer]
static constraints = {
name blank: false
}
}
package test
class Employer {
String name
static hasMany = [users: User, items: Subitem]
static belongsTo = User
static constraints = {
name blank: false, unique: true
}
String toString() {
name
}
}
In the create.gsp file which is used to create a Subitem, there is the following statement:
<g:select id="employer" name="employer.id" from="${test.Employer.list()}" optionKey="id" required="" value="${subitemInstance?.employer?.id}" class="many-to-one"/>
From the EmployerController:
def list = {
params.max = Math.min(params.max ? params.int('max') : 10, 100)
[employerInstanceList: employerService.list(params),
employerInstanceTotal: employerService.count()]
}
Following the tutorial given here, I have moved some of the functionality with dealing with Employer to a service called EmployerService:
#PreAuthorize("hasRole('ROLE_USER')")
#PostFilter("hasPermission(filterObject, read)")
List<Employer> list(Map params) {
Employer.list params
}
int count() {
Employer.count()
}
Access to information in any given Employer class instance is restricted using ACL. At present, I can see ALL instances of Employer in the database in the drop down, and I assume that is because I am using the controller list(), not the service list() - however, I only want to see the filtered list of Employer domain classes. However, if I replace the g:select with:
<g:select id="employer" name="employer.id" from="${test.EmployerService.list()}" optionKey="id" required="" value="${subitemInstance?.employer?.id}" class="many-to-one"/>
then I get an internal server error because I haven't passed a Map parameter to the service list() function (and I don't know how to do this within the tag):
URI /security/subitem/create
Class groovy.lang.MissingMethodException
Message No signature of method: static test.EmployerService.list() is applicable for argument types: () values: [] Possible solutions: list(java.util.Map), is(java.lang.Object), wait(), find(), wait(long), get(long)
I only want to see the information that comes from the EmployerService list() function - how do I do this please? How do I reference the correct function from within the gap?
Edit 16 Mar 0835: Thanks #OverZealous, that's really helpful, I hadn't realised that. However, I've tried that and still get the same problem. I've put a println() statement in both the Employer and EmployerService list() functions, and can see that neither actually seems to get called when the g:select tag is parsed (even if I leave the g:select to refer to Employer). Is there another version of the list() function that is being called perhaps? Or how else to get the g:select to take account of the ACL?
Just change your method signature in the Service to look like this:
List<Employer> list(Map params = [:]) {
Employer.list params
}
The change is adding this: = [:]. This provides a default value for params, in this case, an empty map.
(This is a Groovy feature, BTW. You can use it on any method or closure where the arguments are optional, and you want to provide a default.)
OK, I worked it out, and here is the solution to anyone else who comes up against the same problem.
The create Subitem page is rendered by means of the Subitem's create.gsp file and the SubitemController. The trick is to amend the SubitemController create() closure:
class SubitemController {
def employerService
def create() {
// this line was the default supplied method:
// [subitemInstance: new Subitem(params)]
// so replace with the following:
params.max = Math.min(params.max ? params.int('max') : 10, 100)
[subitemInstance: new Subitem(params), employerInstanceList: employerService.list(params),
employerInstanceTotal: employerService.count()]
}
}
So now when the SubitemController is asked by the g:select within the Subitem view for the list of Employers, it calls the EmployerService, which supplies the correct answer. We have simply added 2 further variables that are returned to the view, and which can be referenced anywhere within the view (such as by the g:select tag).
The lesson for me is that the View interacts with the Controller, which can refer to a Service: the Service doesn't play nicely with a View, it seems.

Initializing a django modelform with data from url

I have two models for store and city:
class City(models.Model):
name = models.CharField()
slug = models.SlugField()
class Store(models.Model):
name = models.CharField()
slug = models.SlugField()
city = models.ForeignKey(City)
If I have my Add Store url designed as
r^'mysite.com/city/(?[-\w]+)/venue/add$'
where the represents the City.slug field can I initialize a StoreForm that automatically populates the Store.city field from the url data?
In your template, define your link as follows.
{%url somename relevant_slug_text%}
Or :
href='/mysite.com/city/{{slug_text}}/venue/add'
In your url conf, define your url like:
url(r^'mysite.com/city/(?P<slug_text>\w+)/venue/add$', 'func_name', name='somename')
So, you can pass the value of relelvant_slug_text variable to your url as slug_text, and in your function definiton:
def func_name(request, slug_text):
So , you can pass text value to your funcrtion with slug_text parameter...
EDIT:
There are tow ways...
One:
Crete your city selection form using ModelForm..., then inthe second step, use posted data to populate your form again like:
form = StoreForm(request.POST)
then you can render this form to your template...
But if it is not possible o use this, yo ucan do the following...
Since you are using ModelForm to create your forms:
class StoreForm(forms.ModelForm):
# your form field definitions are here
im your function, just override city field, but at this point, since you use modelform, your form thml will be created as
<select>
<option value="id of record">"unicode value of record"</option>
So, you have record id's as option values. And you have slug_field values to initialize your field. So you have to get related city to use it...
my_city = City.objects.get(slug=<your_slug_value>)
Now you can override the form, but you must do it before you pass your form to a variable to render to your template...
StoreForm.base_fields['city'] = forms.ModelChoiceField(queryset=City.objects.all(), initial=my_city)
form = StoreForm()

Resources