This is my forms.py
from django import forms
from django.core import validators
from django.contrib.auth.models import User
class RegistrationForm(forms.Manipulator):
def __init__(self):
self.fields = (
forms.TextField(field_name='username',
length=30, maxlength=30,
is_required=True, validator_list=[validators.isAlphaNumeric,
self.isValidUsername]),
forms.EmailField(field_name='email',
length=30,
maxlength=30,
is_required=True),
forms.PasswordField(field_name='password1',
length=30,
maxlength=60,
is_required=True),
forms.PasswordField(field_name='password2',
length=30, maxlength=60,
is_required=True,
validator_list=[validators.AlwaysMatchesOtherField('password1',
'Passwords must match.')]),
)
def isValidUsername(self, field_data, all_data):
try:
User.objects.get(username=field_data)
except User.DoesNotExist:
return
raise validators.ValidationError('The username "%s" is already taken.' % field_data)
def save(self, new_data):
u = User.objects.create_user(new_data['username'],
new_data['email'],
new_data['password1'])
u.is_active = False
u.save()
return u
This is my views.py
from django.contrib.auth import authenticate, login
from django.shortcuts import render_to_response
import datetime, random, sha
from django.shortcuts import render_to_response, get_object_or_404
from django.core.mail import send_mail
def login(request):
def errorHandle(error):
form = LoginForm()
return render_to_response('login.html', {
'error' : error,
'form' : form,
})
if request.method == 'POST': # If the form has been submitted...
form = LoginForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
# Redirect to a success page.
login(request, user)
return render_to_response('userprof/why.html', {
'username': username,
})
else:
# Return a 'disabled account' error message
error = u'account disabled'
return errorHandle(error)
else:
# Return an 'invalid login' error message.
error = u'invalid login'
return errorHandle(error)
else:
error = u'form is invalid'
return errorHandle(error)
else:
form = LoginForm() # An unbound form
return render_to_response('login.html', {
'form': form,
})
def loggedin(request):
return render_to_response('loggedin.html', {})
def register(request):
if request.user.is_authenticated():
# They already have an account; don't let them register again
return render_to_response('userprof/register.html', {'has_account': True})
manipulator = RegistrationForm()
if request.POST:
new_data = request.POST.copy()
errors = manipulator.get_validation_errors(new_data)
if not errors:
# Save the user
manipulator.do_html2python(new_data)
new_user = manipulator.save(new_data)
# Build the activation key for their account
salt = sha.new(str(random.random())).hexdigest()[:5]
activation_key = sha.new(salt+new_user.username).hexdigest()
key_expires = datetime.datetime.today() + datetime.timedelta(2)
# Create and save their profile
new_profile = UserProfile(user=new_user,
activation_key=activation_key,
key_expires=key_expires)
new_profile.save()
# Send an email with the confirmation link
email_subject = 'Your new example.com account confirmation'
return render_to_response('userprof/register.html', {'created': True})
else:
errors = new_data = {}
form = forms.FormWrapper(manipulator, new_data, errors)
return render_to_response('userprof/register.html', {'form': form})
def confirm(request, activation_key):
if request.user.is_authenticated():
return render_to_response('userprof/confirm.html', {'has_account': True})
user_profile = get_object_or_404(UserProfile,
activation_key=activation_key)
if user_profile.key_expires < datetime.datetime.today():
return render_to_response('confirm.html', {'expired': True})
user_account = user_profile.user
user_account.is_active = True
user_account.save()
return render_to_response('confirm.html', {'success': True})
This is the template I am planning to use
https://github.com/yourcelf/django-registration-defaults/tree/master/registration_defaults/templates .
According to it , I did changes in the settings.py , but it gave me an error
Error: Can't find the file 'settings.py' in the directory containing 'manage.py'. It appears you've customized things.
You'll have to run django-admin.py, passing it your settings module.
(If the file settings.py does indeed exist, it's causing an ImportError somehow.)
Is it a good idea to use those templates or should I go for my own custom templates ?
Wow, what version of Django are you using? forms.Manipulator was removed in version 1.0 - three years ago - and was deprecated for a year before that.
I'm guessing that your project has an import problem - but Django catches this and doesn't show you the real error. Note that this does not need to be an import from settings.py - it can be an import anywhere in the project.
Try running:
python settings.py
And python should tell you what the import problem is. If there is no output, your problem is elsewhere - but this is a very likely candidate.
Related
I have a register view that sends an activation email and I want to test sending emails with "mailoutbox" parameter in pytest_django.
This is the register view:
for EmailMessage I used mail_templated and send
from django.http import HttpResponseRedirect
from django.shortcuts import redirect, render
from django.urls import reverse_lazy
from django.views.generic.edit import FormView
from django.contrib.auth import get_user_model
from rest_framework_simplejwt.tokens import RefreshToken
from mail_templated import EmailMessage
from decouple import config
from .utils import EmailThread
from .forms import UserCreationModelForm
User = get_user_model()
class AccountsRegisterFormView(FormView):
"""
A view for displaying a register form and rendering a template response.
"""
form_class = UserCreationModelForm
template_name = 'accounts/register.html'
success_url = reverse_lazy('task:list')
def get_token_for_user(self, user):
refresh = RefreshToken.for_user(user)
return str(refresh.access_token)
def form_valid(self, form):
"""
If the form is valid, redirect to the supplied URL
:param form: registration form
:return: if form valid register user.
"""
user_obj = form.save()
if user_obj is not None:
email = user_obj.email
username = user_obj.username
token = self.get_token_for_user(user_obj)
domain = 'http://127.0.0.1:8000/'
url = 'accounts/activation/confirm/'
activation_email = EmailMessage(
'email/activation_account.tpl',
{
'user': username,
'token': f'{domain}{url}{token}/',
},
'sender#example.com',
[email]
)
EmailThread(activation_email).start()
return HttpResponseRedirect(
reverse_lazy('accounts:activation_send')
)
return super(AccountsRegisterFormView, self).form_valid(form)
I used EmailMessage from mail_templated package.
and this is the test:
#pytest.mark.django_db
class TestAccountsViews:
def test_accounts_views(self, client, mailoutbox):
url = reverse('accounts:register')
data = {
'email': 'test_register#test.com',
'username': 'test_username_register',
'password1': 'password2register',
'password2': 'password2register',
}
response = client.post(path=url, data=data)
assert response.status_code == 302
assert len(mailoutbox) == 1
email = mailoutbox[0]
assert email.subject == 'Email Verification'
and this is what I received:
======================================= FAILURES ========================================
_________________________ TestAccountsViews.test_accounts_views _________________________
self = <app.accounts.tests.TestAccountsViews object at 0x7f7856be1ed0>
client = <django.test.client.Client object at 0x7f785660f010>
mailoutbox = [<mail_templated.message.EmailMessage object at 0x7f7855d90310>]
def test_accounts_views(self, client, mailoutbox):
url = reverse('accounts:register')
data = {
'email': 'test_register#test.com',
'username': 'test_username_register',
'password1': 'password2register',
'password2': 'password2register',
}
response = client.post(path=url, data=data)
assert response.status_code == 302
> assert len(mailoutbox) == 1
E assert 0 == 1
E + where 0 = len([])
accounts/tests.py:67: AssertionError
How could i achieve email functionality using drf as backeend and django to hit these apis.What i need how will user be confirm from django while using drf to send activation link.
At first, you need to add the code to send the verification email when you register.
from base64 import urlsafe_b64decode, urlsafe_b64encode
from django.contrib.auth.tokens import default_token_generator
from django.template.loader import render_to_string
from threading import Thread
class EmailRegisterView(APIView):
"""APIs for Email Registeration"""
permission_classes = [AllowAny]
def post(self, request):
"""Signup with Email"""
serializer = EmailRegisterSerializer(data=request.data)
if serializer.is_valid():
...
user.save()
// send verification link
cur_token = default_token_generator.make_token(user)
email = urlsafe_b64encode(str(user.email).encode('utf-8'))
# now send email
mail_subject = 'Email Confirmation'
message = render_to_string('emails/email_verification.html', {
'site_url': settings.SITE_URL,
'token': f'api/users/verify/{email.decode("utf-8")}/{cur_token}',
})
t = Thread(target=send_mail, args=(
mail_subject, message, settings.EMAIL_FROM_USER, to_email))
t.start()
return Response({
"success": True,
"user": MemberSerializer(user).data
}, status.HTTP_200_OK)
And you can add the confirmation view.
urlpatterns = [
...
path('verify/<str:email>/<str:email_token>',
verify_email, name="verify_token"),
...
]
Then the verify_email function verifies the token and redirects.
#api_view(['GET'])
#permission_classes([AllowAny])
def verify_email(request, email, email_token):
"""Verify Email"""
try:
target_link = settings.CLIENT_URL + "/account/result?type=email_verified"
if verify_token(email, email_token):
return redirect(target_link)
else:
return render(
request,
"emails/email_error.html",
{'success': False, 'link': target_link}
)
except BaseException:
pass
Here is the verify_token function.
def verify_token(email, email_token):
"""Return token verification result"""
try:
users = Member.objects.filter(
email=urlsafe_b64decode(email).decode("utf-8"))
for user in users:
valid = default_token_generator.check_token(user, email_token)
if valid:
user.is_verified = True
user.save()
return valid
except BaseException:
pass
return False
I want to override the pipeline to associate user's with their email for accounts that are only active. But I need the backend for a regular login. The AUTHENTICATION_BACKENDS(django.contrib.auth.backends.AllowAllUsersModelBackend) allows for all user's to be authenticated but I want only certain user's with the is_active to be authenticated using Google Login.
settings.py
AUTHENTICATION_BACKENDS = ['social_core.backends.google.GoogleOAuth2','django.contrib.auth.backends.AllowAllUsersModelBackend',]
# Extends default user with additional fields
AUTH_USER_MODEL = 'pages.Profile'
SOCIAL_AUTH_USER_MODEL = 'pages.Profile'
# social auth configs for google
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = config('GOOGLE_OAUTH2_KEY')
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = config('GOOGLE_OAUTH2_SECRET')
SOCIAL_AUTH_GOOGLE_OAUTH2_SCOPE = ['https://www.googleapis.com/auth/calendar']
SOCIAL_AUTH_JSONFIELD_ENABLED = True
SOCIAL_AUTH_GOOGLE_OAUTH2_AUTH_EXTRA_ARGUMENTS = {'access_type': 'offline','approval_prompt':'force'}
SESSION_COOKIE_SAMESITE = None
SOCIAL_AUTH_PIPELINE = (
'social_core.pipeline.social_auth.social_details',
'social_core.pipeline.social_auth.social_uid',
'social_core.pipeline.social_auth.auth_allowed',
'social_core.pipeline.social_auth.social_user',
'social_core.pipeline.user.get_username',
'social_core.pipeline.social_auth.associate_by_email', # <--- enable this one
'social_core.pipeline.user.create_user',
'social_core.pipeline.social_auth.associate_user',
'social_core.pipeline.social_auth.load_extra_data',
'social_core.pipeline.user.user_details',
'pages.pipeline.save_token'
)
views.py
def login(request):
if request.method == 'POST':
form = AuthenticationForm(request.POST)
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user:
if user.is_active:
auth_login(request, user)
return redirect('home')
else:
messages.error(request,'User blocked',extra_tags='login')
return redirect('login')
else:
messages.error(request,'username or password not correct',extra_tags='login')
return redirect('login')
else:
form = AuthenticationForm()
return render(request, 'registration/login.html',{'form':form})
To override the original partial,
In your pipeline.py (create one, if you don't have it in your app directory), define a method:
from social_core.pipeline.partial import partial
from social_core.exceptions import AuthAlreadyAssociated, AuthException, AuthForbidden
#partial
def associate_by_email(backend, details, user=None, *args, **kwargs):
# No user
if user:
return None
email = details.get('email')
if email:
# Try to associate accounts registered with the same email address,
# only if it's a single object. AuthException is raised if multiple
# objects are returned.
users = list(backend.strategy.storage.user.get_users_by_email(email))
#That's the line you want to add
active_users = [user for user in users if user.is_active]
if len(active_users) == 0:
return None
elif len(active_users) > 1:
raise AuthException(
backend,
'The given email address is associated with another account'
)
else:
return {'user': active_users[0],
'is_new': False}
And then in your settings.py replace the line
SOCIAL_AUTH_PIPELINE = (
...
'social_core.pipeline.social_auth.associate_by_email', # <--- enable this
...
)
with:
SOCIAL_AUTH_PIPELINE = (
...
'your_app.pipeline.associate_by_email', # <--- enable this one
...
)
Sure position the pipeline partial where it has in place of original one.
The original method does not perform the check, so to add this behavior you have to write your own partial that overrides this behavior.
Assuming you have a Django app (here for the sake of simplicity called your_app - just update the names accordingly.
The original code for partial associate_by_email can be found here.
If you want to add this check to simple authentication, you can provide your own backend in a file your_app/backends.py:
from django.core.exceptions import ObjectDoesNotExist
from settings import settings
from django.contrib.auth.models import User
from django.contrib.auth.backends import ModelBackend
class LocalActiveBackend(object):
def authenticate(self, username=None, password=None):
try:
# Add extra check for is_active
user = User.objects.get(username=username, is_active=True)
except User.DoesNotExist:
return None
pwd_valid = user.check_password(password)
if pwd_valid:
return user
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
And then append that new backend to the list of available backends:
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend', # default
...
'your_app.backends.LocalActiveBackend',
)
This will allow you using it with simple authentication by password and username if needed.
I am making a website in django and in my homepage I want to show the list of my recent blog post and a few blocks below I want to make a simple contact form. The blog and the contact form separately are working fine. But I want to include them in the same page(obviously in the same url).
The views.py is:
from .forms import NameForm
def get_name(request):
if request.method == 'POST':
form = NameForm(request.POST)
if form.is_valid():
return HttpResponseRedirect('/thanks/')
else:
form = NameForm()
return render(request, 'personal/index.html', {'form': form})
If you want to look at the forms.py then :
from django import forms
class NameForm(forms.Form):
your_name = forms.CharField(label='Your name', max_length=100)
The urlpattern in urls.py of my homepage is:
urlpatterns = [
url(r'^$', ListView.as_view(
queryset=Post.objects.all().order_by("-date")[:2],
template_name="personal/index.html")),
url(r'^$', views.get_name, name='contact'),
]
With this urlpatter the list of blog post shows up perfectly but the contact form doesn't show up. But with the below urlpattern contact form shows up but blog posts doesn't show up.
urlpatterns = [
url(r'^$', views.get_name, name='contact'),
url(r'^$', ListView.as_view(
queryset=Post.objects.all().order_by("-date")[:2],
template_name="personal/index.html")),
]
I want to make both of these contents show up in the same page. Please help me. If you need any more information then do tell.
It is not possible to have multiple views for the same URL. You can only have one view for each URL.
In this case, the easiest fix is to update your get_name view so that it includes the posts in the template context.
def get_name(request):
if request.method == 'POST':
form = NameForm(request.POST)
if form.is_valid():
return HttpResponseRedirect('/thanks/')
else:
form = NameForm()
post_list = Post.objects.all().order_by("-date")[:2]
return render(request, 'personal/index.html', {'form': form, 'post_list': post_list})
Then remove the url pattern that uses ListView so that get_name handles the requests.
While you cannot do that directly, you could consider your function in views.py to handle the request in a way that when it gets a post request from one model then it initialises the form for the respective model only. For this to happen you will need to assign a name to your button. See below code -
def SignUpLogin(request):
message = ""
Login_message = ""
print(request.POST)
if request.method == 'POST':
if request.POST.get('UserForgotPassword') == 'Sign Up':
form_SignUP_Login = LoginForm()
print("POST request received")
print(request.POST)
form_SignUP=SignUpForm(request.POST)
print(form_SignUP)
if form_SignUP.is_valid():
if request.POST["Password"] != request.POST["Confirm_Password"]:
message = "* Passwords do not match"
#raise forms.ValidationError(('Passwords do not match'), code="PasswordNotMatched")
else:
try:
user = User.objects.get(username=request.POST["Username"])
context= {'form': form_SignUP, 'error':'* Username already taken. Please try different username.'}
return render(request, 'User/SignUp.html', context)
except User.DoesNotExist:
user = User.objects.create_user(request.POST["Username"], request.POST["Email"], request.POST["Password"])
user.first_name = request.POST["First_Name"]
user.last_name = request.POST["Last_Name"]
user.save()
form_SignUP = SignUpModel(Username = request.POST["Username"], First_Name = request.POST["First_Name"], Last_Name = request.POST["Last_Name"], Email = request.POST["Email"], Company_Name = request.POST["Company_Name"], Address = request.POST["Address"], Password = make_password(request.POST["Password"]), Confirm_Password = make_password(request.POST["Confirm_Password"]), Phone_Number = request.POST["Phone_Number"])
form_SignUP.save()
#queryset = SignUpModel.objects.get(Username = request.POST["Username"])
#queryset.Password = "pwd_removed"
#queryset.Confirm_Password = "pwd_removed"
#queryset.save()
#send_email_to_host(request.POST["First_Name"], request.POST["Family_Name"], request.POST["Number_Of_Adults"], request.POST["Number_Of_Kids"], request.POST["Email"])
return redirect(HomePage)
elif request.POST.get('UserLogin') == 'Login':
form_SignUP = SignUpForm()
form_SignUP_Login=LoginForm(request.POST)
if form_SignUP_Login.is_valid():
user = authenticate(username=request.POST["Username"], password=request.POST["Password"])
if user is not None:
print("User authenticated")
return redirect(HomePage)
else:
print("User not authenticated")
form_SignUP_Login = LoginForm()
Login_message = "Username and password combination is not correct"
elif request.POST.get('UserForgotPassword') == 'Forgot Password':
form_SignUP = SignUpForm()
form_SignUP_Login = LoginForm()
else:
form_SignUP = SignUpForm()
form_SignUP_Login = LoginForm()
return render(request, 'User/SignUp.html', {'form' : form_SignUP, "error":message,'form_login' : form_SignUP_Login, "error_login":Login_message})
I'm trying to implement a custom auth using RemoteUserMiddleware
My settings.py has:
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.RemoteUserBackend',
'my_app.backends.custom_auth.CustomHeaderMiddleware',
)
And backends.custom_auth.py
class CustomHeaderMiddleware(RemoteUserMiddleware):
header = "CUSTOM_USERID"
def process_request(self, request):
if not hasattr(request, 'user'):
raise ImproperlyConfigured("...")
try:
username = request.META[self.header]
except KeyError:
if request.user.is_authenticated():
try:
stored_backend = load_backend(request.session.get(
auth.BACKEND_SESSION_KEY, ''))
if isinstance(stored_backend, RemoteUserBackend):
auth.logout(request)
except ImproperlyConfigured as e:
# backend failed to load
auth.logout(request)
return
if request.user.is_authenticated():
if request.user.get_username() == self.clean_username(username, request):
return
user = auth.authenticate(remote_user=username)
if user:
request.user = user
auth.login(request, user)
I'm testing this locally, and request.META['CUSTOM_USERID'] is set. But CustomHeaderMiddleware.process_request is not being hit.
I want the user to be checked whenever the CUSTOM_USERID key is present.
What am I doing wrong?
Thanks in advance!!