Django: List item.id as field attr? - django

In my template I have :
{% for link in solution_list %}
<form id="marked_solved_form{{link.id}}" action="" method="post">
{{mark_solved_form.is_solution}}
{% csrf_token %}
<label for="id_is_solution{{link.id}}">test form</label>
<input type="hidden" name="next" value="{{ request.path }}"/>
</form>
{% endfor %}
is_solution is just a booleanfield for a checkbox that user's can set.
The problem I am facing is that all the forms populate correctly with id's like id="marked_solved_form3" except for the {{mark_solved_form.is_solution}}. I thought I could just go to the modelform and edit the attributes:
class PostMarkedSolved(ModelForm):
class Meta:
model = MarkedSolved
def __init__(self, *args, **kwargs):
super(PostMarkedSolved, self).__init__(*args, **kwargs)
#self.fields['is_solution'].queryset = Factory.objects.all()
self.fields['is_solution'].widget.attrs={'onchange':'this.form.submit();', 'class':'testing{{link.id}}', 'id':'testing{{link.id}}'}
So far so good. Except when the form loads, I get the following for every checkbox in every list form:
<input name="is_solution" type="checkbox" class="testing{{link.id}}" onchange="this.form.submit();" id="testing{{link.id}}">
It's as though django is ignoring the modelform is escaping template tag {{link.id}} all together. Ideally it would return for each form instance something like:
<td>
<form id="marked_solved_form3" action="" method="post">
<input name="is_solution" type="checkbox" class="testing3" onclick="this.form.submit();" id="testing3" />
<div style='display:none'><input type='hidden' name='csrfmiddlewaretoken' value='bwuXYhSpQA8yEOEPSaCt3wAANC0kR7CE' /></div>
<label for="id_is_solution3" onclick="this.form.submit();">test form</label>
<input type="hidden" name="next" value="/task/1"/>
</form>
</td>
<td>
<form id="marked_solved_form4" action="" method="post">
<input name="is_solution" type="checkbox" class="testing4" onclick="this.form.submit();" id="testing4" />
<div style='display:none'><input type='hidden' name='csrfmiddlewaretoken' value='bwuXYhSpQA8yEOEPSaCt3wAANC0kR7CE' /></div>
<label for="id_is_solution4" onclick="this.form.submit();">test form</label>
<input type="hidden" name="next" value="/task/1"/>
</form>
</td>
I even went as far as to install django-widget-tweaks (Which is awesome, btw!), and tried doing:
{{mark_solved_form.is_solution|attr:"id:id_is_solution{{link.id}}"}}
However that choked because it cannot handle {{link.id}} within another template tag. :( Any suggestions would be appreciated. Thanks in advance!

The template isn't able to render those variables placed in the __init__ method. You should be able to access the model instance through self.instance and add the correct id's in the __init__ method by doing doing the following:
def __init__(self, *args, **kwargs):
super(PostMarkedSolved, self).__init__(*args, **kwargs)
self.fields['is_solution'].widget.attrs={
'onchange':'this.form.submit();',
'class':'testing{0}', 'id':'testing{1}'.format(self.instance.id, self.instance.id)
}
Hidden in Django's docs:
Also, a model form instance bound to a model object will contain a self.instance attribute that gives model form methods access to that specific model instance.

This is the wrong approach. You should use separate forms with prefixes, or even better use a formset.

Related

How To - render a string as a template in django

i've been wondering about how i could render a template just by passing a made-inside-view string so that i woudn't have the need to create an html file. I read the Django Docs, but i couldn't find an explanation on this, as far as seen all was about giving the template path string, (also i tried a few lines, but got nothing).
Please, have this as an example:
from django.shortcuts import render
def my_view(request):
arbitrary_string_as_template = """
<form action="" method="POST">
{% csrf_token %}
<label for="">Username</label>
<input type="text" name="username">
<label for="">Password</label>
<input type="password" name="password">
<button type="submit">
submit
</button>
</form>
"""
return render(request, arbitrary_string_as_template, {})
So if this can be done... then i think it would have in its way a good potential, since it gains in terms of versatility..and btw..
#I'm New to Django
Thanks for your attention
You can construct a Template object [Django-doc] with the template string, and then .render(…) [Django-doc] it:
from django.http import HttpResponse
from django.template import Template, RequestContext
def my_view(request):
arbitrary_string_as_template = """
<form action="" method="POST">
{% csrf_token %}
<label for="">Username</label>
<input type="text" name="username">
<label for="">Password</label>
<input type="password" name="password">
<button type="submit">
submit
</button>
</form>
"""
template = Template(arbitrary_string_as_template)
context = RequestContext(request)
return HttpResponse(template.render(context))
If however the content is static, I would advise to use a file. It makes it easier to separate concerns, and write clean files that each focus on a specific part.

How to change form layouts in Django 1.8

I have a form
Field Name: [Input Box]
I want
Field Name:
[Input Box]
How can I achieve this?
forms.py
class SearchForm(forms.Form):
search = forms.CharField()
views.py
form = SearchForm()
html_dtc = {'form':form}
return render_to_response('site/home.html', html_dtc)
home.html
<form method='POST' action=''> {% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-success btn-sm">Update</button>
</form>
Thank you :)
You want a custom form rendering. You can read more about it here. For example, the following code would achieve what you're after.
<form method='POST' action=''> {% csrf_token %}
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }} <br/>
{{ field }}
</div>
{% endfor %}
<button type="submit" class="btn btn-success btn-sm">Update</button>
</form>
(field.errors are added, because when you are manually rendering the form, you have to take care of error rendering as well)
Try to overide form.as_p()
class SearchForm(forms.Form):
search = forms.CharField()
def as_p(self):
"Returns this form rendered as HTML <p>s."
return self._html_output(
normal_row='<p%(html_class_attr)s>%(label)s <br> %(field)s%(help_text)s</p>',
error_row='%s',
row_ender='</p>',
help_text_html=' <span class="helptext">%s</span>',
errors_on_separate_row=True)
If this is a one off thing you can render your form manually like described here in the documentation.
Otherwise there's the django-floppyforms which gives you great control over how forms and (default) widgets are rendered.
Just define a custom layout, make it the default, use floppyforms custom Form classes (they behave exactly the same) and you're good to go.
As far as I remember some of floppyforms's functionality will also be included in Django 1.9, so look out for that, too.
Use django_crispy_forms: http://django-crispy-forms.readthedocs.org/en/latest/
In the template include {% load crispy_forms_tags %} and for the form:
{% crispy form %}
In addition, you can change the layout of the form easily, by overriding the form_class in the init function of the form:
class ContactForm(forms.Form):
def __init__(self, *args, **kwargs):
super(ContactForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.form_class = 'form-horizontal' # <-- leave out for vertical, or form-inline
self.helper.render_hidden_fields = True
self.helper.layout = Layout(
Div(InlineRadios('applying_for_whom'), css_class="col-xs-12"),
Div(InlineRadios('gender'), css_class='col-xs-12'),
Div('agreed_to_data_transmit', css_class="col-xs-12"),
As a bonus, if you are using bootstrap, set also set CRISPY_TEMPLATE_PACK = 'bootstrap3' so then everything is taken care of automatically for vertical bootstrap.
you can do
<form method='POST' action=''>
{% csrf_token %}
<label>Field Name:</label>
{{ form.search }}
<button type="submit" class="btn btn-success btn-sm">Update</button>
</form>
generally I don't recommend use the HTML code generated by Django, but instead I supply what is needed by the DJANGO form.
but some are required: like the ERRORs, like the CSRF token.
let me add some examples to clarify what I am talking
<form class="*" style="*">
<label /><input name="email" />
<label /><input name="password" />
<form>
basically what I am suggesting is, do not use template tags unless absolute necessary like CSRF.
in this way, you can completely separate the design from the backend logic. you can have front end work indecently on the UI. the interface is the form fields, you have to supply all fields to the backend. like in this case 'email' && 'password' is required at backend

Function with arguments in a template. Django

In my template, I display a list of users a user follows. I would like the user to be able to delete one of the users he follows thanks to a button.
I have a function remove_relationship that deletes a relationship.
Here is the function in my models.py:
class UserProfile(models.Model):
(...)
def remove_relationship(self, person):
Relationship.objects.filter(
from_person=self,
to_person=person).delete()
return
I would like to pass this function into my template:
{% for user in following % }
<form method="post">
{% csrf_token %}
<input type="submit" value="delete" onclick="remove_relationship"/>
</form>
{%endfor%}
The thing is that I can't pass argument in my template. So how can I do so that each button deletes the relationship with the right user?
I saw an other question on this topic, abut it looks like it doesn't solve my problem (http://stackoverflow.com/questions/1333189/django-template-system-calling-a-function-inside-a-model)
Thank you for your help.
It looks as though you are confusing client-side code (JavaScript) with server-side (Django).
To get the relevant user ID submitted you could add an additional hidden field to the form:
{% for user in following % }
<form method="post" action="{% url views.remove_relationship %}">
{% csrf_token %}
<input type="hidden" name="user_id" value="{{ user.id }}">
<input type="submit" value="delete" />
</form>
{%endfor%}
Then create a remove_relationship view that does the deletion on the server side, based on the user id you'll now find in request.POST['user_id']

Django identifying non field validation error is associated with which form

Is there a way to identifying the validation error is associated with which form for a template contaning multiple forms?
Let me explain the scenario. I have login window popup script within my base.html
<div id="dialog1" class="window">
<form method="post" action="/accounts/login/?next=/IW/home" id='login-form' name=login-form>{% csrf_token %}
<div class="d-header">
{{ form.non_field_errors }}
<input type="text" name="username" id="id_username" value="" onclick="this.value=''"/><br/>
<input type="password" name="password" id="id_password" value="" onclick="this.value=''"/><br/>
<input type="hidden" name="login_form" value="1" />
<input type="submit" value="login" />
</div>
{% endif %}
</div>
</form>
</div>
<div id="mask"></div>
{% if form.non_field_errors %}
<script>
var maskHeight = $(document).height();
var maskWidth = $(window).width();
//Set heigth and width to mask to fill up the whole screen
$('#mask').css({'width':maskWidth,'height':maskHeight});
$('#mask').show();$('.window').show();
</script>
{% endif %}
As all other templates extends base,html whenever there is an error non_field error then login window pops up . I would like to show the login error only when login form is submit not on submit of someother form with a non field error.
For this I need to identify the name of the form.something like this {% ifequal form.form_name login_form %} - Display login error .Is this possible??
They isn't anything special about the name 'form' in the template. It's just a default context name, you can choose to name your forms anything you like.
Just name the forms differently in your context:
from django.http import Http404
def detail(request, poll_id):
# NOTE: This view code isn't really form processing code, just using it to show
# how you can pass different names into your template context.
login_form = MyLoginForm()
detail_form = MyDetailForm()
try:
p = Poll.objects.get(pk=poll_id)
except Poll.DoesNotExist:
raise Http404
return render_to_response('polls/detail.html', {'poll': p, 'login_form': login_form, 'detail_form': detail_form})
And then:
<div id="dialog1" class="window">
<form method="post" action="/accounts/login/?next=/IW/home" id='login-form' name=login-form>
{% csrf_token %}
<div class="d-header">
{{ login_form.non_field_errors }}
.
.
.
Also, if you want to do multiple instances of the same form type, have a look at formsets

Customizing django-comments

So, I'm using django.contrib.comments. I've installed it OK but rather than the unwieldy default comment form, I'd like to use a custom form template that just shows a textarea and submit button.
The rationale behind this is that user only see the form if they area already authenticated, and I'd like the keep the form simple and pick up their username etc automatically.
I've implemented a custom form, but am getting an error when I try to submit it.
Here's what I have in my template for the page with the comment form (entry is the object passed from the view):
{% load comments %}
{% render_comment_form for entry %}
And here's my HTML in /templates/comments/form.html:
{% if user.is_authenticated %}
<p>Submit a comment:</p>
<form action="/comments/post/" method="post">
<textarea name="comment" id="id_comment" rows="2" style="width: 90%;"></textarea>
<input type="hidden" name="options" value="{{ options }}" />
<input type="hidden" name="target" value="{{ target }}" />
<input type="hidden" name="gonzo" value="{{ hash }}" />
<input type="hidden" name="next" value="{{ entry.get_absolute_url }}" />
<span style="float:right;"><input type="submit" name="post" value="Add"></span>
</form>
{% else %}
<p>Please log in to post a comment.</p>
{% endif %}
It renders okay initially, but when I try to submit the comment form, I get the following Django error:
Comment post not allowed (400)
Why: Missing content_type or object_pk field.
Can anyone help?
The comment model uses a generic foreign key to map to the object for which the comment was made such as a blog entry. These are required hidden fields included in the standard comment form.
From django.contrib.comments.models
...
class CommentSecurityForm(forms.Form):
"""
Handles the security aspects (anti-spoofing) for comment forms.
"""
content_type = forms.CharField(widget=forms.HiddenInput)
object_pk = forms.CharField(widget=forms.HiddenInput)
...
If you haven't changed the form class and only want to change the html template then you can include these fields by adding a for loop over all the hidden fields.
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
Fixed the problem by copying from Theju's app - in particular, see Joshua Works' comment on part 2.