Rails 3.2 Mass Assignment - ruby-on-rails-3

I'm hoping I can get some help with a mass assignment issue in my recently upgraded Rails 3.2 app.
I understand that in Rails 3.2 attributes are locked down by default and in order for them to be assigned I need to "unlock" them using attr_accessible. This works fine for normal model attributes.
However, I have a homegrown custom property mixin that allows me to add arbitrarily named properties to any model. These properties are stored in the custom_properties table. This mixin leverages method missing to look for a property from that table if I ask a model for a property like: foo.property_foobar.
Each model that uses this mixin can have X custom properties with arbitrary names. I don't have the ability to dictate the names of these properties which obviously makes it difficult to add to attr_accessible.
Does anyone have a recommendation on how I can allow mass assignment of these dynamic properties? I would rather not whitelist all model attributes.
Hopefully all of this makes sense. Thanks everyone!
Louis

One solution is to use attr_protected instead. This allows you to blacklist some attributes while allowing the rest. However, this is a bit harder to secure.
Another solution is to move assignment protection to the controller and allow/reject attributes as needed in each controller/action. There is a gem called strong parameters that allows this and it will also be included in Rails 4. I suggest this solution.
If none of the above work for you, maybe you should try another approach to implement those arbitrary attributes? For example, you could instead store them as a serialized hash in a database column.

Related

When should we use the advanced parameters of save()?

Normally we save an instance into the database simply with inst.save(), but Django uses user.save(using=self._db) in its source code. Also, it uses user.save(update_fields=['last_login']) elsewhere.
This somewhat confuses me. To make things worse, the document for the save() method is extremely simple:
Model.save(force_insert=False, force_update=False,
using=DEFAULT_DB_ALIAS, update_fields=None)[source]
If you want customized saving behavior, you can override this save()
method. See Overriding predefined model methods for more details.
The model save process also has some subtleties; see the sections
below.
It doesn't even contain the explanation of those parameters!
My question is: how do I know when I should use the advanced parameters of save()? If I'm implementing a custom model, I would definitely write user.save().
I've done a couple of experiments myself, like change user.save(using=self._db) to user.save(), and nothing went wrong, but I don't want to be surprised someday. Also, the parameters must be passed for some reasons, right?
The answer is you will know when you need to :)
For now resort to this practice
class MyModel(models.Model):
def save(self,*args, **kwargs):
# do whatever
super(MyModel,self).save(*args,**kwarags)
This way you make sure that you don't accidentally drop any of those mysterious, parameters. But let's try to demystify some of them.
using=self._db
This is to facilitate the use of multible databases in a single django app. Which most apps don't really need.
update_fields
If save() is passed a list of field names in keyword argument
update_fields, only the fields named in that list will be updated.
This may be desirable if you want to update just one or a few fields
on an object. There will be a slight performance benefit from
preventing all of the model fields from being updated in the database
https://docs.djangoproject.com/en/1.11/ref/models/instances/
So the link to the source code is a specific instance where they have used this feature. Quite useful to keep track of when a user logged in for the last time without updating the entire record.
force_insert vs force_update
These tell django to try forcing one or the other operation. Also explained to some extent in https://docs.djangoproject.com/en/1.11/ref/models/instances/
The example of user.save(using=self._db) I believe is redundant when you only have one db, usually defined as "default
. This example simply points out that if you have multiple dbs, you can pass in which of multiple dbs to use when saving.
update_fields is also handy when you keep a reference to an instance for a long time (for example in a middleware) and the data might be changed from somewhere else. In these cases you either have to perform a costly refresh_from_db() on the instance to get the newest data from the database, or when you only want to override specific attributes you can omit the refresh_from_db() call and just use save(update_fields=['attr1', 'attr2']). This will leave all other attributes unchanged in the database except for the ones you specified. If you omit update_fields in this case all values would be overwritten in the database with the values of your cached reference to the instance, leading to information loss.

FileHelper attributes limitied to fields/members only

I use the FieldQuoted attribute in my form class. I noticed that I cannot put this attribute on properties. It only allows it on members/fields. Is there a reason for this? Can this be expanded to allow for properties?
Reason I ask is that I use this class in other places that are property friendly but not member friendly at all (e.g. MVC model binder).
According to Allow Properties instead of fields #67
Yes the library started with fields in 2004 mostly because if you
support properties you must to ensure that are writable, can throw
errors, can slow down assignment with custom code, also the reflection
of .net 1.1 were incredible slow and dynamic code generation for
fields was easy to implement
Later for backward compatibility and lazyness I didn't support
properties, now autoproperties work better thanks to work in this PR
#170
But we must to do the effort and support full properties and try to
make a little impact in the previous code to avoid weird errors, maybe
with new record classes
There seem to have been some changes made against Autoproperties full support #170 but this hasn't been included in the release yet. It only seems to be available on the master branch.
I will be merging this but later I will change the way it works to
avoid use of both autoproperties and fields and also to allow
properties in the general way, but your implementation make the use of
autoproperties a simple task
If you're not happy with using that, I would suggest just creating a separate class for the import/export to mirror your existing class but using fields, as suggested by the accepted answer to Formatting properties with FileHelper. Probably the safest bet for now until they get released.

MVC : How to use database to drive validation attributes, or ALTERNATIVES?

In summary, I'm trying to create instance-specific data-annotation attributes at runtime, based on database fields. What I have now works fine for creating the initial model, but falls over when the model is posted-back and the server-validation happens.
(I have the same input model being used in a collection within a viewmodel, but different validation must be applied to each instance in the collection....for example the first occurrence of the input may be restricted to a range of 1-100 but the next occurrence of the same model, prompted for on the same input page, would be a range of 1000-2000. Another may be a date, or a string that has to be 6 characters long.......)
I'll explain what I've done and where my issues are:
I've inherited DataAnnotationsModelMetadataProvider and provided my own implementation of GetMetadataForProperty (This doesn't have any bearing on the validation problem....yet)
I've inherited DataAnnotationsModelValidatorProvider and provided a facade implementation of GetValidators. What I want to do here is create new attributes based on my database-records and then pass those attributes through to the base implementation so the Validators are created accordingly.
However...... GetValidators is called at a PROPERTY level....When it is called with a propertyname that I want to apply validators to, I need to find the applicable DB record for this propertyname so I can find out what attributes I need to create....BUT...I can't get the DB record's key from just a propertyname of the value field.....In fact, the DB key is in the parent model.....So how do I get hold of it?!
I've tried using a static variable (YUK) and storing the key during a call for one property, and retrieving it during another call for my value field property....But because the model is serialised one-way and deserialised the opposite way I end up with my key being out-of-sync with my required attributes.
To add a slight complication I'm also using a custom model binder. I've overridden CreateModel as advised elsewhere on here, but I can't find a way of attaching metadata or additionalvalues to a PROPERTY of my output model....Only to the model itself....but how do I get at MODEL metadata/additionalvalues inside the GetValidators call for a PROPERTY ?
So....My question is twofold.....
1) Can anyone help me get my database-key from my custom-Model-binder to my GetValidators method on my ValidationProvider? Or maybe using my custom Metadata provider?
2) Is there a different, simpler, way of creating validators at runtime based on database records?
I think you are making this far more complicated than it needs to be. You just need to make whatever your validation criteria selectors are part of your view model. They don't necessarily have to be displayed (they can be stored in hiddens if they need to be kept for postback purposes).
Then you can use something like FluentValidation to create rules that say
RuleFor(model => model.myprop)
.When(model => model.criteria == whatever)
.GreaterThan(100)
.LessThan(1000);
Where criteria is whatever value you use to select when your property has to be in a certain range.
So that would mean you build your view model to include the criteria that is used for validation rule selection.
I'd asked this on the FluentValidation forums also and the lack of answers here as well as the advice against using Fluent from there led me to find my own solution (I understand this almost certainly means I'm doing something really bad / unusual / unnecessary!)
What I've ended up doing is assigning my controller static variable in my Custom Model Binder's CreateModel method, where I have access to the entire client model, rather than trying to do it through a custom MetaDataProvider. This seems to work just fine and gets me towards v1 of my app.
I'm not really happy with this solution though so will look to refactor this whole area in the coming months so would still appreciate any other comments / ideas people have about how to implement dynamic validation in a generic way.
I know this is an old question, but I am answering this so that many others can be benefited from this.
Please see the below article where they are loading the attributes from an xml
Loading C# MVC .NET Data Annotation Attributes From XML, Form Validation
I think you can follow the same approach and instead of reading from xml you can read from database and add these rules dynamically based on the model data type
You can refer the below approach also
DataAnnotations dynamically attaching attributes

When does a method warrant being defined in the Model instead of Controller? (from Chap 10 of Rails Tutorial)

I am currently going through Hartl's Rails Tutorial, and through the first 10 chapters or so have gotten used to the convention of putting most of the actions/methods in the controller. Currently, as the book is going through and defining a feed method for the Microposts, the method is placed with the User.rb model instead. As I am relatively new to the world of rails (and programming in general), I was wondering what the rationale or convention followed for putting this method (copied below) in the Model?
The method placed in the User.rb model:
def feed
# This is preliminary. See "Following users" for the full implementation.
Micropost.where("user_id = ?", id)
end
There is actually quite some contention on what code to put where, but in general, there are some easy guidelines to follow.
Does the method have to know some detail about the underlying data structure? Put it in the model.
An easy way to determine this is when it uses ActiveRecord methods like find, where, or specific columns in the database. By keeping this logic in the model, that means if you need to change the underlying datastore, you only have to change the model.
Does the method have some say in how a page is going to be rendered? Put it in the controller.
Generally, controllers should be pretty thin, pushing data to views and saving form data back to models.
While (if I remember correctly) Hartl does not take about non-rails classes, don't be afraid to put 'business logic' outside of the rails structure. You can create a app/lib or app/services or app/x directory and put plain old ruby objects in there, which can then be called from your controllers and models to handle those things they are good at.
Aim to 'push' things 'up' into the model as much as possible, then they will be repeated less and available to more. Don't just use models for Active Record database tables.
You can often unit test models easier.
Another 'next' place to put stuff that is shared is in /lib

Does adding PetaPoco attributes to POCO's have any negative side effects?

Our current application uses a smart object style for working with the database. We are looking at the feasibility of moving to PetaPoco instead. Looking over the features I notice you can add attributes to make it easier to CRUD objects. Does adding these attributes have any negative side effects that I should be aware of?
Has anyone found a reason NOT to use these decorators?
Directly to the use of the POCO object instance itself? None.
At least not that I would be aware of. Jon Skeet should be able to provide more info because he knows compiler inner workings through and through, so he knows exactly what happens with this metadata after it's been compiled.
Other implications indirectly related to these
There are of course implications when accessing these declarative attributes, because they're read using reflection which is normally a slow process.
But there's nothing to worry here, because PetaPoco is a smart library and reads these only once then compiles & caches these things, so you only get penalized once then you get blazing performance afterwards. Because it uses compiled code.
Non-performance related implications
By putting attributes (any) on your classes/properties/methods you somehow bind your code to particular engine that will use this class, because they're directives for this particular engine to understand your code.
In case of PetaPoco attributes this means that your class can be used with PetaPoco but not with some other DAL (ie. EF) unless you add attributes of that one as well (EF Code First uses the very same approach with attributes).
The second implication is related to back-end database. In case you rename a table, column or any other part that is provided in your PetaPoco attribute as a constant magic string, you will subsequently have to change this string as well. This just means that you have to be thorough when doing database changes...
One downside is that it breaks the separation between the "domain" layer and the "data" layer, since it introduces the PetaPoco file (which contains data logic) to domain classes that should really not have any knowledge or dependency on the data layer.
If you're doing a single-project MVC app or something then it's okay to just use the Models directory for both, but for non-trivial and separated apps you'll have to have two PetaPoco files or play around with abstracting portions of the file in order to annotate your models without making them "know too much" about the underlying data, or else have you specify the table and/or primary key name all over the place.