I try to enhance my all auth's signup form with Crispy-form but it's not working :)
I modify the allauth form in the forms.py to hide the labels and add the button
My current problem is that the "self.helper.layout = Layout" in the forms.py makes the fields to disappear in the template, but if I comment the self.helper.layout = Layout, I don't have a button to validate my form x(
I would like to have my fields and a working submit button for my form,
Thanks for your help!
details below
forms.py
from django import forms
from allauth.account.forms import SignupForm
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, ButtonHolder, Submit,Field
from crispy_forms.bootstrap import FormActions
class MySignupForm(SignupForm):
def __init__(self, *args, **kwargs):
super(MySignupForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_show_labels = False
self.helper.layout = Layout(
FormActions(
Submit('submit', 'Sign-up', css_class = 'btn-primary')
),
)
I also define in my settings.py that I was using a custom form as signup
# CUSTOM : Allauth Setup part
AUTHENTICATION_BACKENDS = (
# Needed to login by username in Django admin, regardless of `allauth`
'django.contrib.auth.backends.ModelBackend',
# `allauth` specific authentication methods, such as login by e-mail
'allauth.account.auth_backends.AuthenticationBackend',
)
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_AUTHENTICATED_LOGIN_REDIRECTS = False
SOCIALACCOUNT_QUERY_EMAIL = ACCOUNT_EMAIL_REQUIRED
#Since users don't have account on the landing page, sign up redirect to thank you page
LOGIN_REDIRECT_URL = '/thank-you'
ACCOUNT_FORMS = {'signup': 'public.forms.MySignupForm'}
I overrided the allauth template with this version
{% extends "base.html" %}
{% load socialaccount %}
{% load crispy_forms_tags %}
{% load i18n %}
{% block head_title %}{% trans "Signup" %}{% endblock %}
{% block content %}
<section id="contact">
<div class="row">
<div class="container">
<div class="col-lg-12 text-center">
<h1 class="section-heading">{% trans "Sign Up" %}</h1>
</div>
<form class="signup" id="signup_form" method="post" action="{% url 'thankyou' %}">
{% crispy form %}
</form>
</div>
</div>
</section>
{% endblock %}
{% crispy form %} already declares by itself <form></form>.
I think this double declaration of <form> confuses the browser.
replace:
<form class="signup" id="signup_form" method="post" action="{% url 'thankyou' %}">
{% crispy form %}
</form>
by:
{% crispy form %}
and insert into forms.py:
class MySignUpForm(SignupForm):
def __init__(self, *args, **kwargs):
super(MySignUpForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_id = 'signup_form'
self.helper.form_class = 'signup'
self.helper.form_method = 'post'
self.helper.form_action = reverse('thankyou')
# and then the rest as usual:
self.helper.form_show_labels = False
self.helper.add_input(Submit('signup', 'Sign Up'), css_class='btn btn-primary'))
Related
I have read many questions, followed the Django docs, googled for answers, and I can't get to upload a file in my Django app.
There is no error, form.cleaned_data shows the file and the other foreign key field, but no upload to media folder and no record in my db.
I can't figure what am I missing. Any help would be much appreciated.
#models.py
class ReportFile(models.Model):
report = models.ForeignKey(Report, on_delete=models.CASCADE)
file = models.FileField(upload_to='files/reports')
uploaded = models.DateTimeField(auto_now_add=True)
uploaded_by = models.ForeignKey(User, on_delete=models.CASCADE)
def save(self, *args, **kwargs):
user = get_current_user()
if user and not user.pk:
user = None
if not self.pk:
self.creado_por = user
###UPDATE: As Iain Shelvington kindly pointed out, I was missing the call to super.
super(ReportFile, self).save(*args, **kwargs)
#forms.py
class FileForm(forms.ModelForm):
class Meta:
model = ReportFile
fields = ['file','report']
This is the view I'm using, based on what I've read
#views.py
def CreateFile(request):
if request.method == 'POST':
form = FileForm(request.POST,request.FILES)
if form.is_valid():
form.save()
print(form.cleaned_data)
#OUTPUT: HTTP POST /file 200 [0.09, 127.0.0.1:59053]
# {'file': <InMemoryUploadedFile: test-image.png (image/png)>, 'report': <Report: 49>}
return render(request, 'segcom/file_upload.html', {'form': form,})
else:
form = FileForm()
context = {
'form':form,
}
return render(request, 'segcom/file_upload.html', context)
The relevant settings that I know of
#settings.py
# Media Root
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
MEDIA_URL = '/media/'
This is the template I'm using
{% extends "layouts/base.html" %}
{% load crispy_forms_tags %}
{% block title %} File Upload Test {% endblock %}
<!-- Specific CSS goes HERE -->
{% block stylesheets %}{% endblock stylesheets %}
{% block content %}
<div class="row">
{{ form.non_field_errors }}
<form method = 'post' action="{% url 'file_upload' %}" id="report-form" enctype="multipart/form-data">
{% csrf_token %}
{{form|crispy}}
<div class="modal-footer">
<button type="submit" form="report-form" class="btn btn-primary">Guardar</button>
</div>
</form>
</div>
{% endblock content %}
<!-- Specific Page JS goes HERE -->
{% block javascripts %}
{% endblock javascripts %}
When the page loads I am seeing the error of - "This field is required" with the input form 'Title' highlighted with the red board. I would expect that this should only show after the Save button is pressed. I can toggle the message on and off with self.helper.form_tag but the behavior seems incorrect to me, it seems as though it is trying to submit before I click save.
Am I doing something wrong or is this expected behavior? If it is expected is there a way to change it to only show the warnings after Save?
forms.py
from django import forms
from .models import Request, RequestDocument
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Field, Div, Layout
class RequestForm(forms.Form):
title = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'title'}))
rfp_No = forms.CharField(widget=forms.TextInput(attrs={}), required=False)
company_Name = forms.CharField(widget=forms.TextInput(attrs={}), required=False)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
#self.helper.form_tag = False
self.helper.help_text_inline = True
self.helper.layout = Layout(
Div(Field('title'), css_class='col-md-12', ),
Div(
Div(Field('rfp_No'), css_class='col-md-6', ),
Div(Field('company_Name'), css_class='col-md-6', ),
css_class='row',
),
)
create.html
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<form method="post">
{% csrf_token %}
{% crispy form %}
<button type="submit" class=" d-block btn btn-lg btn-success mt-4 w-50 mx-auto">
Save
</button>
</form>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.3.1.min.js"></script>
<script>
$(document).ready(function(){
$(".active").removeClass("active");
$("#link-create").addClass("active");
});
</script>
adding self.helper.form_show_errors = False to the init hides the errors but this the block actual errors from showing such as if the max length was exceeded.
I eventually figured this out by splitting the POST and GET methods in my form.
forms.py
form = RequestForm()
if request.method == 'POST':
##Do Post Stuff
elif request.method == 'GET':
##Do Get Stuff
else:
context = {
}
return render(request, "requests/create.html", context)
I'm trying to implement profile picture field for users. The following is the code for each file for the implementation I tried, forms.py, models.py, views.py, and urls.py.
I use a IDE (vscode) to debug django, and I placed a breakpoint on the user.avatar = form.cleaned_data['avatar'] line in views.py below, to quickly check if cleaned_data['avatar'] is filled as user input, as I expect.
However, even after I upload a file on the url, submit, the line shows None while expected a image object, and of course it doesn't save anything so no change to the database either.
#
# forms.py
# accounts/forms.py
#
from accounts.models import UserProfile
# ..
class UserProfileForm(forms.ModelForm):
avatar = forms.ImageField(label=_('Avatar'))
class Meta:
model = UserProfile
fields = [
'avatar',
]
def __init__(self, *args, **kwargs):
super(UserProfileForm, self).__init__(*args, **kwargs)
self.fields['avatar'].required = False
#
# models.py
# accounts/models.py
#
from django.contrib.auth.models import User
from PIL import Image
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
avatar = models.ImageField(upload_to="images", blank=True, null=True)
# note: I also did "python manage.py makemigrations accounts; python manage.py migrate accounts;"
#
# views.py
# accounts/views.py
#
class UserProfileView(FormView):
template_name = 'accounts/profile/change_picture.html'
form_class = UserProfileForm
def form_valid(self, form):
user = self.request.user
user.avatar = form.cleaned_data['avatar']
user.save()
messages.success(self.request, _('Profile picture has been successfully updated'))
return redirect('accounts:change_profile_picture')
#
# urls.py
# accounts/urls.py
#
from .views import UserProfileView
urlpatterns = [
# ..
path('change/profile_picture/', UserProfileView.as_view(), name='change_profile_picture'),
]
What is wrong with the code? Thanks.
edit
as requested, the html accounts/profile/change_picture.html
{% extends 'layouts/html.html' %}
{% load static %}
{% load bootstrap4 %}
{% load i18n %}
{% block content %}
{% include 'head.html' %}
<body>
{% include 'navbar.html' %}
<div id="content" name="content" class="main container">
<div class="w-100 p-3"></div>
<h2>{% trans 'Change profile picture' %}</h2>
<form method="post">
{% csrf_token %}
{% bootstrap_form form %}
<button class="btn btn-success">{% trans 'Change' %}</button>
</form>
<div class="w-100 p-3"></div>
</div>
{% include 'footer.html' %}
</body>
{% endblock %}
Add enctype="multipart/form-data" to the form:
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{% bootstrap_form form %}
<button class="btn btn-success">{% trans 'Change' %}</button>
</form>
I'm trying to render this form:
class LoadForm(forms.Form):
class Meta:
model = Load
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.layout = Layout(
Row(
'whouse',
'supplier',
'company',
'product',
'quantity',
'unit_price',
'load_date',
)
)
with the following view:
def load(request):
form = LoadForm()
context = {
'form': form,
'title': 'Nuovo Carico',
}
return render(request, 'warehouse/load.html', context)
and the following template:
{% extends "masterpage.html" %}
{% load static %}
{% block headTitle %}
<title>{{title}}</title>
{% endblock %}
{% block contentHead %}
{% endblock %}
{% block contentBody %}
{% load document_tags %}
{% load custom_tags %}
{% load crispy_forms_tags %}
<FORM method="POST" autocomplete="off">
{{ form.media }}
{% csrf_token %}
<div class="alert alert-info">
{{ title }}
</div>
{% crispy form %}
<input type="submit" class="btn btn-primary margin-left" value="CARICA">
</FORM>
{% endblock %}
For some strange reason the form will not show up, I just see the title and the input button. I've tried the very simple form.as_p with no crispy, but still nothing...
By looking at the sourse code on the browser I see there is a div with class 'form-row' but not form in it...
looks strange.
Any help?
thank you very much.
Carlo
Your form class is defined as follows: class LoadForm(forms.Form): Note that here this is a Form and not a ModelForm hence using a Meta class and specifying model makes no difference. Instead you want to use a ModelForm [Django docs] and you also need to specify either fields or exclude in the Meta:
class LoadForm(forms.ModelForm): # `ModelForm` here
class Meta:
model = Load
fields = '__all__' # All fields
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.layout = Layout(
Row(
'whouse',
'supplier',
'company',
'product',
'quantity',
'unit_price',
'load_date',
)
)
Call form in template like this. First the name of the form you have in context plus pipe crispy. It works for me.
</div>
{{form|crispy}}
<input type="submit" class="btn btn-primary margin-left" value="CARICA">
I am creating a small system that has two users, both of these users need singup forms.
To allow social accounts and ease of use i have used django_allauth. But i ran into a problem of creating two custom signin forms with different fields.
i have used multiple stackoverflow answers but unfortunately none have helped if anything they are now adding to the confusion ...
Multiple user type sign up with django-allauth
Multiple signup, registration forms using django-allauth
I find it hard to believe that this is not a use case that comes up a lot, someone must have done this before. My current code has 2 custom signup forms, 2 custom sign-up views and two custom URLs where the forms should be rendered. But they are both using the same form and I have no idea why.
can anyone shed any light on the situation?
from .models import GraduateUserProfile
from django import forms
from allauth.account.forms import SignupForm
import datetime
def year_choices():
return [(r, r) for r in range(2015, datetime.date.today().year + 1)]
def current_year():
return datetime.date.today().year
class GraduateUserSignupForm(SignupForm):
def __init__(self, *args, **kwargs):
super(GraduateUserSignupForm, self).__init__(*args, **kwargs)
self.fields['first_name'] = forms.CharField(required=True)
self.fields['last_name'] = forms.CharField(required=True)
self.fields['phone_number'] = forms.CharField(required=True)
self.fields['degree_course_name'] = forms.CharField(required=True)
self.fields['graduation_year'] = forms.TypedChoiceField(coerce=int, choices=year_choices, initial=current_year)
def save(self, request):
user = super(GraduateUserSignupForm, self).save(request)
user.first_name = self.cleaned_data.get('first_name')
user.last_name = self.cleaned_data.get('last_name')
user.is_graduate = True
user.save()
graduate = GraduateUserProfile.objects.create(user=user)
graduate.phone_number = self.cleaned_data.get('phone_number')
graduate.graduation_year = self.cleaned_data.get('phone_number')
graduate.degree_course = self.cleaned_data.get('degree_course')
graduate.save()
return user
class CompanyUserSignupForm(SignupForm):
def __init__(self, *args, **kwargs):
super(CompanyUserSignupForm, self).__init__(*args, **kwargs)
self.fields['degree_course_name'] = forms.CharField(required=True)
self.fields['degree_course_test'] = forms.CharField(required=True)
def save(self, request):
user = super(CompanyUserSignupForm, self).save(request)
return user
from .forms import CompanyUserSignupForm, GraduateUserSignupForm
from allauth.account.views import SignupView
class CompanyUserSignupView(SignupView):
template_name = 'account/company_signup.html'
form_class = CompanyUserSignupForm
redirect_field_name = 'next'
view_name = 'company_signup'
success_url = None
def get_context_name(self, **kwargs):
ret = super(CompanyUserSignupView, self).get_context_data(**kwargs)
ret.update(self.kwargs)
return ret
company_signup = CompanyUserSignupView.as_view()
class GraduateUserSignupView(SignupView):
template_name = 'account/graduate_signup.html'
form_class = GraduateUserSignupForm
redirect_field_name = 'next'
view_name = 'graduate_signup'
success_url = None
def get_context_name(self, **kwargs):
ret = super(GraduateUserSignupView, self).get_context_data(**kwargs)
ret.update(self.kwargs)
return ret
grad_signup = GraduateUserSignupView.as_view()
urlpatterns = [
path('business/signup', view=company_signup, name='company_signup'),
path('graduate/signup', view=grad_signup, name='graduate_signup'),
]
{% load i18n %}
{% block head_title %}{% trans "Signup" %}{% endblock %}
{% block content %}
<h1>{% trans "Sign Up" %}Grad Sign up</h1>
<p>{% blocktrans %}Already have an account? Then please sign in.{% endblocktrans %}</p>
<form class="signup" id="company_signup_form" method="post" action="{% url 'graduate_signup' %}">
{% csrf_token %}
{{ form.as_p }}
{% if redirect_field_value %}
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
{% endif %}
<button type="submit">{% trans "Sign Up" %} »</button>
</form>
{% endblock %}
{% extends "account/base.html" %}
{% load i18n %}
{% block head_title %}{% trans "Signup" %}{% endblock %}
{% block content %}
<h1>{% trans "Sign Up" %}BUSINESS LOGIN</h1>
<p>{% blocktrans %}Already have an account? Then please sign in.{% endblocktrans %}</p>
<form class="signup" id="graduate_signup_form" method="post" action="{% url 'company_signup' %}">
{% csrf_token %}
{{ form.as_p }}
{% if redirect_field_value %}
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
{% endif %}
<button type="submit">{% trans "Sign Up" %} »</button>
</form>
{% endblock %}
I have just figure it out. If you remove 'signup': 'accounts.forms.GraduateUserSignupForm', my forms are appearing correctly
EDIT: After a few days i found that the original allauth sign up view is still available to view. So i used this little peice of code
path('accounts/signup/', page_not_found, {'exception': Exception('Not Found')}, name="default_signup"),
to throw a 404 if anyone tried to view it