Différence entre attr_accessor et attr_accessible

235

Dans Rails, quelle est la différence entre attr_accessoret attr_accessible? D'après ma compréhension, l'utilisation attr_accessorest utilisée pour créer des méthodes getter et setter pour cette variable, afin que nous puissions accéder à la variable comme Object.variableou Object.variable = some_value.

J'ai lu que cela attr_accessiblerend cette variable spécifique accessible au monde extérieur. Quelqu'un peut-il me dire quelle est la différence

Felix
la source
4
Vous avez raison qui attr_accessorest utilisé pour générer des méthodes getter et setter. Veuillez voir ma réponse à une question précédente pour une explication assez complète de attr_accessible: stackoverflow.com/questions/2652907/… puis mettez à jour votre question si vous avez besoin d'autres détails spécifiques après cela.
mikej
2
attr_accessible n'est plus pris en charge dans Rails 4 à moins que vous n'utilisiez la gemme protected_attributes, selon la réponse du haut à stackoverflow.com/questions/17371334/… (juillet 2014)
emery

Réponses:

258

attr_accessorest une méthode Ruby qui fait un getter et un setter. attr_accessibleest une méthode Rails qui vous permet de transmettre des valeurs à une affectation en masse: new(attrs)ou update_attributes(attrs).

Voici une affectation de masse:

Order.new({ :type => 'Corn', :quantity => 6 })

Vous pouvez imaginer que la commande peut également avoir un code de réduction, par exemple :price_off. Si vous ne marquez pas :price_offlorsque attr_accessiblevous empêchez le code malveillant de pouvoir le faire:

Order.new({ :type => 'Corn', :quantity => 6, :price_off => 30 })

Même si votre formulaire n'a pas de champ :price_off, s'il est dans votre modèle, il est disponible par défaut. Cela signifie qu'un POST conçu pourrait toujours le définir. En utilisant attr_accessibledes listes blanches, les choses qui peuvent être affectées en masse.

Paul Rubel
la source
2
Pourquoi n'est pas attr_accessibledans la documentation Rails? api.rubyonrails.org
Chloe
19
On dirait que Rails4 a une nouvelle façon de faire les choses. Voir cette réponse: stackoverflow.com/questions/17371334/…
Paul Rubel
1
Parce attr_accessible qu'un
Imran Ahmad
173

Beaucoup de gens sur ce fil et sur Google expliquent très bien qui attr_accessiblespécifie une liste blanche d'attributs qui peuvent être mis à jour en masse ( tous les attributs d'un modèle d'objet ensemble en même temps ) C'est principalement (et seulement) pour protéger votre application de l'exploit pirate "Assignation de masse".

Ceci est expliqué ici sur le document officiel de Rails: Mass Assignment

attr_accessorest un code rubis pour créer (rapidement) des méthodes setter et getter dans une classe. C'est tout.

Maintenant, ce qui manque comme explication est que lorsque vous créez en quelque sorte un lien entre un modèle (Rails) avec une table de base de données, vous n'avez JAMAIS, JAMAIS, JAMAIS besoin attr_accessordans votre modèle de créer des setters et des getters afin de pouvoir modifier votre enregistrements de la table.

En effet, votre modèle hérite de toutes les méthodes de la ActiveRecord::Baseclasse, qui définit déjà pour vous les accesseurs CRUD de base (créer, lire, mettre à jour, supprimer). Ceci est expliqué dans la doc officielle ici Rails Model et ici Overwriting default accessor (faites défiler jusqu'au chapitre "Overwrite default accessor")

Disons par exemple que: nous avons une table de base de données appelée "utilisateurs" qui contient trois colonnes "prénom", "nom" et "rôle":

Instructions SQL:

CREATE TABLE users (
  firstname string,
  lastname string
  role string
);

J'ai supposé que vous définissiez l'option config.active_record.whitelist_attributes = truedans votre config / environment / production.rb pour protéger votre application contre l'exploit d'affectation de masse. Ceci est expliqué ici: Affectation en masse

Votre modèle Rails fonctionnera parfaitement avec le modèle ci-dessous:

class User < ActiveRecord::Base

end

Cependant, vous devrez mettre à jour chaque attribut d'utilisateur séparément dans votre contrôleur pour que la vue de votre formulaire fonctionne:

def update
    @user = User.find_by_id(params[:id])
    @user.firstname = params[:user][:firstname]
    @user.lastname = params[:user][:lastname]

    if @user.save
        # Use of I18 internationalization t method for the flash message
        flash[:success] = t('activerecord.successful.messages.updated', :model => User.model_name.human)
    end

    respond_with(@user)
end

Maintenant, pour vous faciliter la vie, vous ne voulez pas créer un contrôleur compliqué pour votre modèle utilisateur. Vous utiliserez donc la attr_accessibleméthode spéciale dans votre modèle de classe:

class User < ActiveRecord::Base

  attr_accessible :firstname, :lastname

end

Vous pouvez donc utiliser la "route" (affectation de masse) pour mettre à jour:

def update
    @user = User.find_by_id(params[:id])

    if @user.update_attributes(params[:user])
        # Use of I18 internationlization t method for the flash message
        flash[:success] = t('activerecord.successful.messages.updated', :model => User.model_name.human)
    end

    respond_with(@user)
end

Vous n'avez pas ajouté les attributs "role" à la attr_accessibleliste car vous ne laissez pas vos utilisateurs définir leur rôle par eux-mêmes (comme admin). Vous le faites vous-même sur une autre vue d'administration spéciale.

Bien que votre vue utilisateur ne montre pas de champ "rôle", un pirate pourrait facilement envoyer une requête HTTP POST qui inclut "rôle" dans le hachage des paramètres. L'attribut "role" manquant sur le attr_accessibleest de protéger votre application contre cela.

Vous pouvez toujours modifier votre attribut user.role seul comme ci-dessous, mais pas avec tous les attributs ensemble.

@user.role = DEFAULT_ROLE

Pourquoi diable utiliseriez-vous le attr_accessor?

Eh bien, ce serait dans le cas où votre formulaire utilisateur affiche un champ qui n'existe pas dans votre table d'utilisateurs sous forme de colonne.

Par exemple, supposons que votre vue utilisateur affiche un champ "veuillez-dire-à-l'administrateur-que-je-suis-ici". Vous ne voulez pas stocker ces informations dans votre table. Vous voulez juste que Rails vous envoie un e-mail vous avertissant qu'un utilisateur "fou" ;-) s'est abonné.

Pour pouvoir utiliser ces informations, vous devez les stocker temporairement quelque part. Quoi de plus simple que de le récupérer dans un user.peekabooattribut?

Vous ajoutez donc ce champ à votre modèle:

class User < ActiveRecord::Base

  attr_accessible :firstname, :lastname
  attr_accessor :peekaboo

end

Vous pourrez donc faire un usage éclairé de l' user.peekabooattribut quelque part dans votre contrôleur pour envoyer un e-mail ou faire ce que vous voulez.

ActiveRecord n'enregistrera pas l'attribut "peekaboo" dans votre table lorsque vous effectuez un, user.savecar elle ne voit aucune colonne correspondant à ce nom dans son modèle.

Douglas
la source
48

attr_accessorest une méthode Ruby qui vous donne des méthodes setter et getter pour une variable d'instance du même nom. C'est donc équivalent à

class MyModel
  def my_variable
    @my_variable
  end
  def my_variable=(value)
    @my_variable = value
  end
end

attr_accessible est une méthode Rails qui détermine quelles variables peuvent être définies dans une affectation en masse.

Lorsque vous soumettez un formulaire, et que vous avez quelque chose comme ça, MyModel.new params[:my_model]vous voulez avoir un peu plus de contrôle, afin que les gens ne puissent pas soumettre des choses que vous ne voulez pas.

Vous pouvez faire en attr_accessible :emailsorte que lorsque quelqu'un met à jour son compte, il puisse changer son adresse e-mail. Mais vous ne le feriez pas attr_accessible :email, :salarycar une personne pourrait alors fixer son salaire en soumettant un formulaire. En d'autres termes, ils pourraient se frayer un chemin vers une augmentation.

Ce type d'informations doit être explicitement traité. Le supprimer du formulaire ne suffit pas. Quelqu'un pourrait utiliser Firebug et ajouter l'élément dans le formulaire pour soumettre un champ de salaire. Ils pourraient utiliser la boucle intégrée pour soumettre un nouveau salaire à la méthode de mise à jour du contrôleur, ils pourraient créer un script qui soumet une publication avec ces informations.

Il attr_accessors'agit donc de créer des méthodes pour stocker des variables, et attr_accessiblede la sécurité des affectations de masse.

Joshua Cheek
la source
2
Vous avez une faute de frappe, après le bloc de code, il devrait direattr_accesible
Chubas
Très bien écrit, j'aime l'exemple de classe. Points bonus supplémentaires (faux) pour inclure une explication de :as!
Ian Vaughan
Le modèle est étendu par ActiveRecord :: Base. class User < ActiveRecord::Base
Vert
18

attr_accessorest un code rubis et est utilisé lorsque vous n'avez pas de colonne dans votre base de données, mais que vous souhaitez toujours afficher un champ dans vos formulaires. La seule façon de l'autoriser est de le faire attr_accessor :fieldnameet vous pouvez utiliser ce champ dans votre vue ou votre modèle, si vous le souhaitez, mais surtout dans votre vue.

Prenons l'exemple suivant

class Address
    attr_reader :street
    attr_writer :street  
    def initialize
        @street = ""
    end
end

Ici, nous avons utilisé attr_reader( attribut lisible ) et attr_writer( attribut inscriptible ) pour accéder à l'objectif. Mais nous pouvons obtenir la même fonctionnalité en utilisant attr_accessor. En bref, attr_accessor donne accès aux méthodes getter et setter.

Le code ainsi modifié est comme ci-dessous

class Address
    attr_accessor :street  
    def initialize
        @street = ""
    end
end

attr_accessiblevous permet de répertorier toutes les colonnes que vous souhaitez autoriser l'attribution en masse. L'opposé de ceci est ce attr_protectedqui signifie que ce champ, je ne veux pas que quiconque soit autorisé à assigner en masse. Il est plus que probable que ce sera un champ de votre base de données avec lequel vous ne voudrez pas que quelqu'un fasse le singe. Comme un champ d'état, ou similaire.

shrikant1712
la source
2
Alors, dites-vous que si j'ai créé des champs dans une migration, puis les rendre disponibles en utilisant attr_accessible, il n'est pas nécessaire de créer un getter et un setter? Mais si le champ n'est pas dans la base de données, comment se fait-il que attr_accessible n'agisse pas comme un getter / setter? Si j'inclus une ligne "has_secure_password", attr_accessible devient suffisant pour permettre à getter / setter de: password et: password_confirmation même s'ils ne sont pas dans la base de données. Très confus;)
dix fois le
2

En deux mots:

attr_accessorest getter, la setterméthode. alors que attr_accessiblec'est dire qu'un attribut particulier est accessible ou non. c'est tout.


Je souhaite ajouter que nous devrions utiliser le paramètre Strong au lieu de nous attr_accessibleprotéger de l'affectation de masse.

À votre santé!

Manish Shrivastava
la source
2

Un aperçu rapide et concis des différences:

attr_accessorest un moyen simple de créer des accesseurs en lecture et en écriture dans votre classe. Il est utilisé lorsque vous n'avez pas de colonne dans votre base de données, mais que vous souhaitez quand même afficher un champ dans vos formulaires. Ce champ est “virtual attribute”dans un modèle Rails.

attribut virtuel - un attribut ne correspondant pas à une colonne de la base de données.

attr_accessible est utilisé pour identifier les attributs qui sont accessibles par vos méthodes de contrôleur rend une propriété disponible pour une affectation en masse. Il autorisera uniquement l'accès aux attributs que vous spécifiez, refusant le reste.

Muhammad Yawar Ali
la source