django multiwidget subclass not calling decompress() - compression

I am trying to implement a MultiValueField for IP Adress/Domain Name entries. It works as expected for entering data.
My Problem is that if I want to display the form bound to specific data, the IP Address/Domain Name field stays empty. All other fields are filled with the desired data. If I use a normal CharField, I get the data that I would expect. But it does not work with my custom field.
I have tracked it down to the fact that my custom MultiWidget does not call its decompress method.
Here is my Field:
class accessIPField(forms.MultiValueField):
"""
custom Field for access IP
"""
def __init__(self, *args, **kwargs):
self.fields=(
forms.IPAddressField(label='IP Adress'),
forms.CharField(max_length=50,label='Domain Name')
)
self.widget=accessIPWidget()
super(accessIPField,self).__init__(self.fields,self.widget, *args, **kwargs)
def compress(self,data_list):
if data_list:
return " ".join(data_list)
And here is my widget:
class accessIPWidget(forms.MultiWidget):
"""
Widget to display IP Adress / Domain name pairs
"""
def __init__(self,*args,**kwargs):
self.widgets=(forms.TextInput(),forms.TextInput())
super(accessIPWidget,self).__init__(self.widgets,*args,**kwargs)
def decompress(self,value):
print 'decompress called'
if value:
return value.rsplit()
return [None,None]
def format_output(self, rendered_widgets):
return u'\n'.join(rendered_widgets)
The whole thing is called (in a larger context) as
self.fields['access_IPs'] = accessIPField()
Now as you can see, I put a print statement in my compress method, and I never get to see that statement. Also, if I rename compress to something like foobar, I would expect (according to the django code for MultiWidget) to get the NotImplementedError, which is not the case. Any suggestions?
I am using python 2.6.5, django 1.1 on ubuntu server 10.04.

It turns out that the problem was with the value_from_datadict() method as implemented by MultiWidget. First of all, it allready returned a list, so that is why decompress() was not called in the first place. Secondly, it allways returen a [None,None] list, so that is why the bound form stayed empty.
I needed to implement my own (within my accessIPWidget class):
def value_from_datadict(self, data, files, name):
try:
return data.get(name,None).rsplit()
except AttributeError:
return [widget.value_from_datadict(data, files, name + '_%s' % i) for i, widget in enumerate(self.widgets)]
Now the last line is what the original method did. In order to get the data into the bound form, I needed to add data.get(name,None).rsplit().
As far as I understand, the original value_from_datadict method only works for unbound fields. Because it changes the name of the original field to name + '_%s', which is what you get when pressing the submit button. In order to fill in a bound method, the datadict needs to be queried for 'name' only.
Hm, not shure if there is a way around this, but it seems to me that this behaviour should at least be documented somewhere.
Maybe I misunderstood something?

Related

Django object "lock" and context rendering

I have a simple(I think) question, about Django context rendering.
I'll step right into it -
Basically what I need is, some temp table, which in my case, I called Locked. And when a user presses a button, which Is a form, that object goes straight to the table Locked(just a simple insert). And inside that table there is a field called is_locked, and if its True, that object needs to go gray, or to have some lock icon inside the html table.
Just some kind of a viewable sign, that an object is inside the table Locked, and that another user can't access it.
But, my problem is, since in my views.py, my lock function is not returning exact html where I want to render that locker icon, instead, it returns another html.
Is there any way, to render same context, on 2 html pages? Thank's.
This is my code :
views.py
def lock(request, pk):
# Linking by pk.
opp = get_object_or_404(OpportunityList, pk=pk)
opp_locked = get_object_or_404(Locked, pk=pk)
# Taking two parametters for 2 fields.
eluid = Elementiur.objects.get(eluid=pk)
user = User.objects.get(username=request.user)
# Dont bother with this one! Just pulling one field value.
field_name = 'optika_korisnik'
obj = OpportunityList.objects.get(pk=pk)
field_object = OpportunityList._meta.get_field(field_name)
field_value = getattr(obj, field_object.attname)
# This is the main part! This is where i'm inserting data into Locked table.
if opp_locked.DoesNotExist:
opp_locked.id = int(eluid.eluid)
opp_locked.locked_eluid = eluid
opp_locked.locked_comment = field_value
opp_locked.locked_user = user
opp_locked.locked_name = 'Zaključao korisnik - ' + request.user.username
opp_locked.is_locked = True
opp_locked.save()
# This is what has to be returned, but i need context on the other page.
return render(request, 'opportunity/detalji/poziv.html',
{'opp': opp, 'locked': opp_locked})
else:
# This return has the context that i need(from the first opp_locked variable)
return render(request, 'opportunity/opp_optika.html', {'locked_test': opp_locked})
I can provide more code, but i think that it's not important for this type of question, because all of the logic is happening inside the lock finction, and last two returns.
I just had a quick overview of your snippet sorry if this not help you but you need to review it a little bit.
You call DoesNotExist on an instance of a Locked model
if opp_locked.DoesNotExist: [...]
that's not how you should use this exception.
You have a method .exists() that is available but only for Querysets.
Also if your instance does not exists you are alredy returning an Http404 response when you use get_object_or_404() method.
And perhaps you should avoid sharing primary keys between instances and replace them with models.OneToOneField (OneToOnefield)
Since i got no answers, i added a new field, is_locked, into my Locked model and that solved it.

How to make Python property object human-readable - Rendering it through Django templates

I have this property that should display the total price for each product, and I call it in my views and render it. On the rendered page, it just shows up as <property object at 0x0000023CDA655048> How am I supposed to make this human-readable? I know this is really simple, and I can't figure out how. I tried the following within my cart.py file that the class and property function belongs to:
def __str__(self):
return self.total_price
#property
def total_price(self):
return sum(Decimal(item['price']) * item['quantity'] for item in self.cart.values())
If I return it as a print statement, it returns none instead of an object, so maybe I am rendering it in the wrong place?
I called it in my views.py like:
def get_cart(request):
cart = Cart(request)
total = Cart.total_price
return render(request, 'buylist/cart.html', {'cart':cart, 'total':total})
within a for loop, I rendered it for each item in my cart.html and called it like:
Total: {{total}}
I am expecting to see the human-readable total price for each product on each product's line, but instead I'm just seeing the actual property object. I'm sure it's something really simple I'm missing haha, but I've been stuck on this for a minute! If you could help, that would be awesome.
I needed to use cart.total_price instead of Cart.total_price. I referenced the class function directly instead of the requested cart

How to dynamically update the 'initial' value in a Form ChoiceField

I have a Form with the following field:
image_choices = []
images = forms.ChoiceField(label=_("Images"), choices=image_choices, initial="")
I need to be able to update the value of the 'initial' attribute, after I learn what that value should be.
Currently, I have this assignment done within the __init__:
def __init__(self, request, image_choices=image_choices,
flavor_choices=flavor_choices, args, *kwargs):
super(UpdateWorkload, self).__init__(request, args, *kwargs)
selected_image = selected_workload['image']
self.fields['images'].initial = selected_image
I do not get any errors, and, when printed, the value is there,
but, in the actual form on the screen,
I still get my default list, and no specific items are selected, as per self.fields['images'].initial = str(selected_image)
How can I fix that?
After all, using this approach:
self.fields['images'].initial = selected_image
self.fields['flavors'].initial = selected_flavor
is working. The only thing I did differently was changing my backend from django restframework to tastypie

Dynamic forms in Django hosted by GAE

I am a newbie to Django, GAE, and met some problems when I am trying to build some dynamic forms using Django. The idea is that the selection made from one drop-down box determines value in another field.
In detail, if an user
choose "A" in "Species", then the value in the "body_weight" will be 178. Similarly,
if he choose "B" in "Species", the value in "body_weight" is 1580, and
if choose "C" in "Species", the he can fill in the "body_weight"
I read some examples, and found some features like "init, and super should be included". Thus I modified my codes, but it still did not work.
My problems are:
What is the right position to place "init" and "super"?
Do I need "self.field" here?
All suggestions are welcomed!
Thanks!
Here is the code.
Species_CHOICES=(('178','A'),('1580','B'),('','C'))
class Inp(forms.Form):
Species = forms.ChoiceField(label='Species', choices=Species_CHOICES, initial='A')
#Here detect the choice made by the user
def get_STB_choices(Species):
if Species=='178':
r= 178
elif Species=='1580':
r= 1580
else:
r= 4440
return a
#assign values to a new parameter based on user's choice
def __init__(self, *args, **kwargs):
super(Inp, self).__init__(*args, **kwargs)
body_weight=forms.FloatField(required=True, label='Body weight', initial=get_STB_choices(Species))
#write out in HTML
class InputPage(webapp.RequestHandler):
def get(self):
html = str(Inp())
self.response.out.write(html)
You're going to need Javascript running on the page to update the available selections.
I implemented it one way here. Each time you make a selection, the page makes an AJAX request to get the new form fields that were dependent on the initial change.
https://github.com/dragonx/django-hier-ajax-form
After I did mine, I realized there was already an existing project that did the same thing, but our implementations are pretty different:
https://github.com/digi604/django-smart-selects
btw, I believe usually when you call super() you use it with the name of the current class, so I would have expected
super(Inp, self).__init__(*args, **kwargs)

Setting the selected value on a Django forms.ChoiceField

Here is the field declaration in a form:
max_number = forms.ChoiceField(widget = forms.Select(),
choices = ([('1','1'), ('2','2'),('3','3'), ]), initial='3', required = True,)
I would like to set the initial value to be 3 and this doesn't seem to work. I have played about with the param, quotes/no quotes, etc... but no change.
Could anyone give me a definitive answer if it is possible? And/or the necessary tweak in my code snippet?
I am using Django 1.0
Try setting the initial value when you instantiate the form:
form = MyForm(initial={'max_number': '3'})
This doesn't touch on the immediate question at hand, but this Q/A comes up for searches related to trying to assign the selected value to a ChoiceField.
If you have already called super().__init__ in your Form class, you should update the form.initial dictionary, not the field.initial property. If you study form.initial (e.g. print self.initial after the call to super().__init__), it will contain values for all the fields. Having a value of None in that dict will override the field.initial value.
e.g.
class MyForm(forms.Form):
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
# assign a (computed, I assume) default value to the choice field
self.initial['choices_field_name'] = 'default value'
# you should NOT do this:
self.fields['choices_field_name'].initial = 'default value'
You can also do the following. in your form class def:
max_number = forms.ChoiceField(widget = forms.Select(),
choices = ([('1','1'), ('2','2'),('3','3'), ]), initial='3', required = True,)
then when calling the form in your view you can dynamically set both initial choices and choice list.
yourFormInstance = YourFormClass()
yourFormInstance.fields['max_number'].choices = [(1,1),(2,2),(3,3)]
yourFormInstance.fields['max_number'].initial = [1]
Note: the initial values has to be a list and the choices has to be 2-tuples, in my example above i have a list of 2-tuples. Hope this helps.
I ran into this problem as well, and figured out that the problem is in the browser. When you refresh the browser is re-populating the form with the same values as before, ignoring the checked field. If you view source, you'll see the checked value is correct. Or put your cursor in your browser's URL field and hit enter. That will re-load the form from scratch.
Both Tom and Burton's answers work for me eventually, but I had a little trouble figuring out how to apply them to a ModelChoiceField.
The only trick to it is that the choices are stored as tuples of (<model's ID>, <model's unicode repr>), so if you want to set the initial model selection, you pass the model's ID as the initial value, not the object itself or it's name or anything else. Then it's as simple as:
form = EmployeeForm(initial={'manager': manager_employee_id})
Alternatively the initial argument can be ignored in place of an extra line with:
form.fields['manager'].initial = manager_employee_id
Dave - any luck finding a solution to the browser problem? Is there a way to force a refresh?
As for the original problem, try the following when initializing the form:
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.base_fields['MyChoiceField'].initial = initial_value
To be sure I need to see how you're rendering the form. The initial value is only used in a unbound form, if it's bound and a value for that field is not included nothing will be selected.