tags:

views:

55

answers:

3

model:

class Store(models.Model):
  name = models.CharField(max_length = 20)
  class Admin:
    pass
  def __unicode__(self):
    return self.name

class Stock(Store):
  products = models.ManyToManyField(Product)
  class Admin:
    pass
  def __unicode__(self):
    return self.name

class Product(models.Model):
  name = models.CharField(max_length = 128, unique = True)
  parent = models.ForeignKey('self', null = True, blank = True, related_name='children')
  (...)
  def __unicode__(self):
    return self.name

mptt.register(Product, order_insertion_by = ['name'])

admin.py:

from bar.drinkstore.models import Store, Stock

from django.contrib import admin

admin.site.register(Store)
admin.site.register(Stock)

Now when I look at admin site I can select any product from the list. But I'd like to have a limited choice - only leaves. In mptt class there's function:

is_leaf_node() -- returns True if the model instance is a leaf node (it has no children), False otherwise.

But I have no idea how to connect it

I'm trying to make a subclass: in admin.py:

  from bar.drinkstore.models import Store, Stock

  from django.contrib import admin

  admin.site.register(Store)

  class StockAdmin(admin.ModelAdmin):
    def queryset(self, request):
      return super(StockAdmin, self).queryset(request).filter(ihavenoideawhatfilter)

    admin.site.register(Stock, StockAdmin)

but I'm not sure if it's right way, and what filter set.

UPD: This is definetely wrong way. the queryset in class StockAdmin produces list of stocks. But I need to filter product list "on stock" - still don't know how.

A: 

So far your idea is right, I'm no expert on how to filter this correctly, but if you look at mptt.models.get_descendant_count you see how the number of descendents i calculated, the leaves are those where the count is zero. I guess you will have to make this condition into raw sql!

EDIT: I just read the title of your question again right now, is it now about seectbox values or changing the queryset for the change list?

lazerscience
+2  A: 

Edit: Completely updated this

So the queryset, is finally ok but you need to filter the products on the Stock page select box (I guess?). You can define a custom form for the Stock ModelAdmin.

class StockForm(ModelForm):
    products = forms.ModelChoiceField(queryset=Products.objects.filter(lft=F('rght')-1))
    class Meta:
        model = Stock

class StockAdmin(admin.ModelAdmin):
    form = StockForm
Béres Botond
+1 for that! @SledgehammerPL: If you are implementing somethng like that you should keep in mind that the right/left attributes in django-mptt do not always have to be called the same, so look eg at `Product._meta.right_attr` for their actual name!
lazerscience
@lazerscience - you are "rght" ;D
SledgehammerPL
syntax error: Product.objects.annotate(desc_count=(F('rght') - F('lft') - 1) / 2) AttributeError: 'ExpressionNode' object has no attribute 'lookup'
SledgehammerPL
Might need to be: annotate(desc_count=( (F('rght') - F('lft') - 1) / 2) )
Béres Botond
@SledgehammerPL: Sorry for the confusion, unfortunately annotate cannot be combined with F() ... hmmm
Béres Botond
Yes, we are at final straight. Not ModelChoiceField but ModelMultipleChoiceField, and it gives the correct result. The only inconvenience is, that add new Product button disappears. Give me it back, and the task will be complete :D
SledgehammerPL
+3  A: 

Botondus has the right idea, but you can't do that with annotate - that's for aggregations across related querysets. Try using extra instead:

qs = super(StockAdmin, self).queryset(request).extra(
        select={ 'desc_count': '(rght-lft-1)/2' }
     ).filter(desc_count=0)
Daniel Roseman
Indeed, this is the right way :). +1
Béres Botond
I know how stupid it sounds but:FieldError: Cannot resolve keyword 'desc_count' into field. Choices are: children, genitive, id, image, level, lft, name, parent, receipt, receiptcomponent, rght, stock, tree_id
SledgehammerPL
qs = Product.objects.extra( select={ 'desc_count': '(rght-lft-1)/2' } )print qs.querySELECT ((rght-lft-1)/2) AS "desc_count", "common_product"."id", "common_product"."name", "common_product"."genitive", "common_product"."parent_id", "common_product"."image", "common_product"."lft", "common_product"."rght", "common_product"."tree_id", "common_product"."level" FROM "common_product"qs.filter(desc_count=0)FieldError: Cannot resolve keyword 'desc_count' into field. Choices are: children, genitive, id, image, level, lft, name, parent, receipt, receiptcomponent, rght, stock, tree_id
SledgehammerPL
ah! Of course - this wouldn't work! the problem is in sql:where clause can not use aliases! correct filter is WHERE (rght-lft-1)/2)=0 but how to put it into filter ;D
SledgehammerPL
GOT IT! qs.filter(lft=F('rght')-1)
SledgehammerPL
almost... Now the problem is that filter is putted on stock table not product. How to "suggest" to make join?
SledgehammerPL
I'm wondering if we make the filter in right place ;D
SledgehammerPL
qs.filter(products__lft=F('products__rght')-1)?
lazerscience
YES and NO ;D Yes - this produces correct filter, No - definetely wrong place ;D
SledgehammerPL
Well that was actually my question in the edit of my first answer: WHAT do you want to filter exactly WHERE (because you also wrote "selectbox" values)?
lazerscience