rails controller creating instead of updating when passing in an id - ruby-on-rails-3

I am trying to add a file to a model using qqfile (though that really isn't relevant here).
I look at the params being passed to the server for update, and I have
{ id: 63, photo: 'foto_file.jpg'}
My understanding was that if an object was passed with an id parameter, rails would understand that as an already existing object, and update that model. If no id parameter is present, Rails would use create.
Is that not correct?? How in this instance can I tell rails to update rather than create?
I'm assuming more code isn't needed here, as my controllers won't really help with the solution because I think the decision is made by rails before it really hits the controller. But I'm happy to post the controller code if it is needed.
--------------- my javascript used to update or create the model ---------------------
render: function(){
var start_form=HandlebarsTemplates['user/userForm'](user.attributes);
$(this.el).html(start_form);
var uploader = new qq.FileUploader({
element: document.getElementById('file-upload'),
action: '/users',
onSubmit: function(id, fileName){
if(MyApp.user.id){
uploader.setParams({
id: MyApp.user.id
});
}
},
debug: true
});
},

The update method is only used when you sent a PUT request, not a POST request. Make sure you're using the PUT method. (If you show your form's code, I can give a more specific answer).
Update -- With your code, try adding this as a parameter to your qq.FileUploader call:
params: {
_method: "put"
}
Rails will look for a _method parameter to handle PUT/DELETE requests.

I couldn't get Dylan's javascript method to work, so in my controller I redirected to my update if the response had an id.
def create
if params[:id]
return self.update
end
#then all my regular create stuff here
end
def update
#all the usual update stuff
end

Related

Mongoose Methods not stored in Session?

I'd like to use a Method defined in the Mongoose Model after saving the retrieved Object to a Session. Its not working though. Is it normal that these methods get lost after storing it to the session?
Calling Method from Mongoose Model works fine:
Puppies.findOne({_id:123}).then(puppy => puppy.bark()) // WOOF WOOF
Storing Model in Session and calling method fails:
// First Request
Puppies.findOne({_id:123}).then(puppy => {
req.session.puppy = puppy
})
// Second Request somewhere else in the app
app.use(function(req,res,next){
req.session.puppy.bark() // req.session.puppy.bark is not a function
})
I've got the exact issue, but I believe what happens is that when you're storing the variable in session, it's being toObject()'d, causing it to become a simple JavaScript object, instead of remaining as an instance of Model. I've used Model.hydrate as a means of recreating this Model instance.
app.use(function(req,res,next){
let puppyModel = mongoose.model("puppy");
let puppy = puppyModel.hydrate(req.session.puppy);
puppy.bark() // Awooo
});
This essentially is creating a new Model and then filling it with all the relevant information so it acts a clone.
Because it is needing all the relevant information to make an update (including _id if you have it), I believe you may need to extend the toObject function to return getters/virtuals:
puppySchema.set('toObject', { getters: true, virtuals: true });
Else, when it attempts to save, and it's missing the _id field, it won't be able to save it.
I do hope someone else can provide a nicer method of doing this and/or explain why when storing it it has to be converted to an object and can't remain as an instance of Model.
I think what Ciaran Blewitt said was correct. Finally worked around it by just using mongoose statics:
puppy.model.js
schema.statics.bark(puppy) {
console.log(puppy.sound)
}
Storing Model in Session and getting desired effect via static:
// First Request, storing Puppy in Session
Puppy.findOne({_id:123}).then(puppy => {
req.session.puppy = puppy
})
// Second Request somewhere else in the app
app.use(function(req,res,next){
Puppy.bark(req.session.puppy) // WOOF WOOF
})

Phalcon router sends the wrong parameters

I have a problem with my router in Phalcon.
I have an action in my controller which ether takes a date parameter or not.
So when I access an URL: http://example.com/sl/slots/index/2017-06-27
everything works ok.
But when I go to: http://example.com/sl/slots/index
I get the following error:
DateTime::__construct(): Failed to parse time string (sl) at position
0 (s): The timezone could not be found in the database.
So the router actually takes the "sl" in the beginning as a parameter.
My router for this kind of url is set like this:
$router->add(
"/{language:[a-z]{2}}/:controller/:action",
array(
"controller" => 2,
"action" => 3
)
);
Btw it does the same withut the index: http://example.com/sl/slots
Oh and my slots index action looks like this:
public function indexAction($currentDate = false){ //code }
So the $currentDate is set to "sl" when I call the action without a parameter
Thank you for the help
Well you need to add language in first argument of action too. Then it should work.
In addition to #Juri's answer.. I prefer to keep my Actions empty or as slim as possible. Imagine if you have 3-4 parameters in the Route, you will end up with something like:
public function indexAction($param1 = false, $param2 = false, $param3 = false....)
Here is how I prefer to handle Route parameters:
public function indexAction()
{
// All parameters
print_r($this->dispatcher->getParams());
// Accessing specific Named parameters
$this->dispatcher->getParam('id');
$this->dispatcher->getParam('language');
// Accessing specific Non-named parameters
$this->dispatcher->getParam(0);
$this->dispatcher->getParam(1);
...
}

Variable in controller is lost when passing between views

I am currently stuck with an issue in my MVC 4 application. I have private variable in controller, that holds instance of a simple class:
private InstallationStatus status = null;
When data get submitted on a view, it gets filled like this:
InstallationStatus installStatus = Install();
if (installStatus != null)
{
status = installStatus;
TempData["installPercent"] = 0;
return View("InstallationProgress", status);
}
This part works as intended, variable is set to the instance as it should be.
After that view periodically checks another variable (using ajax):
<script type="text/javascript">
$(document).ready(function () {
var progress = 0;
$("div.status-message").text("Progress: " + progress + "%");
var statusUpdate = setInterval(function () {
$.ajax({
type: 'GET',
url: "/AppStart/GetInstallProgress",
datatype: "application/html; charset=utf-8",
success: function (data) {
progress = parseInt(data);
if (progress >= 100) {
clearInterval(statusUpdate);
var data = $(this).serialize();
$.ajax({
type: 'POST',
url: "#Url.Action("CompletedStatus", "AppStart")",
success: function () {
window.location = "/Login/Login"
}
});
}
$("div.status-message").text("Progress: " + progress + "%");
}
});
}, 2000);
});
</script>
When it calls "CompletedStatus" action on the controller, variable "status" on the controller is null (the instance set previously is not there?
How do I ensure that its value will persist? It seems to me like whole instance of controller gets lost, but that doesnt really matter to me - the source for "status" is webservice and once I get the instance of InstallationStatus, I cant get it again - I need to keep it.
I tried using TempData to store it but since there can be more than one step between storing it and retrieving it TempData proved unreliable.
The final process is:
Request installation status and navigate to view for installation progress (status will be received when progress will finish).
navigate to view where I will by updating installation progress
using javascript whenever I get callback from server with info about
progress
when installation finishes (status is returned) pass that status to
another view
In the example above I have some dummy code-behind, so the status is returned immediately, but that has no impact on the problem I have.
Currently I can do 1 and 2 and I can call the final view, but I cant pass the status in there because I dont have it on controller anymore and TempData are not reliable for this (sometimes it is still there, sometimes it is not).
Any help will be greatly appreciated.
When it calls "CompletedStatus" action on the controller, variable
"status" on the controller is null (the instance set previously is not
there?
How do I ensure that its value will persist?
private InstallationStatus status = null;
It won't unless it's a static value and that would be a very bad thing to do. Remember that variable values (private members' values) are only scoped within the http request. If you do another request then that's a totally whole new scope for your private variables.
I tried using TempData to store it but since there can be more than
one step between storing it and retrieving it TempData proved
unreliable.
That's because TempData will not have the value you expect it to have once you do another request. One good example of using this TempData is when you want to pass/move some values between a POST and GET, that is when you do a POST and do a redirect. TempData does not fit your case.
Now for a possible solution to your scenario, a good question is: is the installation process called once? Is it unique per user? If it is, which I highly suspect it is, then you need to uniquely identify each request. You can simply use a GUID to identify each request. Save that into your database (better than saving in a session) along with some other information like the status of the installation. Pass that guid back to your client and let them pass it back to the controller and retrieve an update on the status of the installation.

Backbone.js using non-restful actions on a restful model

I have a regular restful model that I do get, create, delete and update. On top of that, I'd like to call special actions on that model, like change_password.
The regular RESTful routes are traditional Rails 3 routes:
fetch => GET /api/models/:id
save => PUT /api/models/:id
create => POST /api/models
destroy => DELETE /api/models/:id
But, on top of those, I have special operations:
changePassword => GET /api/models/:id/change_password
activate => GET /api/models/:id/activate
And so on.
What do I need to setup on the model, so it recognizes those new actions, and how to wrap the calls into model methods?
It's fairly simple to add new methods to a Model - just specify the new methods in .extend(). You have to code some of this yourself, but you can take advantage of existing machinery like Backbone.sync (mostly just a wrapper around $.ajax()) and the Model's existing url property:
var MyModel = Backbone.Model.extend({
activate: function(opts) {
var model = this,
url = model.url() + '/activate',
// note that these are just $.ajax() options
options = {
url: url,
type: 'POST' // see my note below
};
// add any additional options, e.g. a "success" callback or data
_.extend(options, opts);
return (this.sync || Backbone.sync).call(this, null, this, options);
},
// etc
});
Just as a comment, from a REST perspective, your changePassword and activate operations should not be GET methods - all GET methods should be idempotent. This is not just RESTifarianism, it's a Good Idea - you could end up caching these URLs (so nothing happens) or hitting them multiple times by accident (usually requiring user confirmation with a POST request). Make these POST calls if you can.
I would advise that if possible add a Password model/controller where you can call save on to change the password. This follows the REST standards and is built in functionality of Backbone.js
If that's not an option, the following is a CoffeeScript example, add this to your model:
activate: ->
unless user.get('active')
(#sync || Backbone.sync).call #, 'activate', #,
url: "#{#url()}/users/#{message.get('id')}/activate"
data: {}
complete: =>
user.set(active: true)
#set(active: true)

CakePHP: get user info in models

I'm moving some of my find code inside models.
Previously in my controller I had
$this->Book->Review->find('first', array(
'conditions' => array(
'Review.book_id' => $id,
'Review.user_id' => $this->Auth->user('id')
)
));
so in my Review model I put something like
function own($id) {
$this->contain();
$review = $this->find('first', array(
'conditions' => array(
'Review.book_id' => $id,
'Review.user_id' => AuthComponent::user('id')
)
));
return $review;
}
So I'm calling AuthComponent statically from the Model. I know I can do this for the method AuthComponent::password(), which is useful for validation. But I'm getting errors using the method AuthComponent::user(), in particular
Fatal error: Call to a member function
check() on a non-object in
/var/www/MathOnline/cake/libs/controller/components/auth.php
on line 663
Is there a way to get the info about the currently logged user from a model?
Create a new function in the "app_model.php" ("AppModel.php" in CakePHP 2.x), so it will be available at all models within our application:
function getCurrentUser() {
// for CakePHP 1.x:
App::import('Component','Session');
$Session = new SessionComponent();
// for CakePHP 2.x:
App::uses('CakeSession', 'Model/Datasource');
$Session = new CakeSession();
$user = $Session->read('Auth.User');
return $user;
}
in the model:
$user = $this->getCurrentUser();
$user_id = $user['id'];
$username = $user['username'];
The way that I use is this:
App::import('component', 'CakeSession');
$thisUserID = CakeSession::read('Auth.User.id');
It seems to work quite nicely :-)
I think the code is fine as it is and belongs in the Controller, or at the very least it needs to receive the ids from the Controller and not try to get them itself. The Model should only be concerned with fetching data from a data store and returning it. It must not be concerned with how the data is handled in the rest of the application or where the parameters to its request are coming from. Otherwise you paint yourself into a corner where the ReviewModel can only retrieve data for logged in users, which might not always be what you want.
As such, I'd use a function signature like this:
function findByBookAndUserId($book_id, $user_id) {
…
}
$this->Review->findByBookAndUserId($id, $this->Auth->user('id'));
There is a nice solution by Matt Curry. You store the data of the current logged user in the app_controller using the beforeFilter callback and access it later using static calls. A description can be found here:
http://www.pseudocoder.com/archives/2008/10/06/accessing-user-sessions-from-models-or-anywhere-in-cakephp-revealed/
EDIT: the above link is outdated: https://github.com/mcurry/cakephp_static_user
I think this is not good idea to get value from Session. Better solution to get logged user id inside any model simply try this:
AuthComponent::user('id');
This will work almost every where. View, Model and Controller
Dirtiest way would be to just access the user information in the Session. Least amount of overhead associated with that.
The "proper" way would probably be to instantiate the AuthComponent object, so that it does all the stuff it needs to be fully operational. Much like a death star, the AuthComponent doesn't really work well when not fully setup.
To get a new AC object, in the model:
App::import( 'Component', 'Auth' );
$this->Auth = new AuthComponent();
Now you can use $this->Auth in the model, same as you would in the controller.
For CakePHP 3.x this easy component is available: http://cakemanager.org/docs/utils/1.0/components/globalauth/. Direct accessing the Session is not possible because of different SessionKeys.
With the GlobalAuthComponent you can access your user-data everywhere with: Configure::read('GlobalAuth');.
Greetz
Bob
I use cake 2.2 and these both work great:
$this->Session->read('Auth.User');
//or
$this->Auth->user();
You can also get a field of currently logged in user:
$this->Session->read('Auth.User.email');
//or
$this->Auth->user()['email'];
None of these solutions work in CakePHP version 3. Anyone know of a way to do this? Right now, I'm completely stepping around the framework by accessing the $_SESSION variable directly from my model.