Creating Two Custom Singin Forms using django_allauth - django

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

Related

Django "ImageField" form value is None

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>

Show django form in a designed page

How are you?
I m totally new in Django.I designed a page and I wanted to show a django form(edit or create) in a well designed HTML page. but i do not know how.
This is my owner method:
class OwnerUpdateView(LoginRequiredMixin, UpdateView):
"""
queryset to the requesting user.
"""
def get_queryset(self):
print('update get_queryset called')
""" Limit a User to only modifying their own data. """
qs = super(OwnerUpdateView, self).get_queryset()
return qs.filter(user=self.request.user)
class OwnerCreateView(LoginRequiredMixin, CreateView):
"""
Sub-class of the CreateView to automatically pass the Request to the Form
and add the owner to the saved object.
"""
# Saves the form instance, sets the current object for the view, and redirects to get_success_url().
def form_valid(self, form):
print('form_valid called')
object = form.save(commit=False)
object.user = self.request.user
object.save()
return super(OwnerCreateView, self).form_valid(form)
This is my views.py
class TaskUpdateView(OwnerUpdateView):
model = Task
fields = ["title", "text", "endDate"]
class TaskCreateView(OwnerCreateView):
model = Task
fields = ["title","text","status","endDate"]
This is my urls.py:
app_name='task'
urlpatterns = [
path('', views.TaskListView.as_view(), name='all'),
path('task/<int:pk>/', views.TaskDetailView.as_view(), name='detail'),
path('task/create', views.TaskCreateView.as_view(success_url=reverse_lazy('task:all')), name='task_create'),
path('task/update/<int:pk>', views.TaskUpdateView.as_view(success_url=reverse_lazy('task:all')),
name='task_update'),
path('task/delete/<int:pk>', views.TaskDeleteView.as_view(success_url=reverse_lazy('task:all')),
name='task_delete'),
path("accounts/login/", views.login, name='login'),
path("accounts/logout/", views.logout, name='logout'),
]
And this is the models.py:
class Task(models.Model):
title=models.CharField(max_length=250)
text=models.TextField()
user=models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=False)
status=models.ForeignKey('Status',on_delete=models.SET_NULL,null=True)
startDate=models.DateTimeField(auto_now_add=True)
endDate=models.DateField(null=True)
def __str__(self):
return self.title
class Status(models.Model):
name=models.CharField(max_length=250)
def __str__(self):
return self.name
And this is where these both function work:
{%extends 'base.html'%}
{% block content %}
<form action="" method="post">
{% csrf_token %}
<table>{{ form.as_table }}</table>
<input type="submit" value="Submit">
{# <input type="submit" onclick="window.location='{% url 'project:all' %}' ; return false;" value="Cancel">#}
</form>
{% endblock %}
How can i separate each element of this form and put it in a better designed page?
Thanks
There are two ways:
Option 1:
Loop over the form fields and render them individually:
{% for field in form %}
<div class="form-group">
{{ field.errors }}
{{ field.label_tag }} {{ field }}
{% if field.help_text %}
<span class="form-text">{{ field.help_text|safe }}</span>
{% endif %}
</div>
{% endfor %}
See docs for more.
Option 2:
You can manually create form inputs and give them the correct field name attribute. This gives you more control but also requires more work:
<div class="form-group"
<input
type="text"
name="title"
value="{{ form.title.value }}"
class="form-control {% if form.title.errors %}is-invalid{% endif %}"
>
{% if form.title.help_text%}
<span class="form-text">{{ form.title.help_text|safe }}</span>
{% endif %}
<div class="invalid-feedback">{{ form.title.errors }}</div>
</div>
<!-- now do the same for other fields -->

To change the display depending on the presence or absence of data

Django2.1
I want to create a button that will be displayed when the user saves data in the past.
I thought it could be done with {% if object %}, but it seemed different.
Here is the failed code.
{% if user.is_authenticated %}
{% if object %}
<a class="btn" href="{% url 'detail' user.id %}">Check</a>
{% else %}
<a class="btn" href="{% url 'create' %}">Create</a>
{% endif %}
{% else %}
#models.py
class Mymodel(models.Model):
user = models.OneToOneField(get_user_model(), on_delete=models.CASCADE)
title = models.CharField(max_length=20)
content = models.TextField(max_length=5000)
posted_date = models.DateTimeField(auto_now=True)
I will create data in CreateView.
class MyCreateView(LoginRequiredMixin, CreateView):
model = Mymodel
form_class = MyForm
def form_valid(self, form):
obj = form.save(commit=False)
obj.user = self.request.user
return super(MyCreateView, self).form_valid(form)
def get_success_url(self):
return reverse_lazy('detail', kwargs={"pk": self.object.pk})
thank you for reading it until the very end.
I think it should be like this:
{% if user.is_authenticated %}
{% if user.mymodel %}
<a class="btn" href="{% url 'detail' user.id %}">Check</a>
{% else %}
<a class="btn" href="{% url 'create' %}">Create</a>
{% endif %}
{% else %}
Because, Mymodel and User has OneToOne relation, so you can get the Mymodel value using:
user.mymodel # class name all lower case
And also get user data from my model using:
mymodel = Mymodel.objects.get(id=1)
mymodel.user

How to add user authentication in generic delete view in django2.0.6

I have a page which displays posts of all user and user can only delete his posts.
Heres the code:
class PostDelete(generic.DeleteView):
model = Post
template_name = 'dashboard/post_delete.html'
success_url = reverse_lazy('dashboard:posts')
post_delete.html:
{% extends 'dashboard/sidebar.html' %}
{% block title %}Confirmation{% endblock %}
{% block mainpage %}
<div id="page-wrapper" align="center">
<div id="page-inner">
<h1>New post</h1>
<form method="post">
{% csrf_token %}
Are you sure you want to delete?
<br>
<button class="btn btn-danger">Yes</button>
No
</form>
</div>
</div>
{% endblock %}
Urls.py:
path('delete/<int:pk>',views.PostDelete.as_view(),name='delete'),
How do I add user authentication code?
If it were a function I would have used " if request.user.is_authenticated "
But I don't know how to achieve this thing in a class. If you need an excerpt of another code then comment. Thanks!
Try to use UserPassesTestMixin:
from django.contrib.auth.mixins import UserPassesTestMixin
class PostDelete(UserPassesTestMixin, generic.DeleteView):
model = Post
template_name = 'dashboard/post_delete.html'
success_url = reverse_lazy('dashboard:posts')
raise_exception = True
def test_func(self):
self.object = self.get_object()
return self.object.user == self.request.user

Retrieving models from form with ModelMultipleChoiceField

I am having difficulties with forms, specifically ModelMultipleChoiceField.
I've pieced together this code from various examples, but it sadly doesn't work.
I would like to be able to:
Search for some Works on work_search.html
Display the results of the search, with checkboxes next to each result
Select the Works I want, via the checkboxes
After pressing Add, display which works were selected.
I believe everything is okay except the last part. The page simply displays "works" :(
Here is the code - sorry about the length.
Models.py
class Work(models.Model):
title = models.CharField(max_length=200)
artist = models.CharField(max_length=200)
writers = models.CharField(max_length=200)
def __unicode__(self):
return self.title + ' - ' + self.artist
forms.py
class WorkSelectForm(forms.Form):
def __init__(self, queryset, *args, **kwargs):
super(WorkSelectForm, self).__init__(*args, **kwargs)
self.fields['works'] = forms.ModelMultipleChoiceField(queryset=queryset, widget=forms.CheckboxSelectMultiple())
views.py
def work_search(request):
query = request.GET.get('q', '')
if query:
qset = (
Q(title__icontains=query) |
Q(artist__icontains=query) |
Q(writers__icontains=query)
)
results = Work.objects.filter(qset).distinct()
form = WorkSelectForm(results)
return render_to_response("work_search.html", {"form": form, "query": query })
else:
results = []
return render_to_response("work_search.html", {"query": query })
def add_works(request):
#if request.method == POST:
form = WorkSelectForm(request.POST)
#if form.isvalid():
items = form.fields['works'].queryset
return render_to_response("add_works.html", {"items":items})
work_search.html
{% extends "base.html" %}
{% block content %}
<h1>Search</h1>
<form action="." method="GET">
<label for="q">Search: </label>
<input type="text" name="q" value="{{ query|escape }}">
<input type="submit" value="Search">
</form>
{% if query %}
<h2>Results for "{{ query|escape }}":</h2>
<form action="add_works" method="post">
<ul>
{% if form %}
{{ form.as_ul }}
{% endif %}
</ul>
<input type="submit" value="Add">
</form>
{% endif %}
{% endblock %}
add_works.html
{% extends "base.html" %}
{% block content %}
{% if items %}
{% for item in items %}
{{ item }}
{% endfor %}
{% else %}
<p>Nothing selected</p>
{% endif %}
{% endblock %}
In add_works, you're not constructing your WorkSelectForm the right way. It's expecting as a first parameter the queryset of possible/authorized choices, then the POST data.
Also, you're not accessing the selected works correctly from the form. You have to use is_valid method on the form, then use cleaned_data as described in the doc.
From what I see in your work_search view, there's no restriction on which Work objects you can search then add to the result, so you could do simply:
def add_works(request):
#if request.method == POST:
form = WorkSelectForm(Work.objects.all(), request.POST)
if form.is_valid():
# the items are in form.cleaned_data['works']
items = form.cleaned_data['works']
return render_to_response("add_works.html", {"items":items})
else:
# handle error case here
...