This is a Noob question regarding Backbone.JS and ActiveRecord.
I'd be grateful for a pointer on how to debug this.
I'm trying to use Backbone.Js code to create a "Trainer" object, which has a single attribute, "name" (a string).
The front end of the application is Ruby on Rails. There is a data migration for the Trainer table.
In trainers controller:
def create
document = Trainer.create! params[:trainer]
render :json => document
end
Now, in app/assets/javascripts/backbone/views/trainers/new_view.js:
Gym.Views.Trainers.NewView = Backbone.View.extend({
el : 'div.trainer_form',
template: JST['backbone/templates/trainers/new_template'],
model : new window.Gym.Models.Trainer({}),
initialize: function() {
this.document = this.options.user;
Backbone.Validation.bind(this, {
invalid: function(view, attr, error) {
$("form#new-trainer .errors ul").append("<li>" + error + "</li>")
}
});
this.render();
},
render : function() {
$(this.el).html(this.template({trainer:this.model.toJSON()}));
return this
},
events : {
"click input#submit_button" : 'create_trainer'
},
create_trainer : function(event) {
event.preventDefault()
params = $("form#new-trainer").formParams()
params['user_id'] = Gym.currentUser.userId
this.model.save(params, {success : function(model, response) {
Gym.trainers.add(model)
Objects.views.selectTrainer.render()
Gym.current_trainer = model
$("select#trainer_selector").val(Gym.current_trainer.get('id'))
Objects.views.new_trainer.model = new Gym.Models.Trainer()
Objects.views.new_trainer.render()
}
});
Now, I can see in the Rails log that I'm getting to the controller:
Started POST "/trainers" ...
Processing by TrainersController#create as JSON
Parameters: {"name"=>"Lori Stevens", "user_id"=>1, "trainer"=>{}}
However, when it gets to the SQL, I see this:
[1m^[[36mSQL (0.4ms)^[[0m ^[[1mINSERT INTO `trainers` (`created_at`, `name`, `updated_at`, `user_id`) VALUES ('2012-11-07 20:33:09', NULL, '2012-11-07 20:33:09', NULL)^[[0m
The parameter 'name' - which comes from the template, and is the attribute of the Trainer object- is not getting to the database, even though the parameter "name" is set in the JSON.
I'd appreciate a pointer on how to debug this - clearly I am not understanding how Backbone.js and ActiveRecord are connected.
The Controller takes the request from your browser and puts the data into ActiveRecord:
def create
document = Trainer.create! params[:trainer]
...
end
But then you see request in the log, params[:trainer] equals the empty hash {}
You can either change the javascript that it creates json with a hash like
{ 'trainer': {'name' : 'Lori stevens', ... }}
I don't know how easy this in backbone.
Or you can change your controller that it gets the values out of the hash and constructs an new hash for your trainer model, like it is:
gotten_name_from_json = params['name']
...
document = Trainer.create!(
{:name => gotten_name_from_json, :town => gotten_twon_from_json})
I made this verbose to show that using this you can translate what ever json comes in, even when it comes from third parties where you can not control the format.
Related
im trying to do pagination on my django rest code, but i get the same code when i change the number of the page, this is what im doing to get that page: http://localhost:8000/movies?page=3
When i change the page number i get the same response, idk if i have to send the number of the page or something but i do the same of this stackoverflow thread
I put the entire view code:
#api_view(['GET', 'POST', 'DELETE', 'PUT'])
def movies(request):
if request.method == 'GET':
if request.query_params.get('id'):
try:
id = request.query_params.get('id')
movie = Movie.objects.get(id=id)
serializer = MovieSerializer(movie, many=False)
return Response(serializer.data)
except Movie.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
movies = Movie.objects.all().order_by('release_date')
serializer = MovieSerializer(movies , many=True, context={'request':request})
if request.query_params.get('page'):
paginator = LimitOffsetPagination()
result_page = paginator.paginate_queryset(movies, request)
serializer = MovieSerializer(result_page, many=True, context={'request':request})
return Response(serializer.data)
if request.query_params.get('Genre'):
genreparam = request.query_params.get('Genre')
genre = Genre.objects.get(name=genreparam)
queryset = Movie.objects.filter(genre_relation=genre.id).values().order_by('release_date')
return Response(queryset)
return Response(serializer.data)
this is my settings.py
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 2,
}
this is what i get whatever number i send via request params
[
{
"id": 1,
"title": "Guardians of the galaxy",
"tagline": "this is a tagline",
"overview": "this is an overview, starlord in the begins...",
"release_date": "1971-07-13T03:00:00Z",
"poster_url": "http\"//posterurl",
"backdrop_url": "http\"//backdropurl",
"imdb_id": "idk what is a imdb",
"genre_relation": []
},
{
"id": 2,
"title": "Avengers endgame",
"tagline": "this is a tagline",
"overview": "tony stark dies, theres no more happy days, only days",
"release_date": "2019-07-13T03:00:00Z",
"poster_url": "http//posterurl",
"backdrop_url": "http//backdropurl",
"imdb_id": "idk what is a imdb",
"genre_relation": [
1
]
}
]
You are not using the pagination properly. You need to instantiate the paginator with the request, and then call paginate_queryset. You are merely instantiating a paginator, and then completely ignoring it.
paginator = LimitOffsetPagination()
result_page = paginator.paginate_queryset(movies, request)
You thus should rewrite this to:
paginator = LimitOffsetPagination()
result_page = paginator.paginate_queryset(movies, request, view=self)
Note that we here pass view=self, since the LimitOffsetPagination uses self.request, self.response, etc.
Furthermore you should not construct a new serializer, but reuse the existing one, and pass result_page as the queryset:
serializer = MovieSerializer(result_page, many=True, context={'request': request})
Finally you should return the paginated results with:
return paginator.get_paginated_response(serializer.data)
This will add pagination metadata to the response.
So a full example:
#api_view(['GET', 'POST', 'DELETE', 'PUT'])
def movies(request):
# ...
if request.query_params.get('page'):
paginator = LimitOffsetPagination()
result_page = paginator.paginate_queryset(movies, request, view=self)
serializer = MovieSerializer(result_page, many=True, context={'request':request})
return paginator.get_paginated_response(serializer.data)
# ...
Note that using the #api_view decorator is often discouraged. You might want to consider using the #api_view decorator.
How do i push my results into a data object please
I would like to push the :key to the id field and the :data to map across to my other data fields
my data object is called orgs: []
I have the following data returned from my get query
[{…}]
0:
data: {person: 'John', orgName: 'test', due: 'this is due'}
key: "11ed25ccf7548160bff2ab4c1d56ee40"
I have tried
this.$db.collection('orgs').get({ keys: true })
.then
(response => {
console.log('orgs: ', response) <--- this displays the results
this.orgs.forEach(response => {
const data = {
'person': response.data.person,
'orgName': response.data.orgName,
'due': response.data.due,
'id': response.key
}
this.orgs.push(data)
})
});
I get no errors but my this.orgs data item remains empty so assume I am not updating the array for some reason
Thanks
I seem to be able to populate it in a simple way as follows
this.$db.collection('orgs').get({ keys: true })
.then
(response => {
this.orgs = response
console.log('response:', this.orgs)
})
Is there any issue with this approach for just getting my results or should I be using push?
I'm having an interesting issue where Laravel's save() does not replace null values in database, while the update() does. I also tested by manually changing null to 1 and afterwards the save() started updating the column. I am using save() throughout the project and would prefer that it stays that way or that at least I understand the problem before I go changing the code.
Migration (all codes will be partial extracts for brewity):
$table->foreignId('checklist_id')->nullable()->constrained('checklists')->onUpdate('cascade')->onDelete('cascade');
$table->foreignId('action_id')->nullable()->constrained('actions')->onUpdate('cascade')->onDelete('cascade');
Model (added the checklist_id and action_id for testing, otherwise they are not here):
// test
protected $fillable = [
'checklist_id',
'action_id',
];
protected $casts = [
'checklist_id' => 'integer',
'action_id' => 'integer',
];
// actual
protected $fillable = [
];
protected $casts = [
];
Controller:
public function update(UpdateWorkOrderRequest $request, WorkOrder $workOrder)
{
$response = $workOrder->save($request->all());
}
Form submission (from Chrome):
------WebKitFormBoundary73Z4wzsmsGcgOOAM
Content-Disposition: form-data; name="checklist_id"
1
------WebKitFormBoundary73Z4wzsmsGcgOOAM
Content-Disposition: form-data; name="action_id"
1
dd($request->all()):
array:2 [▼
"checklist_id" => "1"
"action_id" => "1"
]
The fields checklist_id and action_id are null after using save(), but they do get updated using update(). It's also working if I do this:
$workOrder->checklist_id = $request->checklist_id;
$workOrder->action_id = $request->action_id;
$workOrder->save();
I opted for save() to write less code instead of above example and because from docs I presume I can use save() when assigning data from $request, like here: https://laravel.com/docs/9.x/eloquent#updates
As discussed in the comments, you're trying to use model->save(attributes).
The save function does not provide any parameters for this. (docs).
You have two options, either use the model->update function (docs):
$workOrder->update($request->all());
Or use the model->fill (docs) function in combination with model->save like so:
$workOrder->fill($request->all())->save();
I am trying to test a function, which basically calls the API by passing a some values, which is then loaded to the schema to be validated and then accepted or rejected based on validation.
The below is my test function.
params = {
'first': [10,20],
'second': 400,
'third ': 'Testing'
}
headers = {
'Authorization': 'Bearer{}'.format(token),
'Data-type' : "json"
}
response = self.client.post(url_for('MyView:post', id=current_id.id),
json=params,
headers=headers)
This renders me the error below:
{
"msg": "Invalid header string: must be a json object"
}
I was able to check the issue and found out its due to the mapping of invalid types. But as headers is still a dictionary, I am not able to comprehend why this error is being rendered.
I have also added the structure of my schema and API below:
class MySchema(marshmallow.Schema):
id = fields.Integer(required=True)
second = fields.Integer(fields.Integer(), required=True)
first = fields.List(fields.String(), required=False)
third = fields.Str(validate=validate.Length(min=0, max=255), required=False)
class Meta:
fields = ('id',
'second',
'first',
'third')
#validates_schema(pass_original=True)
def validate_numbers(self, _, data):
//function code
The below is the structure of my API
class MyView(V1FlaskView):
#jwt_requried
def post(id):
//code
Maybe a space character is missing after Bearer:
'Authorization': 'Bearer {}'.format(token),
I've been trying to structure the relations in my database for more efficient querying and joins but after following the guides for '.belongsToMany', '.through' and '.belongsTo' I'm now getting empty results.
I've got a Sound model and a Keyword model which I want to model with a many-to-many relationship (each Sound can have multiple Keywords, and each Keyword can be related to multiple sounds). Based on the documentation '.belongsToMany' would be the relation to use here.
I've set up my models as follows, using a 'sound_keyword' relational table/SoundKeyword relational model (where each entry has it's own unique 'id', a 'soundID', and a 'keywordID'):
var Sound = bookshelf.Model.extend({
tableName: 'sounds',
keywords: function () {
return this.belongsToMany(Keyword, 'sound_keyword', 'id', 'id').through(SoundKeyword, 'id', 'soundID');
},
});
var Keyword = bookshelf.Model.extend({
tableName: 'keywords',
sounds: function () {
return this.belongsToMany(Sound, 'sound_keyword', 'id', 'id').through(SoundKeyword, 'id', 'keywordID');
}
});
where:
var SoundKeyword = bookshelf.Model.extend({
tableName: 'sound_keyword',
sound: function () {
return this.belongsTo(Sound, 'soundID');
},
keyword: function () {
return this.belongsTo(Keyword, 'keywordID');
}
});
From what I've read in the docs and the BookshelfJS GitHub page the above seems to be correct. Despite this when I run the following query I'm getting an empty result set (the Sound in question is related to 3 Keywords in the DB):
var results = await Sound
.where('id', soundID)
.fetch({
withRelated: ['keywords']
})
.then((result) => {
console.log(JSON.stringify(result.related('keywords')));
})
Where am I going wrong with this? Are the relationships not set up correctly (Possibly wrong foreign keys?)? Am I fetching related models incorrectly?
Happy to provide the Knex setup as needed.
UPDATED EDIT:
I had been using the Model-Registry Plugin from the start and had forgotten about it. As it turns out, while the below syntax is correct, it prefers syntax similar to the following (i.e. lowercase 'model', dropping the '.extends' and putting model names in quotes):
var Sound = bookshelf.model('Sound',{
tableName: 'sounds',
keywords: function () {
return this.belongsToMany('Keyword', 'sound_keyword', 'soundID', 'keywordID');
},
});
var Keyword = bookshelf.model('Keyword',{
tableName: 'keywords',
sounds: function () {
return this.belongsToMany('Sound', 'sound_keyword', 'keywordID', 'soundID');
}
});
Hope this can be of help to others.
Seems like removing the '.through' relation and changing the IDs in the '.belongsToMany' call did the trick (as below), though I'm not entirely sure why (the docs seem to imply belongsToMany and .through work well together - possibly redundant?)
var Sound = bookshelf.Model.extend({
tableName: 'sounds',
keywords: function () {
return this.belongsToMany(Keyword, 'sound_keyword', 'soundID', 'keywordID');
},
});
var Keyword = bookshelf.Model.extend({
tableName: 'keywords',
sounds: function () {
return this.belongsToMany(Sound, 'sound_keyword', 'keywordID', 'soundID');
}
});
I did try my original code with soundID and keywordId instead of 'id' (as below), but without the .through relation and that gave the same empty results.