I am new to Rails and am currently learning strong parameters in Rails 4 and following the below example from the official documentation:
`class PeopleController < ActionController::Base
# Using "Person.create(params[:person])" would raise an
# ActiveModel::ForbiddenAttributes exception because it'd
# be using mass assignment without an explicit permit step.
# This is the recommended form:
def create
Person.create(person_params)
end
# This will pass with flying colors as long as there's a person key in the
# parameters, otherwise it'll raise an ActionController::MissingParameter
# exception, which will get caught by ActionController::Base and turned
# into a 400 Bad Request reply.
def update
redirect_to current_account.people.find(params[:id]).tap { |person|
person.update!(person_params)
}
end
private
# Using a private method to encapsulate the permissible parameters is
# just a good pattern since you'll be able to reuse the same permit
# list between create and update. Also, you can specialize this method
# with per-user checking of permissible attributes.
def person_params
params.require(:person).permit(:name, :age)
end
end`
Question 1:
What does current_account.people.find mean inside the update method?
Question 2:
Could someone please explain the person_params method. What is "params" inside the person_params method?
current_account is a most likely a private method that returns an Account instance. current_account.people.find(params[:id]) searches the people table for a person that belongs to the current_account and has an ID of params[:id]. Object#tap is a ruby method that yields a block with the current object, and then returns that object. In this case, the Person instance is updated inside the block and the returned from tap. Finally, redirect_to is a controller method that will redirect the request to a different path. redirect_to can take many different types of arguments, including an ActiveRecord model, a string, or a symbol. Passing it an ActiveRecord model will redirect the request to the model's resource path, which is defined in routes.rb. In this case, that path will most likely be /people/:id.
The params object is a hash containing parameter names and values. For example, the request /people?name=Joe&age=34 will result in the following params object: {name: 'Joe', age: '34'}.
Related
I am working on refactoring a tool to OOP in PS5.
I have a number of classes. Sets can contain other Sets as well as Packages. Packages can contain other Packages and Tasks. And Tasks can contain other Tasks. For example...
Set1
Package1.1
Task1.1
Set2
Package2.1
Task2.1
Set2A
Package2A
Task2A.1
Task2A.2
Package2.2
Task2.2
Set3
Package3.1
Task3.1
Task3.1A
I plan to have Set, Package and Task classes, but there are a number of different Tasks with some common features and some unique, so I will have a base Task class that is then extended by the various final task classes.
My question relates to the data structure to contain the nested objects. If each class could only contain the next deeper type everything would be easy; the variable to hold the Packages in a Set could be an array of Packages, i.e. [Package[]]$Contents.
I could make it super flexible and just do an array; [Array]$Contents, but that allows for invalid items like strings and such.
Alternatively I could have some sort of Root class, with Sets, Packages and Tasks all extended that, and final Tasks then extending Tasks, and use[Root[]]$Contents or some such. But that might not be possible and it would still allow for adding a Task to a Set, since a final Task class would ultimately be extending from Root.
So, the question becomes, can you define an array that accepts multiple possible types but is still limited, something like [Set/Package[]]$Contents? Or is there perhaps a totally different way to define a variable that limits the valid members? An Enum seems to have potential, but it seems like they are limited to strings as I tried
enum AllowedTypes {
[Array]
[Strings]
}
and that in no good.
Or am I best of just using an Array and validating what I am adding in the Add method of each Class? I can see a possible solution there where I have overloaded Add methods in the Set class, one that takes a Set, one that takes a Package, and one that takes a generic object and throws an error to log. Assuming that the more specific overload method takes priority rather than everything going to the generic method since it's technically valid. Or perhaps that generic method won't even work since the collection of overloaded Add methods technically can't collapse to one valid choice because a Set is both a [Set] and a [PSObject] I guess.
PetSerAl, as countless times before, has provided an excellent (uncommented) solution in a comment on the question, without coming back to post that solution as an answer.
Given the limits of code formatting in comments, it's worth presenting the solution in a more readable format; additionally, it has been streamlined, modularized, extended, and commented:
In short: a PowerShell custom class (PSv5+) is used to subclass standard type [System.Collections.ObjectModel.Collection[object]] in order to limit adding elements to a list of permitted types passed to the constructor.
class MyCollection : System.Collections.ObjectModel.Collection[object] {
# The types an instance of this collection
# is permitted to store instance of, initialized via the constructor.
[Type[]] $permittedTypes
# The only constructor, to which the permitted types must be passed.
MyCollection([Type[]] $permittedTypes) { $this.permittedTypes = $permittedTypes }
# Helper method to determine if a given object is of a permitted type.
[bool] IsOfPermittedType([object] $item) {
return $this.permittedTypes.Where({ $item -is $_ }, 'First')
}
# Hidden helper method for ensuring that an item about to be inserted / added
# / set is of a permissible type; throws an exception, if not.
hidden AssertIsOfPermittedType([object] $item) {
if (-not $this.IsOfPermittedType($item)) {
Throw "Type not permitted: $($item.GetType().FullName)"
}
}
# Override the base class' .InsertItem() method to add type checking.
# Since the original method is protected, we mark it as hidden.
# Note that the .Add() and .Insert() methods don't need overriding, because they
# are implemented via this method.
hidden InsertItem([int] $index, [object] $item) {
$this.AssertIsOfPermittedType($item)
([System.Collections.ObjectModel.Collection[object]] $this).InsertItem($index, $item)
}
# Override the base class' SetItem() method to add type checking.
# Since the original method is protected, we mark it as hidden.
# This method is implicitly called when indexing ([...]) is used.
hidden SetItem([int] $index, [object] $item) {
$this.AssertIsOfPermittedType($item)
([System.Collections.ObjectModel.Collection[object]] $this).SetItem($index, $item)
}
# Note: Since the *removal* methods (.Remove(), .RemoveAt())
# need to type checking, there is no need to override them.
}
With the above class defined, here's sample code that exercises it:
# Create an instance of the custom collection type, passing integers and strings
# as the only permitted types.
# Note the (...) around the type arguments, because they must be passed
# as a *single argument* that is an *array*.
# Without the inner (...) PowerShell would try to pass them as *individual arguments*.
$myColl = [MyCollection]::new(([int], [string]))
# OK, add an [int]
# .Add() implicitly calls the overridden .InsertItem() method.
$myColl.Add(1)
$myColl.Add('hi') # OK, add a [string]
# OK, override the 1st element with a different [int]
# (though a [string] would work too).
# This implicitly calls the overridden .SetItem() method.
$myColl[0] = 2
# OK - insert a [string] item at index 0
$myColl.Insert(0, 'first')
# $myColl now contains: 'first', 2, 'hi'
# Try to add an impermissible type:
$myColl.Add([long] 42)
# -> Statement-terminating error:
# 'Exception calling "Add" with "1" argument(s): "Type not permitted: System.Int64"'
I have a google-cloud-endpoints, in the docs, I did'nt find how to write a PATCH method.
My request:
curl -XPATCH localhost:8080/_ah/api/hellogreeting/1 -d '{"message": "Hi"}'
My method handler looks like this:
from models import Greeting
from messages import GreetingMessage
#endpoints.method(ID_RESOURCE, Greeting,`
path='hellogreeting/{id}', http_method='PATCH',
name='greetings.patch')
def greetings_patch(self, request):
request.message, request.username
greeting = Greeting.get_by_id(request.id)
greeting.message = request.message # It's ok, cuz message exists in request
greeting.username = request.username # request.username is None. Writing the IF conditions in each string(checking on empty), I think it not beatifully.
greeting.put()
return GreetingMessage(message=greeting.message, username=greeting.username)
So, now in Greeting.username field will be None. And it's wrong.
Writing the IF conditions in each string(checking on empty), I think it not beatifully.
So, what is the best way for model updating partially?
I do not think there is one in Cloud Endpoints, but you can code yours easily like the example below.
You will need to decide how you want your patch to behave, in particular when it comes to attributes that are objects : should you also apply the patch on the object attribute (in which case use recursion) or should you just replace the original object attribute with the new one like in my example.
def apply_patch(origin, patch):
for name in dir( patch ):
if not name.startswith( '__' ):
setattr(origin,name,getattr(patch,name))
I'm wondering what the right pattern should be to update an existing datastore object using endpoints-proto-datastore.
For example, given a model like the one from your GDL videos:
class Task(EndpointsModel):
detail = ndb.StringProperty(required=True)
owner = ndb.StringProperty()
imagine we'd like to update the 'detail' of a Task.
I considered something like:
#Task.method(name='task.update',
path='task/{id}',
request_fields=('id', 'detail'))
def updateTask(self, task):
pass
However, 'task' would presumably contain the previously-stored version of the object, and I'm not clear on how to access the 'new' detail variable with which to update the object and re-store it.
Put another way, I'd like to write something like this:
def updateTask(self, task_in_datastore, task_from_request):
task_in_datastore.detail = task_from_request.detail
task_in_datastore.put()
Is there a pattern for in-place updates of objects with endpoints-proto-datastore?
Thanks!
See the documentation for details on this
The property id is one of five helper properties provided by default
to help you perform common operations like this (retrieving by ID). In
addition there is an entityKey property which provides a base64
encoded version of a datastore key and can be used in a similar
fashion as id...
This means that if you use the default id property your current object will be retrieved and then any updates from the request will replace those on the current object. Hence doing the most trivial:
#Task.method(name='task.update',
path='task/{id}',
request_fields=('id', 'detail'))
def updateTask(self, task):
task.put()
return task
will perform exactly what you intended.
Task is your model, you can easily update like this:
#Task.method(name='task.update',
path='task/{id}',
request_fields=('id', 'detail'))
def updateTask(self, task):
# Task.get_by_id(task.id)
Task.detail = task.detail
Task.put()
return task
in my app i for one of the handler i need to get a bunch of entities and execute a function for each one of them.
i have the keys of all the enities i need. after fetching them i need to execute 1 or 2 instance methods for each one of them and this slows my app down quite a bit. doing this for 100 entities takes around 10 seconds which is way to slow.
im trying to find a way to get the entities and execute those functions in parallel to save time but im not really sure which way is the best.
i tried the _post_get_hook but the i have a future object and need to call get_result() and execute the function in the hook which works kind of ok in the sdk but gets a lot of 'maximum recursion depth exceeded while calling a Python objec' but i can't really undestand why and the error message is not really elaborate.
is the Pipeline api or ndb.Tasklets what im searching for?
atm im going by trial and error but i would be happy if someone could lead me to the right direction.
EDIT
my code is something similar to a filesystem, every folder contains other folders and files. The path of the Collections set on another entity so to serialize a collection entity i need to get the referenced entity and get the path. On a Collection the serialized_assets() function is slower the more entities it contains. If i could execute a serialize function for each contained asset side by side it would speed things up quite a bit.
class Index(ndb.Model):
path = ndb.StringProperty()
class Folder(ndb.Model):
label = ndb.StringProperty()
index = ndb.KeyProperty()
# contents is a list of keys of contaied Folders and Files
contents = ndb.StringProperty(repeated=True)
def serialized_assets(self):
assets = ndb.get_multi(self.contents)
serialized_assets = []
for a in assets:
kind = a._get_kind()
assetdict = a.to_dict()
if kind == 'Collection':
assetdict['path'] = asset.path
# other operations ...
elif kind == 'File':
assetdict['another_prop'] = asset.another_property
# ...
serialized_assets.append(assetdict)
return serialized_assets
#property
def path(self):
return self.index.get().path
class File(ndb.Model):
filename = ndb.StringProperty()
# other properties....
#property
def another_property(self):
# compute something here
return computed_property
EDIT2:
#ndb.tasklet
def serialized_assets(self, keys=None):
assets = yield ndb.get_multi_async(keys)
raise ndb.Return([asset.serialized for asset in assets])
is this tasklet code ok?
Since most of the execution time of your functions are spent waiting for RPCs, NDB's async and tasklet support is your best bet. That's described in some detail here. The simplest usage for your requirements is probably to use the ndb.map function, like this (from the docs):
#ndb.tasklet
def callback(msg):
acct = yield ndb.get_async(msg.author)
raise tasklet.Return('On %s, %s wrote:\n%s' % (msg.when, acct.nick(), msg.body))
qry = Messages.query().order(-Message.when)
outputs = qry.map(callback, limit=20)
for output in outputs:
print output
The callback function is called for each entity returned by the query, and it can do whatever operations it needs (using _async methods and yield to do them asynchronously), returning the result when it's done. Because the callback is a tasklet, and uses yield to make the asynchronous calls, NDB can run multiple instances of it in parallel, and even batch up some operations.
The pipeline API is overkill for what you want to do. Is there any reason why you couldn't just use a taskqueue?
Use the initial request to get all of the entity keys, and then enqueue a task for each key having the task execute the 2 functions per-entity. The concurrency will be based then on the number of concurrent requests as configured for that taskqueue.
Update
This was my best effort creating the following scheme
user = self.auth.store.user_model.create_user(email,
password_raw=newpasswd)
if not user[0]: # user is a tuple
return user[1] # Error message
else:
# User is created, let's try making the references
okuser = auth_models.User.get_by_id(long(user[1].key.id()))
okuser.sponsor = auth_models.User.get_by_id(long(sponsor_id)).auth_ids
Original question
How can I make a selfreference with an expando class to indicate which User is the "sponsor" of which? The "sponsor" is the one who invited the new User so at creation we must store that and it would be much neater to store it as a referenceproperty than a string or a stringlist.
I can create a new user but I don't know how to make a reference so that I can tell for one User who another User is who is the sponsor of the first user and I suppose a way to model this is with selfreferenceproperty since both objects are users but the complication is that it is an expando model so I don't know how to use the reference property. Could you tell me how to do it or give me a clue how I can solve this problem in the best way?
user = self.auth.store.user_model.create_user(email,
password_raw=newpasswd)
if not user[0]: # user is a tuple
return user[1] # Error message
else:
# User is created, let's try making the reference
okuser = auth_models.User.get_by_id(user[1].key.id())
okuser.sponsor = db.SelfReferenceProperty(User,
collection_name='matched_images', verbose_name='Sponsor')
I don't know how to do the last part, to store the actual referenceproperty with an epando model. How can it be done?
Update
It seems it can't be done:
NotImplementedError: Property sponsor does not support <class 'google.appengine.ext.db.ReferenceProperty'> types.
Code:
user = self.auth.store.user_model.create_user(email,
password_raw=newpasswd)
if not user[0]: # user is a tuple
return user[1] # Error message
else:
# User is created, let's try redirecting to login page
okuser = auth_models.User.get_by_id(long(user[1].key.id()))
okuser.sponsor = db.SelfReferenceProperty(auth_models.User.get_by_id(sponsor_id),collection_name='matched_distributor')
okuser.put()
It forces me do use a string instead of a reference and then a solution is feasible:
user = self.auth.store.user_model.create_user(email,
password_raw=newpasswd)
if not user[0]: # user is a tuple
return user[1] # Error message
else:
# User is created, let's try redirecting to login page
okuser = auth_models.User.get_by_id(long(user[1].key.id()))
okuser.sponsor = sponsor_id
okuser.put()
You can't assign an instance of a Property class to an instance of a model - property classes define properties, they don't represent individual values.
By far the easiest way to do what you want is to add the property as you would on a regular model. Just because you're using expandos (why, by the way?) doesn't mean you can't have regular properties on them as well.