I have two models User and Item.
The model Item is
class Item
owner = models.ForeignKey(User, related_name='items',null=True)
A user will have more than one item. The user serializer is
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id','username','items')
So that i can access the list of all the items of a user. But here the field items returns only the 'item id', I need the whole item model related to a user. How can i get it?
For customization list of related objects (items) you can use SerializerMethodField. I prefer write another serializer for Item objects and use it for serializing in this method.
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Item
class UserSerializer(serializers.ModelSerializer):
items = serializers.SerializerMethodField()
def get_items(self, obj):
return ItemSerializer(obj.items, many=True).data
class Meta:
model = User
fields = ('id','username','items')
Related
I am using Entity Framework and MVC with Individual User Accounts and I have a table called "Kit" that has a UserId column which is linked to the AspNetUser 'ID' as the foreign key.
When I go to create a new kit and save to the database I want the 'UserId' of my Kit table to be the current ASPNetUser that is logged in.
But currently when I create the new Kit object it just sets the UserId to NULL and never picks up the current user logged in.
What am I missing here?
Controller Create()
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize(Roles = "Admin")]
public ActionResult Create([Bind(Include = "KitId,KitName,ProductId,UserId")] Kit kit)
{
if (ModelState.IsValid)
{
db.Kits.Add(kit);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.ProductId = new SelectList(db.Products, "ProductId", "ProductName", kit.ProductId);
ViewBag.UserId = new SelectList(db.AspNetUsers, "Id", "Email", kit.UserId);
return View(kit);
}
View
<div class="form-group">
#Html.LabelFor(model => model.AspNetUser.Id, "User", htmlAttributes: new { #class = "control-label col-md-2"})
<div class="col-md-10">
#Html.EditorFor(model => model.UserId, "Email", new { htmlAttributes = new { #class = "form-control", #disabled = "disabled" } })
</div>
</div>
Here is the row in my database and you can see UserId was never populated
KitId KitName ProductId Available LoanId UserId
3 TestKit 12 NULL NULL NULL
Disabled controls do not submit a value, so the value of UserId in the POST method will be null (the default for the property - although its unclear why you have made it nullable). While you could make the input readonly (using new { #readonly = "readonly" }), the correct approach is to set the value of the property in the POST method immediately before saving the record to prevent malicious users posting back invalid data and discovering the ID's of other users.
if (ModelState.IsValid)
{
kit.UserId = User.Identity.GetUserId(); // assumes your using Identity
db.Kits.Add(kit);
...
At the very least, your [Bind] attribute should exclude KitId and UserId properties, however the preferred method, especially when editing data, is to use a view model containing only those properties you need in the view, and that view model will also include IEnumerable<SelectListItem> properties from you dropdownlists rather than using ViewBag. Refer What is ViewModel in MVC?.
As a side note, your naming your SelectList the same as the property your binding to which will not work correctly (refer Can the ViewBag name be the same as the Model property name in a DropDownList? for a detailed explanation), and there is no point setting the Selected property using the 4th parameter of the SelectList constructor (its ignored when binding to a model property)
I have an API resource that gives me a list of users that each have several items. The hierarchy is like so:
- users
- user
- items
- item
- item
- item
- user
- items
- item
- item
- item
I would like to display the list of users on a single page, with each user entry displaying each of its items on the page as well.
When any one of these items is clicked, it should set an chosen attribute that is accessible through the overall users collection.
I'm having difficulty getting the item click information to bubble back up. My current implementation is creating a separate items collection in order to render the view, but then I lose the connection to its original user model, so I can't notify it when the item is selected.
My views are structured like so:
class List.Item extends Marionette.ItemView
template: "path/to/template"
events:
"click" : "choose"
choose: (e) ->
# what to do?
class List.User extends Marionette.CompositeView
collection: #collection
template: "path/to/template"
itemView: List.Item
itemViewContainer: "span"
initialize: ->
#collection = new App.Entities.Items(#model.get("items"), parent: #)
events:
"click a" : "toggleChoose"
#include "Chooseable"
class List.Users extends Marionette.CollectionView
itemView: List.User
Is there a better way to structure these collections or views, or is there a way to pass the information from the List.Item view to the parent List.User view and then into the users collection?
EDIT
I have tried backbone-relational, but it didn't seem to quite do what I need. Please correct me if I'm wrong here.
Your List.Item should contain it's current model with all properties at the time when choose is triggered. In this way, you can trigger other events with the List.Item's model values:
choose(e) : ->
trigger("mylistitem:choose", model)
Then listen for the event elsewhere :
itemView.on("itemview:mylistitem:choose", ( childView, model ) -> {
alert(model.get('..whatever..')
}
It is actually possible to instantiate the items collection to reference the parent user and vice-versa directly in Backbone:
class Entities.User extends Backbone.Model
...
initialize: ->
#items = new Entities.Items #get("items"),
user: #
class Entities.Items extends Backbone.Collection
...
initialize: (models, options) ->
#user = options?.user
So now the List.User CompositeView can pass this information to the List.Item ItemView:
class List.User extends Marionette.CompositeView
collection: #collection
...
initialize: ->
#collection = #model.items
With this in place, it is possible to access the user directly from the ItemView:
class List.Item extends Marionette.ItemView
...
events:
"click" : "choose"
choose: (e) ->
e.preventDefault()
user = #model.collection.user
console.log "user: ", user
And from there it's possible to take any necessary actions on the user and its collection.
I have a collection of Animals.
App.Collections.Animals extends Backbone.Collection
model: App.Animal
url: '/animals/' #returns json
And these animal classes:
App.Models.Animal extends Backbone.Model
App.Models.Monkey extends App.Models.Animal
defaults:{type:'Monkey'}
App.Models.Cat extends App.Models.Animal
defaults:{type:'Cat'}
App.Models.Dog extends App.Models.Animal
defaults:{type:'Dog'}
When collection is filled with JSON (records contain the type attribute) I want models to be instantiated as sub-classed models (Monkey,Cat,Dog) and not as Animal. How can you achieve this?
From Backbone documentation:
A collection can also contain polymorphic models by overriding this
property with a function that returns a model.
var Library = Backbone.Collection.extend({
model: function(attrs, options) {
if (condition) {
return new PublicDocument(attrs, options);
} else {
return new PrivateDocument(attrs, options);
}
}
});
The solution is straightforward (pardon the JS, I don't know CoffeeScript):
var SmartZoo = Backbone.Collection.extend({
model: function (attrs, options) {
// This code assumes that the object looks something like '{ type: "Cat", ... }'.
switch (attrs.type) {
case 'Cat':
return new Cat(attrs, options);
case 'Dog':
return new Dog(attrs, options);
default: // Unknown subclass
return new Animal(attrs, options);
}
}
});
You have to:
Include an attribute in your model from which you can infer the type of Backbone model to create. In this example, my objects contain an attribute called "type" whose value is the full name of the Backbone type that represents it. Be sure to set it in the defaults or initialize of your Model so that you can also add real model instances to the collection.
Define the models property of your collection as a function. The first parameter to this function will be the raw JS object (if that's what you passed in), or the attributes object of a Backbone model. Either way, you can access your type field as a property of this object.
Execute your logic to infer the proper model from your type field.
Return an instance of the correct model from the models function.
Here is a JSFiddle that shows this polymorphic collection in action: http://jsfiddle.net/FiddlerOnTheTarmac/uR2Sa/
Override backbone collection's _prepareModel. The collection new uses subclasses when defined otherwise uses the default model.
class App.Collections.Animals extends Backbone.Collection
model: App.Models.Animal
_prepareModel: (attrs, options) ->
if attrs instanceof Backbone.Model
attrs.collection = #
return attrs
options || (options = {})
options.collection = #
model_class = APP.Models[attrs.ntype] or this.model
model = new model_class(attrs, options)
if (!model._validate(attrs, options))
false
else
model
Backbone.LocalStorage stores a list of players client-side when calling create on a players collection, but the stored models are not being fetched later, even though I can inspect them in localStorage. I can call #collections.players.localStorage.findAll() to retrieve all the stored objects and manually push them onto my collection.
class App.Models.Player extends Backbone.Model
defaults:
name: 'Unnamed'
team: 'Unassigned'
class App.Collections.Players extends Backbone.Collection
localStorage: new Backbone.LocalStorage('players')
model: App.Models.Player
class App.ViewModels.Game extends kb.ViewModel
constructor: ->
#collections =
players: new App.Collections.Players()
#collections.players.fetch(
success: (collection) =>
console.log(collection) # shows none
console.log(#collections.players.localStorage.findAll())
# shows all the stored players
)
# #players below is rendered by a foreach in a knockout template
#players = kb.collectionObservable #collections.players, { view_model: App.ViewModels.Player }
addPlayer: -> # called when a button is pressed
#collections.players.create(new App.Models.Player({ name: 'New Player' }))
return
Why is Knockback unable to fetch the stored entities automatically?
The following call manually retrieves all objects:
_.each #collections.players.localStorage.findAll(), (elem) =>
#collections.players.add(elem)
So here's what i have set up. I have two tables; users and todos. Every user can have multiple "todos".
Here's what the tables look like:
Users:
Todos:
And the models:
class User extends Eloquent
{
public function todo() {
$this->has_many('todo');
}
}
And...
class Todo extends Eloquent
{
public function user() {
$this->belongs_to('user');
}
}
Note that i already have a "todo" attached to the user in the database. So, with that said, should i not be able to do the following:
$user = User::find(1)->todo;
To get the todo's for that user? I'm currently getting Null when dd()-ing it:
array(1) {
["todo"]=>
NULL
}
So, any ideas? I tried removing belongs_to() from the Todo's model, because it shouldn't be needed right? Either way it makes no difference. Am i missing something?
You need to return the relation objects, e.g.
class User extends Eloquent
{
public function todos() {
return $this->has_many('Todo');
}
}
Also note that relations that return an array (e.g. has_many, has_many_and_belongs_to) are typically named plural, so 'todos' versus 'todo.