I have a from containing some fields, but my css class applies to all the fileds except the EmailField. I've also tried sender.widget.attrs.update({'class':"contatct-form"}) and it still doesn't work (just change the size of field). Does anybody knows what the problem is? as all of my searches were unsuccessful.
form:
from django import forms
class NameForm(forms.Form):
your_name = forms.CharField(initial='Your name', max_length=100)
sender = forms.EmailField()
#sender.widget.attrs.update({'class':"contatct-form"})
message = forms.CharField(widget=forms.Textarea)
template:
<div class="contatct-form">
<form action="" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="send" />
</form>
</div>
The problem you have probably is because you have not assigned any widget to your EmailField(), change to this (like #Todor said) should work:
...
sender = forms.EmailField(
widget=forms.EmailInput(attrs={'class': 'contatct-form'})
)
If this doesn't work for whatever reason (probably wrong css styling), you can just change the styling in your css/class directly like so:
div.contatct-form form input[type=email] {
/* your changes here... */
}
Hope this helps.
We have to do that in the init method for one field or all fields with for loop.
class NameForm(forms.Form):
def __init__(*args, **kwargs):
super(NameForm, self).__init__(*args, **kwargs)
self.fields['sender'].widget.attrs.update({'class':"contatct-form"})
Related
So I'm uploading an image and a document. The problem is two fold, first in the rendered form it is showing the label but not the initial value or even a picture so its hard for anyone to realize that they have to upload anything. Secondly, upon uploading its not showing the document/image that has been uploaded so you end up thinking it didn't work. I've attached a picture below showing the issue. How do I fix this?
picture showing the problem in the form
The above picture clearly shows the problem at hand.
As of 31/08/17 I've added Meta and tried using the ClearableFileInput widget but still not working. See below for the updated code:
from forms.py
profile_image = forms.ImageField(widget=forms.ClearableFileInput(), required=True, label="Profile Picture*")
cvv = forms.FileField(widget=forms.ClearableFileInput(), initial='upload a CV', required=True, label="CV*")
class Meta:
model = ProfileTeacher
fields = ('description', 'document' 'name','last_name','phone_number', 'date_of_birth', 'bank_account', 'id_number', 'postal_code', 'adress', 'profile_image', 'nationality' )
widgets = {
'profile_image' : forms.FileInput,
}
def __init__(self, *args, **kwargs):
super(RegisterFormTeacher, self).__init__(*args, **kwargs)
self.fields['name'].widget.attrs['placeholder'] = 'John'
self.fields['last_name'].widget.attrs['placeholder'] = 'Doe'
self.fields['adress'].widget.attrs['placeholder'] = '4 Privet Drive, Little Whinging'
from models.py
models.ImageField(upload_to=teacher_directory_path,
null = True,
blank=True,
default='/perfil.png')
cvv = models.FileField(upload_to=teacher_directory_path, null=True, blank = True, verbose_name="CV")
from the html that renders the form:
<div class="card-content">
<form id="teacher-register-form" action="{% url 'teacher-register' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form|bootstrap }}
<span></span>
<br/>
<br/><!-- an attempt to bridge the gap between the buttons. -->
<span>{% trans "If you have an account" %}</span>
<br />
{% trans "Init session" %}
<button type="submit" class="btn btn-primary pull-right">{% trans "Register" %}</button>
</form>
I must add, in the models when in admin everything is showing fine but when rendering then I dont see anyvalue.
I have the following radio button html using django widget tweaks (i do not have to use this library and open to using whatever method works):
{% for choice in seeking_form2.disaster_recovery %}
<div class="radio radio-primary radio-inline">
{{ choice.tag|attr:"required" }}
<label for='{{ seeking_form2.disaster_recovery.auto_id }}_{{ forloop.counter0 }}'>{{ choice.choice_label }}</label>
</div>
{% endfor %}
Which should create radios that look like this:
<input id="id_disaster_recovery" name="disaster_recovery" type="radio" value="2" required>
My model looks like:
BOOL_CHOICES = ((True, 'Yes'), (False, 'No'))
disaster_recovery = models.BooleanField(choices=BOOL_CHOICES, default=False, )
My form looks like:
class MyForm(forms.ModelForm):
class Meta:
model = MyModel
widgets = {
'disaster_recovery': forms.RadioSelect(),
}
I get the error:
'SafeText' object has no attribute 'as_widget'
If i understand your problem correctly, you should be overriding this in the form, rather than at the template, or the models.
class MyModelForm(..):
disaster_recovery = forms.ChoiceField(choices=BOOL_CHOICES, widget=forms.RadioSelect(), required=True) #Default is dropdown
Now, you could simplify your HTML to render the radiobuttons.
Some more context on this can be found here
To add the HTML required attribute, to your existing form, you can do:
class MyForm(forms.ModelForm):
class Meta:
model = MyModel
widgets = {
'disaster_recovery': forms.RadioSelect(attrs={"required": "required"}),
}
Here is the relevant documentation on the custom attributes
I have an UpdateView which contains a form and an InlineFormetSet that is related to the form model (I simplified the code below):
#models.py
class Note(Model):
content = models.TextField()
class Dialog(Model):
message = models.TextField()
note = modes.ForeignKey(Note)
#views.py
class NoteUpdateView(UpdateView):
model = Note
form_class = NoteForm
def get_context_data(self, **kwargs):
context = super(NoteUpdateView ,self).get_context_data(**kwargs)
self.object = self.get_object()
dialogFormset = inlineformset_factory(Note,
Dialog,
fields='__all__',
extra=0)
dialog = dialogFormset(instance=self.object)
context['dialog'] = dialog
return context
def post(self, request, *args, **kwargs):
form = self.get_form(self.get_form_class())
dialog_form = DialogFormset(self.request.POST, instance=Note.objects.get(id=self.kwargs['pk']))
if (form.is_valid() and dialog_form.is_valid()):
return self.form_valid(form, result_form, dialog_form)
else:
return self.form_invalid(form, result_form, dialog_form)
def form_valid(self, form, result_form, dialog_form):
self.object, created = Note.objects.update_or_create(pk=self.kwargs['pk'], defaults=form.cleaned_data)
dialog_form.save()
return HttpResponseRedirect(self.get_success_url())
def form_invalid(self, form, result_form, dialog_form):
return self.render_to_response(self.get_context_data(form=form,
result_form=result_form,
dialog_form=dialog_form))
The purpose of NoteUpdateView is to render existing Note and Dialog when a GET request is made tonote/11. A user may delete an existing Dialog, which is not handled by the code above.
To handle delete, I can do the following on POST:
1) fetch all of the dialog records related to the requested Note:
dialogs = Note.objects.filter(pk=self.kwargs['pk'])
2) loop through self.request.POST and see if the formsets contained in the submitted data also exist in the dialogs created above.
3) If a record is dialogs but not submitted via POST, then that dialog is considered to be removed by the user. Thus, preform delete operation.
I am sure I can implement these steps. But since I am not very familiar with Django's formset. I wonder if there is any built-in classes or methods that I should use to automate these steps. What is the Django way of doing what I just described?
Ok, I figured out what the problem was. The problem that I run into is due to the use of django-crispy-forms. Let me explain what happened:
When Django renders InlineFormSet, it's can_delete attribute is set to True automatically. When this attribute is set to True, a hidden input field is inserted into the rendered HTML:
<input type="hidden" name="dialog_set-0-DELETE" id="id_dialog_set-0-DELETE">
I used django-crispy-forms to render my forms so that they are styled with bootstrap3. When rendering inlineformset using crispy-forms, a FormHelper needs to be defined.
This is because when you have multiple inlineformset forms on the page, you will only want one <form> tag surrounds them instead of giving each inlineformset form it's own <form> tag. To do that, I had to define the FormHelper like this:
#models.py
class Dialog(Model):
info1 = models.TextField()
info2 = models.TextField()
#forms.py
class DialogFormSetHelper(FormHelper):
def __init__(self, *args, **kwargs):
super(DialogFormSetHelper, self).__init__(*args, **kwargs)
self.form_tag = False # This line removes the '<form>' tag
self.disable_csrf = True # No need to insert the CSRF string with each inlineform
self.layout = Layout(
Field('info1', rows='3'), # make sure the name of the field matches the names defined in the corresponding model
Field('info2', rows='3')
)
I need django-crispy-forms to set the row number of a textarea tag to be 3. Thus, I had to specifically redefine how my textarea fields look like under Layout. What I didn't realize when using the Layout is that anything that you didn't define in it will not be rendered in the HTML.
From the look of the code, I didn't miss any fields defined in the Dialog model. But, what I didn't realize is that the hidden fields that come with InlineFormSet are not rendered in the HTML unless I specifically define them in the Layout object and in the template. To get formset & inlineformset working properly, you will need the following hidden inputs:
formset.manageform. They look like this in the HTML:
<input id="id_dialog_set-TOTAL_FORMS" name="dialog_set-TOTAL_FORMS" type="hidden" value="1">
<input id="id_dialog_set-INITIAL_FORMS" name="dialog_set-INITIAL_FORMS" type="hidden" value="1">
<input id="id_dialog_set-MIN_NUM_FORMS" name="dialog_set-MIN_NUM_FORMS" type="hidden" value="0">
<input id="id_dialog_set-MAX_NUM_FORMS" name="dialog_set-MAX_NUM_FORMS" type="hidden" value="1000">
The primary key that is associated with each inlineformset form, and a foreign key that the inlineformset refers to. They look like this in HTML:
<input id="id_dialog_set-0-note" name="dialog_set-0-note" type="hidden" value="11"> <!-- This line identifies the foreign key`s id -->
<input id="id_dialog_set-0-id" name="dialog_set-0-id" type="hidden" value="4"> <!-- This line identifies the inlineformset form`s id -->
[A DELETE hidden field when can_delete is set to True] (https://docs.djangoproject.com/en/1.9/topics/forms/formsets/#can-delete). It looks like this in the HTML:
<input type="hidden" name="dialog_set-0-DELETE" id="id_dialog_set-0-DELETE">
In my template, I had the first two covered:
<form method="post" action="{{ action }}" enctype="multipart/form-data" id="note_form">
{% crispy form %}
{# the management_form is covered here #}
{{ dialog.management_form }}
{% for form in dialog %}
<div class="formset-container">
<div class="dialog-title">
{% crispy form dialogHelper %}
</div>
{# the hidden fields are covered here #}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
</div>
{% endfor %}
</form>
What I didn't have is the DELETE hidden input. To add it to the HTML, I had to add it this way in the Layout:
#forms.py
class DialogFormSetHelper(FormHelper):
def __init__(self, *args, **kwargs):
super(DialogFormSetHelper, self).__init__(*args, **kwargs)
self.form_tag = False
self.disable_csrf = True
self.layout = Layout(
Field('info1', rows='3'),
Field('info2', rows='3'),
Field('DELETE') # <- ADD THIS LINE
)
Finally, everything works properly now
The Django way is to check if someone has made a library that handles this for you :-).
So take a look at the exellent django-extra-views and it's InlineFormSetView. I've used it a lot and it works really well. In your case your view becomes something like this:
from extra_views import InlineFormSetView
class NoteUpdateView(InlineFormSetView):
model = Note
inline_model = Dialog
form_class = NoteForm
extra = 0
def get_context_data(self, **kwargs):
context = super(NoteUpdateView ,self).get_context_data(**kwargs)
context['dialog'] = context['formset']
return context
You could skip .get_context_data method as well if you update your template to refer to the formset as "formset" instead.
I am using Django Userena for the first time.So can not able to customize the appearance of the change password form,as we know that userena used the default change password form from django.contrib.auth.forms (if i am not wrong).Now this is becoming tough for me to customize the appearance of the change password form template cause in the change password template, each and every field is rendered as {{ form.as_p }} like that
<form action = "" method="post" role = "form">
<fieldset>
<legend>{% trans "Change Password" %}</legend>
{% csrf_token %}
{{ form.as_p }}
</fieldset>
<input type="submit" value="{% trans "Change password" %}" class="btn btn-success" />
</form>
in mention,i have already been able to format the appearance of other forms provided by userena.for example i have changed the appearance of the Edit Profile form by adding css classes in the forms.py like that
class EditProfileForm(forms.ModelForm):
""" Base form used for fields that are always required """
first_name = forms.CharField(label=_(u'First name'),
max_length=30,
widget=forms.TextInput(attrs={'class' : 'form-control'}),
required=False)
last_name = forms.CharField(label=_(u'Last name'),
max_length=30,
widget=forms.TextInput(attrs={'class' : 'form-control'}),
required=False)
background = forms.CharField(label=(u'Background'),
max_length=500,
widget=forms.Textarea(attrs={'class' : 'form-control'}),
required=True)
and worked, change password form has been rendered from django.contrib.auth.forms,so i don't know how to add css classes in each field of that that file as it is a core file of Django.May be there alternative way to do this ,but i am inexperience in django and also the django userena,i don't know how do this.
It's too late, but for the new visitors,
you can create a new form in your forms.py as
# forms.py
from django.contrib.auth.forms import PasswordChangeForm
...
class MyPasswordChangeForm(PasswordChangeForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["old_password"].widget = forms.PasswordInput(attrs={"class": "form-control"})
self.fields["new_password1"].widget = forms.PasswordInput(attrs={"class": "form-control"})
self.fields["new_password2"].widget = forms.PasswordInput(attrs={"class": "form-control"})
# other customization
and in your views.py you could use PasswordChangeView with your form as
# views.py
...
from django.contrib.auth.views import PasswordChangeView
from .forms import MyPasswordChangeForm
...
class ChangePasswordView(PasswordChangeView):
form_class = MyPasswordChangeForm
template_name = "path/to/your/template.html"
That's all.
You actually need to override the userena view altogether because it passes its own form in the view
urls.py:
# Change password
url(r'^(?P<username>[\#\.\w-]+)/password/$',
accounts.views.my_own_password_change_view,
name='userena_password_change'),
In your views.py:
#secure_required
#permission_required_or_403('change_user', (get_user_model(), 'username', 'username'))
def my_own_password_change_view(request, username, template_name='userena/password_form.html',
pass_form=YourPasswordChangeForm, success_url=None, extra_context=None):
""" Change password of user.
This view is almost a mirror of the view supplied in
:func:`contrib.auth.views.password_change`, with the minor change that in
this view we also use the username to change the password. This was needed
to keep our URLs logical (and REST) across the entire application. And
that in a later stadium administrators can also change the users password
through the web application itself.
:param username:
String supplying the username of the user who's password is about to be
changed.
:param template_name:
String of the name of the template that is used to display the password
change form. Defaults to ``userena/password_form.html``.
:param pass_form:
Form used to change password. Default is the form supplied by Django
itself named ``PasswordChangeForm``.
:param success_url:
Named URL that is passed onto a :func:`reverse` function with
``username`` of the active user. Defaults to the
``userena_password_complete`` URL.
:param extra_context:
Dictionary of extra variables that are passed on to the template. The
``form`` key is always used by the form supplied by ``pass_form``.
**Context**
``form``
Form used to change the password.
"""
user = get_object_or_404(get_user_model(),
username__iexact=username)
form = pass_form(user=user)
if request.method == "POST":
form = pass_form(user=user, data=request.POST)
if form.is_valid():
form.save()
# Send a signal that the password has changed
userena_signals.password_complete.send(sender=None,
user=user)
if success_url: redirect_to = success_url
else: redirect_to = reverse('userena_password_change_complete',
kwargs={'username': user.username})
return redirect(redirect_to)
if not extra_context: extra_context = dict()
extra_context['form'] = form
extra_context['profile'] = get_user_profile(user=user)
return ExtraContextTemplateView.as_view(template_name=template_name,
extra_context=extra_context)(request)
And finally
class YourPasswordChangeForm(forms.ModelForm):
""" Base form used for fields that are always required """
first_name = forms.CharField(label=_(u'First name'),
max_length=30,
widget=forms.TextInput(attrs={'class' : 'form-control'}),
required=False)
last_name = forms.CharField(label=_(u'Last name'),
max_length=30,
widget=forms.TextInput(attrs={'class' : 'form-control'}),
required=False)
background = forms.CharField(label=(u'Background'),
max_length=500,
widget=forms.Textarea(attrs={'class' : 'form-control'}),
required=True)
do even more customization on the html template
<form action="" method="post" id="password_change_form">
{% csrf_token %}
{% for field in form %}
<div class="profile-input w33">
<div class="profile-label" for="{{ field.name }}">{{ field.label }}</div>
{{ field }}
{{ field.errors }}
</div>
{% endfor %}
<div class="profile-input w33">
<input type="submit" class="input updatebtn" value="{% trans "UPDATE" %}"/>
</div>
</form>
If you are going to use Bootstrap and jQuery you can also customize all templates in your userena base file with jQuery.
In my case it saved me a lot of messy code in multiple files across the project.
Just change the desired parts with jQuery or pure JS and CSS for example:
$( "input" ).addClass( "form-control" );
Crispy forms currently shows my password field as clear text. I have tried the following but I still cannot get it to use type=password.
Fieldset(
'Enter a password.',
PrependedText('password', '<i class="fa fa-key"></i>',
placeholder='Password',
autocomplete='off',
widget=forms.PasswordInput,
),
),
I have also tired type="password" with no effect.
I get no error.
I'm 99.9% positive that you missed adding the widget to the form field declaration. You should have something like this in order to display a password field:
class MyForm(forms.Form):
...
password = forms.CharField(widget=forms.PasswordInput)
...
And the layout should be as simple as:
Layout(
PrependedText('password', '#', placeholder="password", autocomplete='off')
)
The widget= keyword argument is defined on the Django's forms.CharField constructor and not on the django-cryspy-forms' bootstrap.PrependedText constructor.
You can find more information on Django forms fields here.
UPDATE
If you have a model form, it's being constructed from your model, either automatically (eg using the admin views), by using modelform_factory or by defining the ModelForm yourself. Here is an example of the 3rd approach.
class MyModelForm(forms.ModelForm):
class Meta:
fields = (... fields you want to include in the form ...)
password = forms.CharField(widget=forms.PasswordInput, ...)
Please note that by providing your model form field override, you'll have to also provide attributes like label, help_text (and such) because they will not be automatically copied from the model field.
I never had good luck with crispy-forms and formsets, so I just use forms directly in templates. See if that works:
<form method="post">{% csrf_token %}
{{ formset.management_form|crispy }}
{% for form in formset %}
<input id="id_form-{{ forloop.counter0 }}-id" name="form-{{ forloop.counter0 }}-id" type="hidden" value="{{ form.instance.pk }}"/>
{{ form|crispy }}
{% endfor %}
</form>
You need to define PasswordInput.
class YourForm(forms.ModelForm):
password = forms.CharField(label='Enter Password',widget=forms.PasswordInput(attrs={'placeholder': 'alphanumeric password'}))