Finding Descendant Item Sets - django-models

Let's say I have these Models:
class Category(MP_Node):
name = models.CharField(max_length=30)
class Item(models.Model):
category = models.ForeignKey(Category)
and I would like to find all Items belonging to any descendant of a given Category.
Usually I would write category.item_set but this is just Items belonging to the given level of the hierarchy.
Using the example tree in the treebeard tutorial, if an Item belongs to "Laptop Memory", how would I find all Items belonging to descendants "Computer Hardware" where "Laptop Memory" is one of those descendants?

I just had the same problem and figured out how to do it (consider it inside the function get_queryset of a ListView):
category = Category.objects.filter(slug=self.kwargs['category']).get()
descendants = list(category.get_descendants().all())
return self.model.objects.select_related('category').filter(category__in=descendants+[category, ])
Another option that I came up with was using a filter with 'OR':
from django.db.models import Q
category = Category.objects.filter(slug=self.kwargs['category']).get()
descendants = list(category.get_descendants().all())
return self.model.objects.select_related('category').filter(Q(category__in=category.get_descendants()) | Q(category=category))

I looked at the treebeard code to see how it gets descendants of a node. We can apply the same filters as a related field lookup.
paramcat = Category.objects.get(id=1) # how you actually get the category will depend on your application
#all items associated with this category OR its descendants:
items = Item.objects.filter(category__tree_id=paramcat.tree_id, category__lft__range=(paramcat.lft,paramcat.rgt-1))
I think using intermediate calls like get_descendants will result in one query per descendant, plus load all descendants into memory. It defeats the purpose of using treebeard in the first place
I'd be interested in seeing a custom lookup based on this code, I'm not sure how to do it...

Related

AS3 object passed as e.target does not match identical object passed through its array index

I'm designing an inventory system. right now, I need to test whether or not an item is in the inventory in order for the stage to know whether to instantiate that item in the particular level or not.
I add the items to the levels in groups, so this code is located within an array loop which "unloads" the "pack" of items corresponding to each level.
if (inv.indexOf(group[i]) == -1) {
//add item + item functionality
}
This method works when I add the item to the inventory such as this:
inv.push(group[i]);
if (inv.indexOf(group[i]) == -1) {
//add item + item functionality
}
But that doesn't work, because why would I add an item to the inventory without the user collecting it first? so the code is actually structured as so:
if (inv.indexOf(group[i]) == -1) {
//if item is not in inventory, add to stage
addChild(group[i]);
//when a user clicks this (any) item,
group[i].addEventListener(MouseEvent.CLICK, function itemFunctionality(e:MouseEvent){
//target item clicked
var item = e.target;
//add the item to the inventory
inv.push(item);
//sidenote: if i were to check inv.indexOf(item) here, i
//would get a positive index. unfortunately,
//i cant check whether the item is in the inventory
//after its already been added to the level...
item.removeEventListener(MouseEvent.CLICK, itemFunctionality);
});
}
The problem is when you leave and come back to the level, the items you already collected re-instantiate. If you collect an item again, the inventory adds a copy of the item you already collected.
The inv.indexOf(group[i]) checker doesn't understand that when the array loop reaches the corresponding, item group[i] == the object added to the inventory through inv.push(item) or in other words inv.push(e.target) (which, of course, I couldn't write directly into the code)...
When I trace whats inside of static array inv, what group[i] is within the array loop, or what e.target is, they all output the same type of item, "[object itemName]", signifying that the indexOf check SHOULD match up.
Update :
It appears if I make the items static as well as the array group they belong to this method works within the mouse event callback:
inv.push(item);
group.splice(group.indexOf(item), 1);
Though I had to remove the items and the item groups from their own class and put them inside of the level class itself... I feel this method kind of sucks because everything is getting disorganized and grouped into the same class.
Any helpful suggestions?
Objects are matched with their references. It means two objects created from the same class are not identical, they are different objects.
Assign unique IDs to your items and use them in your inventory. Like;
inv.push(item.id);
if (inv.indexOf(item.id) == -1) {
//add item + item functionality
}
Working with IDs is also better for serializing / deserializing.
There's your problem:
The problem is when you leave and come back to the level, the items you already collected re-instantiate.
Why would coming back to a level cause reinstatiation of any kind?
You should only ever once create each level object and part of that process should be creating all objects contained in that level. Visiting a level merely is an interaction with that object, which may include removing objects and adding them to the inventory. Once the objects are gone from the level, they are gone.
There's no reason to reinstantiate a level (or any of the objects within it) when revisiting it. If you are running into this problem because you are using a time line based approach with frames and gotoAndStop() to switch between levels then this is the core of your problem and you should stop doing that.

How do I modify/enrich an Eloquent collection?

I'm using eloquent relationships.
When I call $carcollection = $owner->cars()->get(); I have a collection to work with. So let's say that I have, for this particular owner, retrieved three cars. The collection is a collection of three arrays. Each array describes the car.
This is all working fine.
Now I want to add more attributes to the array, without breaking the collection. The additional attributes will come from a different source, in fact another model (e.g. servicehistory)
Either I retrieve the other model and then try merge() them, or I try manipulate the arrays within the collection without breaking the collection.
All this activity is taking place in my controller.
Is one way better than another, or is there a totally different approach I could use.... perhaps this logic belongs in the model themselves? Looking for some pointers :).
Just to be specific, if you do $owner->cars()->get(); you have a collection of Car Models, not array.
That have been said, you can totally load another relation on you Car model, using
$carcollection = $owner->cars()->with('servicehistory')->get();
$carcollection->first()->servicehistory;
You can try to use the transform method of the collection.
$cars = $owner->cars()->get();
$allServiceHistory = $this->getAllService();
$cars->transform(function($car) use($allServiceHistory) {
// you can do whatever you want here
$car->someAttribute = $allServiceHistory->find(...):
// or
$car->otherAttribute = ServiceHistoryModel::whereCarId($car->getKey())->get();
});
And this way, the $cars collection will be mutated to whatever you want.
Of course, it would be wiser to lazy load the data instead of falling into an n+1 queries situation.

ExtJS Find Node within Tree Branch

I'm trying to check whether a certain node exists beneath a branch of an ExtJS tree. Knowing the ID of the parent node, is there a library function to check whether a node exists beneath the parent (by its ID)?
I've checked the API numerous times over, and can only seem to accomplish this by iterating through the entire branch of the tree.
Is there a library function which allows me to check if a child exists (by its ID) if the parent node ID is known?
Thanks!
PS, to find the parent ID, I'm using the following:
tree.getNodeById('myID');
Ext.tree.TreeNode "contains" function does exactly what you want:
var parent = tree.getNodeById('myID');
parent.contains(tree.getNodeById('childId'));
Have you looked at DomQuery? The API defines the method jsSelect: selects a group of elements.
jsSelect( String selector, [Node/String root] ) : Array
Parameters:
selector : String
The selector/xpath query (can be a comma separated list of selectors)
root : Node/String
(optional) The start of the query (defaults to document).
Returns an Array of DOM elements which match the selector. If there are no matches, and empty Array is returned.

gqlquery for no values in ListProperty

I have this code to find all the nodes where property branches is empty.
nobranches=TreeNode.all()
for tree in nobranches:
if tree.branches==[]:
I wanted to find a better, more efficient way to do this. A meathod where I don't have to retrieve all the TreeNodes. I have tried TreeNode.all().filter(branches=[]) but this gives me a message, "BadValueError('Filtering on lists is not supported'" . How can I do something like TreeNode.gql('WHERE branches=:1', []).fetch(100). I tried this, but I get a "BadValueError: May not use the empty list as a property value; property is []". Is there any other efficient way?
BTW, Here is what TreeNode looks Like
class TreeNode(db.Model):
name = db.StringProperty()
branches =db.ListProperty(db.Key)
You can't do this with a filter: as Saxon says, there's no index row matching what you want to retrieve, and so no way to retrieve it.
One simple alternative is to store another property that contains the number of elements in the list, and filter on that. aetycoon is a library that contains computed properties that may help with that:
class TreeNode(db.Model):
name = db.StringProperty()
branches = db.ListProperty(db.Key)
branch_count = aetycoon.DerivedProperty(lambda self: len(self.branches))
The documentation on how indexes are stored says:
For multi-valued properties, such as ListProperty and StringListProperty, each value has its own index row, so using multi-valued properties does result in more indexing overhead.
So for each item in your list property, there is a row in the index.
My expectation would be that if there are no items in the list property, then there would be no rows in the index. So it wouldn't be possible to use the index to retrieve entities with an empty list.
One solution would be to add another property (eg hasbranches = db.BooleanProperty()), which you maintain when you add or remove branches. Then you will be able to filter for hasbranches = False.

App Engine: Filter for Choosing Entities with a Specific Item Present in their ListProperties

I need to filter entities based on one of their ListProperties having a certain element present. So kind of like:
entities.filter('listProp IN ',element) except where listProp and element are reversed if you see what I mean.
Anyone know how to filter like this?
If I understand you correctly, you want to find all entities which have that particular element present. You should be able to use: entities.filter('listProp =', element)
Look at: http://code.google.com/appengine/docs/python/datastore/typesandpropertyclasses.html#ListProperty
It says, "list_property = value tests if the value appears anywhere in the list".
Ok so it turns out the IN equality clause takes care of this case for lists automatically.
As in it does a for ... each on the list of elements to be searched for and if any one of them is present in the ListProperty for each entity it will return that entity.

Resources