In Django, why does my form context variable disappear? - django

Take this example form.
class TestForm(forms.Form):
def __init__(self, *args, **kwargs):
super(TestForm, self).__init__(*args, **kwargs)
self.fields["test"] = forms.CharField()
In my view, I have this code:
test_form = TestForm()
context['test_form'] = test_form
If I print(test_form), it will appear just fine. Now, when I use {% debug %} in my template, this is the output: { ... 'test_form': , ... }. In other words, the form disappears. I suspect it has something to do with adding a field after initialization, but I would really like to know why? Also how to get around the seeming limitation, if possible.

To test if your form has disappeared, you can try displaying it:
{% for field in test_form %}
{{ field }}
{% endfor %}
This should display the each field of the form as HTML. Hope it helps!

Your contact variable hasn't disappeared: it's right there, in the output you show us. If it had disappeared, you wouldn't see the test_form key in that dictionary.
As to why you don't see a value for that key, that's simply because the default representation of a python object is like this <TestForm object at ....>, and your browser is interpreting the angle brackets as an unknown HTML tag which it ignores.
Put your debug tag within <pre>...</pre> tags and you'll see it just fine.

Related

Manual Django Form Display

I want to manually render the form in my template, but what I'm trying is not yielding the expected result, and it is not apparent as to why.
class MyForm(forms.Form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['field1'] = forms.BooleanField()
self.fields['field2'] = forms.BooleanField()
systems = System.objects.all()
for i in range(len(systems)):
self.fields['s' + str(i)] = forms.BooleanField()
self.fields['field3'] = forms.BooleanField()
self.initial_fields = [self.fields['field1'], self.fields['field2']]
now when I do this in my template:
{% for field in form.visible_fields %}
{{ field }}
{% endfor %}
it returns what you would expect...after looking up the method visible_fields it simply returns a list of fields. So in theory, if I create my own list of fields as with self.initial_fields, the form generator should render the following same as above:
{% for field in form.initial_fields %}
{{ field }}
{% endfor %}
but instead I get this output in my template:
<django.forms.fields.BooleanField object at 0x000001242F51E588>
<django.forms.fields.BooleanField object at 0x000001242F51E400>
I'm assuming I'm missing some initialization of the field itself? I don't understand why one works and the other doesn't. Anyone know?
You need to get the bound field object and not the field itself. It's not really a clean way of doing so, but if you are looking to hack around it, you should do like so,
...
self.initial_fields = [self.fields['field1'].get_bound_field(self, 'field1'),
self.fields['field2'].get_bound_field(self, 'field2')]
...
Hope this helps!

Manually render Django form fields with variable label

I am creating forms with dynamic fields (and field names) using the below code
class BuyItemForm (forms.Form):
def __init__(self, inventory_list, *args, **kwargs):
super(BuyItemForm, self).__init__(*args, **kwargs)
for item in inventory_list:
self.fields["count%s"%item.item_name] = forms.IntegerField()
self.fields["price%s"%item.item_name] = forms.FloatField()
So I get a form that has field names like "counteggs", "priceeggs", "countmilk", etc... when these items are in the inventory_list
I now want to render the fields manually in my template. I am able to iterate through the set of fields, for example
{% for field in form %}
{{ field }}
{% endfor %}
But I am unable to pick out each field individually by using the field name in a string. I have tried
{{ form.fields['counteggs'] }}
but this doesnt work. Any ideas how I can make this work?
Did you try {{ form.fields.counteggs }} ? In the templates, dictionaries are accessed with dot notation.
So, I found a rather convoluted way of doing this by creating a filter in views.py that receives the form and a key as parameters. It iterates through the form till a field that has a label matching the key is found.
#register.filter
def get_countitem(myform, key):
for field in myform:
if field.label.lower() == key.lower():
return field
return "none"
and in the template
{{ form|get_countitem:"counteggs" }}
It works. I can get my template to render form fields by passing the field label in a string but doesnt seem like a particularly elegant solution to me. Any other ideas are most welcome.

django modelform property hidden field

I'm manually displaying my formset as a table, with each form being looped over. At the bottom of each form I include the hidden fields like:
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
But the problem is that I am also including properties in my form like:
class AllocationForm(forms.ModelForm):
name = forms.CharField(widget=forms.TextInput(attrs={'size': '15'}))
def __init__(self, *args, **kwargs):
super(AllocationForm, self).__init__(*args, **kwargs)
if self.instance:
self.fields['total_budgeted'] = self.instance.total_budgeted()
self.fields['total_budgeted_account_percent'] = self.instance.total_budgeted_account_percent()
self.fields['actual_spent'] = self.instance.actual_spent()
self.fields['actual_spent_account_percent'] = self.instance.actual_spent_account_percent()
self.fields['total_budgeted_category_percent'] = self.instance.total_budgeted_category_percent()
self.fields['actual_spent_category_percent'] = self.instance.actual_spent_category_percent()
class Meta:
model = Allocation
exclude = {'created', 'modified', 'source_account'}
And this works in the sense that I definitely see the properties being called, however they display as nothing so that's another issue.
The problem is when I keep the hidden fields in the template I will get errors such as 'int' object has no attribute 'get_bound_field' and so on depending on the return type of the property/method call.
My question is first: is there a check I can do to see if the field is a property in the template and therefore skip over it?
It may have something to do with how I'm using the property since in fact every property is displaying nothing (but I see it callback), so second would be about how to display the properties.
Well I am in the next step of the problem, but i have success in generating true form fields. In place of:
if self.instance:
self.fields['total_budgeted'] = self.instance.total_budgeted()
You can write:
if self.instance:
self.fields['total_budgeted'] = form.CharField(
initial=self.instance.total_budgeted(),
widget=HiddenInput()
)
In this code you I instantiate the form Field as CharField, you can use the FormField you want, and I hide it by choosing the Hidden input widget.

Django user injection in model properties

I have this models in Django:
News
Comments
Reactions
Relations are:
a News has various Comments
a Comment has various Reactions
The problem is the user (in request / session): the user may subscribe to a reaction, or a comment; he may be logged in or not. (it's a foo example, it doesn't have much sense)
I can't do in template:
{% for reaction in this_news.comments.reactions %}
{{ reaction.name }}
{% if reaction.user_subscribed %} #reaction.user_subscribed(request.user)...
You have subscribed this reaction!
{% endif %}
{% endfor %}
Problems are:
I can't call the method in the template with a parameter (see the comment above)
Models don't have access to request
Now i'm calling an init_user method in News Model, passing the request. Then i have the same method in Comment and Reaction model, and i have to set the user_subscribed property cycling the children of each model.
Isn't there a smarter way to do this?
EDIT: thanks to the Ignacio's hint about using custom tag i'm trying to do a generic mode to pass the user (avoiding the use of closures because i don't know how to use them atm):
def inject_user(parser, token):
try:
# split_contents() knows not to split quoted strings.
tag_name, method_injected, user = token.split_contents()
except ValueError:
raise template.TemplateSyntaxError("%r tag requires exactly three arguments" % token.contents.split()[0])
return InjectUserNode(method_injected, user)
class InjectUserNode(template.Node):
def __init__(self, method_injected, user):
self.method_injected = template.Variable(method_injected)
self.user = template.Variable(user)
def render(self, context):
try:
method_injected = self.method_injected.resolve(context)
user = self.user.resolve(context)
return method_injected(user)
except template.VariableDoesNotExist:
return ''
When i use it {% inject_user object.method_that_receives_a_user request.user %} i come to this error 'str' object is not callable in method_injected(user); how i can fix that?
Write custom template tags that take the user and set a context variable to indicate presence or absence of the criterion.
I've resolved it in a less elegant way, but it worked for me.
I've created a sort of singleton in my User defined class, with a property that i set in every view i need it.
The property is User.current.
Then, inside the models, where i need that i get the current user looking in User.current.

Django: How to check if there are field errors from within custom widget definition?

I'd like to create widgets that add specific classes to element markup when the associated field has errors.
I'm having a hard time finding information on how to check whether a field has errors associated with it, from within widget definition code.
At the moment I have the following stub widget code (the final widget will use more complex markup).
from django import forms
from django.utils.safestring import mark_safe
class CustomTextWidget(forms.Widget):
def render(self, name, value, attrs):
field_has_errors=False # change to dynamically reflect field errors, somehow
if field_has_errors:
error_class_string="error"
else:
error_class_string=""
return mark_safe(
"<input type=\"text\" class=\"%s\" value=\"%s\" id=\"id_%s\" name=\"%s\">" % (error_class_string, value, name, name)
)
Can anyone shed light on a sensible way to populate the field_has_errors Boolean here? (or perhaps suggest a better way to accomplish what I'm trying to do). Thanks in advance.
As Jason says, the widget has no access to the field itself. I think a better solution though is to use the cascading nature of CSS.
{% for field in form %}
<div class="field{% if field.errors %} field_error{% endif %}">
{{ field }}
</div>
{% endfor %}
Now in your CSS you can do:
div.field_error input { color: red }
or whatever you need.
The widget has no knowledge of the field to which it is being applied. It is the field that maintains information about errors. You can check for error_messages in the init method of your form, and inject an error class to your widget accordingly:
class YourForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(YourForm, self).__init__(*args, **kwargs)
attrs = {}
if self.fields['your_field'].error_messages is not None:
attrs['class'] = 'errors'
self.fields['your_field'].widget = YourWidget(attrs=attrs)