Re: Need help modifying form on intermediate page of admin action

2017-07-29 Thread Jack Twilley
I looked over those apps and I don't think they'll help with my issue.

Let's say that Crate 1 has a capacity of 12 and currently holds 10 jars. 
 If I look at the jars in Crate 2, select 3 of them, and then trigger the 
admin action to move the selected jars, Crate 1 should *not* be in the drop 
down list on the intermediate form because it only has room for 2 more 
jars.  There isn't really a value entered by the user, it's just the number 
of jars that are selected before the admin action is triggered that matters.

I do not know how to modify the value of dest in an unbound instance of 
MoveMultipleJarsForm based on the length of the queryset.  As a workaround, 
I think I could do some validation in move_multiple_jars() that would check 
the capacity of the destination and generate an error message if the 
destination can't hold all the selected jars, but that seems inelegant.

Jack.

On Saturday, July 29, 2017 at 10:24:02 PM UTC-7, mark wrote:
>
> You might find one of these helpful - 
>
> https://github.com/digi604/django-smart-selects
>
> https://github.com/yourlabs/django-autocomplete-light
>
> Basically, you want a "chained select" that is populated after a value is 
> entered by the user, if I understand the problem. If you want to roll your 
> own, then you need something like ajax or node.js to populate the crate 
> select once you know how many jars you want to ship. 
>
> Another way to think about it, is to use a validation on the crate object 
> to make sure there is room. This solution may not be as elegant, but does 
> not need any additional coding outside of django. 
>
> Good luck!
>
> Mark
>
> On Sat, Jul 29, 2017 at 8:57 PM, Jack Twilley <mat...@gmail.com 
> > wrote:
>
>> I have a Django app which keeps inventory.  There are two relevant 
>> classes here, one named Jar and another named Crate.  The Jar class has an 
>> attribute crate which is a foreign key for the Crate class.  The Crate 
>> class has an attribute capacity of type models.IntegerField which 
>> represents the maximum number of Jar objects that it can hold.  The Crate 
>> class also has a property "jars" which returns "self.jar_set.filter(
>> is_active=True).count()" or in other words, the number of Jar objects 
>> with is_active set to True that are associated with that Crate object.
>>
>> This code is from models.py and shows the two object classes:
>>
>> class Crate(models.Model):
>> number = models.IntegerField()
>> slug = models.SlugField(max_length=10, unique=True, blank=True)
>> capacity = models.IntegerField(default=12)
>> # bin where the crate can currently be found
>> bin = models.ForeignKey(Bin)
>>
>>
>> created_at = models.DateTimeField(auto_now_add=True)
>> updated_at = models.DateTimeField(auto_now=True)
>>
>>
>> class Meta:
>> ordering = ['number']
>>
>>
>> @property
>> def jars(self):
>> return self.jar_set.filter(is_active=True).count()
>>
>>
>> @property
>> def shortname(self):
>> return "C%d" % self.number
>>
>>
>> @property
>> def longname(self):
>> return "Crate %d" % self.number
>>
>>
>> @property
>> def name(self):
>> return self.longname
>>
>>
>> def __str__(self):
>> return self.name
>>
>>
>> def save(self, *args, **kwargs):
>> self.slug = 'c%d' % self.number
>> super(Crate, self).save(*args, **kwargs)
>>
>>
>> def get_absolute_url(self):
>> return reverse('inventory_crate', kwargs={'crate_slug': self.slug
>> })
>>
>>
>> def clean(self):
>> # ensure that the current number of jars is less than or equal 
>> to the capacity
>> if self.jars > self.capacity:
>> raise ValidationError('Capacity of crate exceeded')
>>
>>
>>
>>
>> class Jar(models.Model):
>> product = models.ForeignKey(Product)
>> number = models.IntegerField()
>> slug = models.SlugField(max_length=13, unique=True, blank=True)
>> # volume in liters
>> volume = models.IntegerField(default=1)
>> crate = models.ForeignKey(Crate)
>> # is_active = not yet sold
>> is_active = models.BooleanField(default=True)
>> # is_available = considered 'in stock'
>> is_available = models.BooleanField(default=True)
>> created_at = models.DateTimeField(auto_now_add=True)
>> updated_at = models.DateTimeField(auto_no

Need help modifying form on intermediate page of admin action

2017-07-29 Thread Jack Twilley
I have a Django app which keeps inventory.  There are two relevant classes 
here, one named Jar and another named Crate.  The Jar class has an 
attribute crate which is a foreign key for the Crate class.  The Crate 
class has an attribute capacity of type models.IntegerField which 
represents the maximum number of Jar objects that it can hold.  The Crate 
class also has a property "jars" which returns "self.jar_set.filter(
is_active=True).count()" or in other words, the number of Jar objects with 
is_active set to True that are associated with that Crate object.

This code is from models.py and shows the two object classes:

class Crate(models.Model):
number = models.IntegerField()
slug = models.SlugField(max_length=10, unique=True, blank=True)
capacity = models.IntegerField(default=12)
# bin where the crate can currently be found
bin = models.ForeignKey(Bin)


created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)


class Meta:
ordering = ['number']


@property
def jars(self):
return self.jar_set.filter(is_active=True).count()


@property
def shortname(self):
return "C%d" % self.number


@property
def longname(self):
return "Crate %d" % self.number


@property
def name(self):
return self.longname


def __str__(self):
return self.name


def save(self, *args, **kwargs):
self.slug = 'c%d' % self.number
super(Crate, self).save(*args, **kwargs)


def get_absolute_url(self):
return reverse('inventory_crate', kwargs={'crate_slug': self.slug})


def clean(self):
# ensure that the current number of jars is less than or equal to 
the capacity
if self.jars > self.capacity:
raise ValidationError('Capacity of crate exceeded')




class Jar(models.Model):
product = models.ForeignKey(Product)
number = models.IntegerField()
slug = models.SlugField(max_length=13, unique=True, blank=True)
# volume in liters
volume = models.IntegerField(default=1)
crate = models.ForeignKey(Crate)
# is_active = not yet sold
is_active = models.BooleanField(default=True)
# is_available = considered 'in stock'
is_available = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)


objects = models.Manager()
instock = InStockJarManager()


class Meta:
ordering = ['created_at']
unique_together = ('product', 'number')


@property
def name(self):
return "%s%d" % (self.product, self.number)


def __str__(self):
return self.name


def save(self, *args, **kwargs):
self.slug = ''.join([self.product.slug, str(self.number)])
super(Jar, self).save(*args, **kwargs)


def get_absolute_url(self):
return reverse('inventory_jar', kwargs={'jar_slug': self.slug})


I have written an admin action named "move_multiple_jars" on the JarAdmin 
class which allows me to select multiple Jar objects and move them to a 
particular Crate object.  Right now, the intermediate page for that admin 
action has a form "MoveMultipleJarsForm" with a drop down list which 
contains all Crate objects sorted by number.  This admin action works in 
that it does move the Jar objects from one Crate object to another.  I 
would like that drop down list to contain only the Crate objects which have 
sufficient extra capacity to hold the number of Jar objects selected.

This code is from admin.py:

class JarAdmin(SmarterModelAdmin):
valid_lookups = ('product',)
form = JarAdminForm
list_display = ('product', 'number', 'is_active', 'is_available', 
'crate',)
list_display_links = ('number',)
list_filter = ('product', 'crate', 'is_active')
list_per_page = 50
ordering = ['-created_at', 'is_active']
search_fields = ['product', 'crate']
readonly_fields = ('created_at', 'updated_at',)
actions = ['move_multiple_jars']


class MoveMultipleJarsForm(forms.Form):
# JMT: this needs to somehow be restricted to those crates that 
have room
dest = forms.ModelChoiceField(queryset=Crate.objects.all().order_by(
'number'))


def move_multiple_jars(self, request, queryset):
form = None


if 'apply' in request.POST:
form = self.MoveMultipleJarsForm(request.POST)


if form.is_valid():
dest = form.cleaned_data['dest']


count = 0
for jar in queryset:
jar.crate = dest
jar.save()
count += 1


plural = ''
if count != 1:
plural = 's'


self.message_user(request, "Successfully moved %d jar%s to 
%s" % (count, plural, dest))
return HttpResponseRedirect(request.get_full_path())
if not