Django get field rendering as string - django

How can I get a field rendering from html after form init?
I am trying to add some custom html to the form like this:
class BaseForm(Form):
render_report = forms.BooleanField(required=False, initial=False)
def __init__(self, *args, **kwargs):
super(BaseForm, self).__init__(*args, **kwargs)
html_rendering = str(self.fields['render_report'])
The conflicting part is str(self.fields['render_report']). How can I obtain the html rendering of render_report?. Of course, str() doesn't work.

After some headbanging, here is the catch:
The rendered fields are accesed via the form's _ _ getitem _ _().
So I had to change self.fields['render_report'] for self['render_report']

Related

Why does adding more fields to this widget lead to a keyError?

I'm working on a form in my Django project. I wanted to add a bootstrap class to my input fields.
I tried to do this with the following code:
class CategoryForm(ModelForm):
class Meta:
model = Category
fields = '__all__'
labels = {
"sub_category":"Sub category (if any):"
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['category_name','sub_category','category_info','category_video'].widget.attrs.update({'class':'form-control'})
But when I load the page I get this:
KeyError at /academy/category_form
('category_name', 'sub_category', 'category_info', 'category_video')
Is this not possible? Do I have to add a new line for every field in my form? So like this:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['category_name'].widget.attrs.update({'class':'form-control'})
self.fields['sub_category'].widget.attrs.update({'class':'form-control'})
self.fields['category_info'].widget.attrs.update({'class':'form-control'})
....
This would make for some long lines of code if I have to do this for every form which does not make sense of follow DRY.
Within the form fields is a dictionary containing the form fields. It is not doing what you expect it to do ...your list of keys is interpreted as a tuple and that tuple is not in the form fields contained, that results in the mentioned KeyError.
Now to your attempt to not repeat yourself ... you can use a loop to avoid this:
for key in ('category_name', 'sub_category', 'category_info', 'category_video'):
self.fields[key].widget.attrs.update({'class':'form-control'})

how can I prevent users form extending/ minimizing TextField Django

How can I prevent this from happening
A Django TextField is rendered as a HTML textarea.
Looking at this question, you could use style="resize: none;".
If you would like to add that in your views/form (and not in the templates), you could try something like:
class MyModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['my_field_name'].widget.attrs['style'] = 'resize: none;'
or if you have a form instance
form.fields['my_field_name'].widget.attrs['style'] = 'resize: none;'

formset and input text for each foreign key

In my django formset, I am trying to display a foreign key field with an input instead of a select:
class MinAttend(models.Model):
act = models.ForeignKey(Act)
country = models.ForeignKey(Country)
verbatim = models.ForeignKey(Verbatim)
def __unicode__(self):
return u"%s" % self.verbatim
class MinAttendForm(forms.ModelForm):
country=forms.ModelChoiceField(queryset=Country.objects.all(), empty_label="Select a country")
status=forms.ModelChoiceField(queryset=Status.objects.values_list('status', flat = True).distinct(), empty_label="Select a status")
verbatim=forms.CharField(max_length=300)
class Meta:
model=MinAttend
#fields used for the validation and order
fields = ('country', 'status', 'verbatim')
For the verbatim field, I do have an input box instead of a select but when I want to update a formset, I have the verbatim id instead of its corresponding text:
Here is how I initialize the form:
class MinAttendUpdate(UpdateView):
object=None
model = MinAttend
form_class=MinAttendForm
nb_extra_forms=3
def post(self, request, *args, **kwargs):
attendances=MinAttend.objects.filter(...)
#set the number of forms to the number of ministers + 3 extra form to fill if needed
MinAttendFormSet = modelformset_factory(self.model, form=self.form_class, extra=len(attendances), max_num=len(attendances)+self.nb_extra_forms, can_delete=True)
formset=MinAttendFormSet(queryset=attendances)
I have tried two things:
Instead of the last line I have the following code:
initials=[]
#display text of verbatim instead of id
for index in range(len(attendances)):
initials.append({"verbatim": attendances[index].verbatim.verbatim})
print "initials", initials
formset=MinAttendFormSet(queryset=attendances, initial=initials)
I have overridden the init method of the form:
#~ #verbatim text instead of id for the verbatim field
def __init__(self, *args, **kwargs):
super(MinAttendForm, self).__init__(*args, **kwargs)
instance = kwargs.get("instance", None)
if instance!=None:
print "instance", instance.verbatim.verbatim
self.fields["verbatim"].initial = instance.verbatim.verbatim
None of these methods works, I still get numbers instead of text! What is curious is that I do see text for the verbatim field but only for the three extra forms. Normal?
EDIT - from Bernd Jerzyna comment
In my form:
from django.forms.models import BaseModelFormSet
class MinAttendFormSet(BaseModelFormSet):
def __init__(self, *args, **kwargs):
super(MinAttendFormSet, self).__init__(*args, **kwargs)
for form in self.forms:
#skip extra forms
if not form.empty_permitted:
form.fields['verbatim'].initial= form.instance.verbatim
print "verbatim MinAttendFormSet", form.instance.verbatim
In my view:
from forms import MinAttendForm, MinAttendFormSet
my_formset = modelformset_factory(self.model, form=self.form_class, formset=MinAttendFormSet)
formset = my_formset(request.POST, queryset=attendances)
When I do a print of the text of each verbatim, I see the correct text displayed. However, I still see numbers (primary key ids) in the form of my web page ;(.
What's wrong?
I think something like this should work:
Implement the init method of your formset like
def __init__(self, *args, **kwargs):
...
super(MinAttendFormSet, self).__init__(*args, **kwargs)
for form in self.forms:
form.fields['verbatim'].initial= form.instance.verbatim
That would also require your Verbatim model to have something like:
def __unicode__(self):
return u'%s' % (self.name)
This is my understanding of it...
As a select widget, the verbatim is displayed with the __unicode__ results, but the value 'behind the scenes' is the PK/verbatim-id, ie the HTML <option> tag has a value and 'label'. When you change it to a text input widget, it is expecting you to enter the PK, hence why you are seeing the numbers.
For the solution, I am not sure. You could write some code to accept the verbatim-text, rather than the pk/verbatim-id. The problem with this though, is that if the text is not written exactly, django won't find a match. Also, if more than 1 verbatim has the same text, django wouldn't know which one to use (unless you have set unique=true on the model field for text). You could also set the verbatim-text as the PK.
Perhaps using something like Django-Select2 will give you the desired UI?

How can I get a dynamically assigned widget to display HTML in Django?

I'm trying to get my custom widget to display without the HTML being escaped by Django. Here's my widget:
class MyInput(Widget):
def __init__(self, obj, attrs=None):
super(MyInput, self).__init__(attrs)
def render(self, name, value, attrs=None):
return mark_safe(u'<img src="{url}">').format(url=self.url)
It gets instantiated via a form factory:
def MyFormFactory():
class MyForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
desired_fields = ['field1', 'field2',]
for f in desired_fields:
self.fields[f].widget = MyInput(self.instance)
return MyForm
This gets called in my Django Admin class:
class MyAdminClass(admin.ModelAdmin):
form = MyFormFactory()
Everything 'works' except for the fact that my widget has its HTML escaped. This does not happen if I use the widget via direct form instantiation (using a regular form class and form field widget assignment), but I need to be able to set it up via the factory like this. How can I force Django to allow the HTML? allow_tags doesn't seem to apply in this case, and I've already used mark_safe. What am I still missing?
Try to change
return mark_safe(u'<img src="{url}">').format(url=self.url)
to
return mark_safe(u'<img src="{url}">'.format(url=self.url))
The first line returns a string, the latter returns a SafeBytes instance, and Django treats them differently.

Django send key or value from the view to the form class

I am writing an Edit form, where some fields already contain data. Example:
class EditForm(forms.Form):
name = forms.CharField(label='Name',
widget=forms.TextInput(),
initial=Client.objects.get(pk=??????)) #how to get the id?
What I did for another form was the following (which does not work for the case of the previous EditForm):
class AddressForm(forms.Form):
address = forms.CharField(...)
def set_id(self, c_id):
self.c_id = c_id
def clean_address(self):
# i am able to use self.c_id here
views.py
form = AddressForm()
form.set_id(request.user.get_profile().id) # which works in the case of AddressForm
So what is the best way to pass an id or a value to the form, and that could be used in all forms for that session/user?
Second: is it right to use initial to fill in the form field the way I am trying to do it?
You need to override the __init__ method for your form, like so:
def __init__(self, *args, **kwargs):
try:
profile = kwargs.pop('profile')
except KeyError:
super(SelectForm, self).__init__(*args, **kwargs)
return
super(SelectForm, self).__init__(*args, **kwargs)
self.fields['people'].queryset = profile.people().order_by('name')
and, obviously, build your form passing the right parameter when needed :)