I am going through thinkster.io tutorial about Django and AngularJS. I am about half way of tutorial, but I am getting the error 405 Method not allowed for my POST request of user registration. I tried all sugestions but none of them seems to work ( I also tried to copying whole code from tutorial ). Here is my code:
views.py
from django.shortcuts import render
from rest_framework import permissions, viewsets
from authentication.models import Account
from authentication.permissions import IsAccountOwner
from authentication.serializers import AccountSerializer
class AccountViewSet(viewsets.ModelViewSet):
lookup_field = 'username'
queryset = Account.objects.all()
serializer_class = AccountSerializer
def get_permissions(self):
if self.request.method in permissions.SAFE_METHODS:
return (permissions.AllowAny(),)
if self.request.method == 'POST':
return (permissions.AllowAny(),)
return (permissions.IsAuthenticated(), IsAccountOwner(),)
def create(self, request):
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
Account.objects.create_user(**serializer.validated_data)
return Response(serializer.validated_data, status=status.HTTP_201_CREATED)
return Response({
'status': 'Bad request',
'message': 'Account could not be created with received data.'
}, status=status.HTTP_400_BAD_REQUEST)
serializers.py
from django.contrib.auth import update_session_auth_hash
from rest_framework import serializers
from authentication.models import Account
class AccountSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True, required=False)
confirm_password = serializers.CharField(write_only=True, required=False)
class Meta:
model = Account
fields = ('id', 'email', 'username', 'created_at', 'updated_at',
'first_name', 'last_name', 'tagline', 'password',
'confirm_password',)
read_only_fields = ('created_at', 'updated_at',)
def create(self, validated_data):
return Account.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.username = validated_data.get('username', instance.username)
instance.tagline = validated_data.get('tagline', instance.tagline)
instance.save()
password = validated_data.get('password', None)
confirm_password = validated_data.get('confirm_password', None)
if password and confirm_password and password == confirm_password:
instance.set_password(password)
instance.save()
update_session_auth_hash(self.context.get('request'), instance)
return instance
permissions.py
from rest_framework import permissions
class IsAccountOwner(permissions.BasePermission):
def has_object_permission(self, request, view, account):
if request.user:
return account == request.user
return False
urls.py
from django.conf.urls import patterns, url, include
from thinkster_django_angular_boilerplate.views import IndexView
from rest_framework_nested import routers
from authentication.views import AccountViewSet
router = routers.SimpleRouter()
router.register(r'accounts', AccountViewSet)
urlpatterns = patterns(
'',
# ... URLs
url(r'^/api/v1/', include(router.urls)),
url('^.*$', IndexView.as_view(), name='index'),
)
models.py
from django.contrib.auth.models import AbstractBaseUser
from django.contrib.auth.models import BaseUserManager
from django.db import models
class AccountManager(BaseUserManager):
def create_user(self, email, password=None, **kwargs):
if not email:
raise ValueError('Users must have a valid email address.')
if not kwargs.get('username'):
raise ValueError('Users must have a valid username.')
account = self.model(
email=self.normalize_email(email), username=kwargs.get('username')
)
account.set_password(password)
account.save()
return account
def create_superuser(self, email, password, **kwargs):
account = self.create_user(email, password, **kwargs)
account.is_admin = True
account.save()
return account
class Account(AbstractBaseUser):
email = models.EmailField(unique=True)
username = models.CharField(max_length=40, unique=True)
first_name = models.CharField(max_length=40, blank=True)
last_name = models.CharField(max_length=40, blank=True)
tagline = models.CharField(max_length=140, blank=True)
is_admin = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
objects = AccountManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
def __unicode__(self):
return self.email
def get_full_name(self):
return ' '.join([self.first_name, self.last_name])
def get_short_name(self):
return self.first_name
I think the problem is with Django code, because AngularJS seems to not throw any errors and I can see that request is coming to the django. I am using python3.4, Django 1.8.3, and Django-rest-framework 3.1.
Related
I have been trying to find a solution of a problem in Django for a very long time. The problem is I am trying to develop a login system than can use either email or phone number to authenticate user.
Well, that can be done using by creating a custom user model . I have tested this. It works.
First steps
The first thing you need to do is create a new Django project. Make sure you don't run migrations because there are still a few things we need to do before then.
After creating your new Django project, create a new app called accounts with the following command:
python manage.py startapp accounts
Creating the User Model
By default, the User model provided by Django has a username field, and an email field. However, we also need a phone number field. In order to add this field, we need to extend the Django user model. In the accounts app's models.py file, type in the following code:
models.py
phone_validator = RegexValidator(r"^(\+?\d{0,4})?\s?-?\s?(\(?\d{3}\)?)\s?-?\s?(\(?\d{3}\)?)\s?-?\s?(\(?\d{4}\)?)?$", "The phone number provided is invalid")
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(max_length=100, unique=True)
phone_number = models.CharField(max_length=16, validators=[phone_validator], unique=True)
full_name = models.CharField(max_length=30)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
# is_translator = models.BooleanField(default=False)
objects = CustomUserManager()
USERNAME_FIELD = 'phone_number'
REQUIRED_FIELDS = ['email', 'full_name']
def __str__(self):
return self.email
#staticmethod
def has_perm(perm, obj=None, **kwargs):
return True
#staticmethod
def has_module_perms(app_label, **kwargs):
return True
#property
def is_staff(self):
return self.is_admin
Register the model with the admin
admin.py
class UserAdmin(BaseUserAdmin):
form = UserChangeForm
add_form = UserCreationForm
list_display = ('email', 'phone_number', 'full_name', 'is_active', 'is_admin')
list_filter = ('is_active', 'is_admin')
fieldsets = (
(None, {'fields': ('full_name', 'email', 'phone_number', 'password')}),
('Permissions', {'fields': ('is_active', 'is_admin', 'is_superuser', 'last_login', 'groups', 'user_permissions')}),
)
add_fieldsets = (
(None, {'fields': ('full_name', 'phone_number', 'email', 'password1', 'password2')}),
)
search_fields = ('email', 'full_name')
ordering = ('email',)
filter_horizontal = ('groups', 'user_permissions')
def get_form(self, request, obj=None, **kwargs):
form = super().get_form(request, obj, **kwargs)
is_superuser = request.user.is_superuser
if is_superuser:
form.base_fields['is_superuser'].disabled = True
return form
admin.site.register(User, UserAdmin)
forms.py
class UserLoginForm(forms.Form):
email = forms.CharField(max_length=50)
password = forms.CharField(widget=forms.PasswordInput(attrs={'class': 'form-control'}))
for login costumer in login.html
views.py
import random
from .backends import EmailPhoneUsernameAuthenticationBackend as EoP
class UserLoginView(View):
form_class = UserLoginForm
template_name = 'accounts/login.html'
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated:
return redirect('core:home')
return super().dispatch(request, *args, **kwargs)
def get(self, request):
form = self.form_class
return render(request, self.template_name, {'form': form})
def post(self, request):
form = self.form_class(request.POST)
if form.is_valid():
cd = form.cleaned_data
user = EoP.authenticate(request, username=cd['email'], password=cd['password'])
if user is not None:
login(request, user)
messages.success(request, 'You have successfully logged in!', 'success')
return redirect('core:home')
else:
messages.error(request, 'Your email or password is incorrect!', 'danger')
return render(request, self.template_name, {'form': form})
Writing a Custom Backend
backends.py
from django.contrib.auth.hashers import check_password
from django.contrib.auth import get_user_model
from django.db.models import Q
User = get_user_model()
class EmailPhoneUsernameAuthenticationBackend(object):
#staticmethod
def authenticate(request, username=None, password=None):
try:
user = User.objects.get(
Q(phone_number=username) | Q(email=username)
)
except User.DoesNotExist:
return None
if user and check_password(password, user.password):
return user
return None
#staticmethod
def get_user(user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
Update the settings (3 Options)
settings.py
INSTALLED_APPS = [
...
# Third-party apps
'accounts.apps.AccountsConfig',
...
]
AUTH_USER_MODEL = 'accounts.User'
AUTHENTICATION_BACKENDS = [
'accounts.backends.EmailPhoneUsernameAuthenticationBackend'
]
I hope your problem will be solved and others will use it.
Well that can be done using by creating a custom user model . This is the only way known to me to achieve the result.
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
class MyAccountManager(BaseUserManager):
def create_user(self, email, username, password=None):
if not email:
raise ValueError('Users must have an email address')
if not username:
raise ValueError('Users must have a username')
user = self.model(
email=self.normalize_email(email),
username=username,
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, username, password):
user = self.create_user(
email=self.normalize_email(email),
password=password,
username=username,
)
user.is_admin = True
user.is_staff = True
user.is_superuser = True
user.save(using=self._db)
return user
def get_profile_image_filepath(self , filepath):
return 'profile_images/' + str(self.pk) + '/profile_image.png'
def get_default_profile_image():
return "dummy_image.png"
class Account(AbstractBaseUser):
email = models.EmailField(verbose_name="email", max_length=60, unique=True)
username = models.CharField(max_length=30, unique=True)
date_joined = models.DateTimeField(verbose_name='date joined', auto_now_add=True)
last_login = models.DateTimeField(verbose_name='last login', auto_now=True)
is_admin = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
profile_image = models.ImageField(max_length=255,
upload_to=get_profile_image_filepath, null=True, blank=True, default=get_default_profile_image)
hide_email = models.BooleanField(default=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
objects = MyAccountManager()
def __str__(self):
return self.username
def get_profile_image_filename(self):
return str(self.profile_image)[str(self.profile_image).index('profile_images/' + str(self.pk) + "/"):]
# For checking permissions. to keep it simple all admin have ALL permissons
def has_perm(self, perm, obj=None):
return self.is_admin
# Does this user have permission to view this app? (ALWAYS YES FOR SIMPLICITY)
def has_module_perms(self, app_label):
return True
This is the custom user model and this will login via the email of the user .
You have to define in the settings.py for the user auth model .
In settings.py add this
AUTH_USER_MODEL = "user_app.Account"
replace the "user_app" with the app in whose models.py this model is stored .
And also you need to tell to the admin to do so .
go to admin.py of the app and add this .
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from account.models import Account
class AccountAdmin(UserAdmin) :
list_display = ("email" , "username" , "date_joined" , "last_login" , "is_admin" , "is_staff")
search_fields = ("email" , "username")
readonly_fields = ("id" , "date_joined" , "last_login")
filter_horizontal = ()
list_filter = ()
fieldsets = ()
admin.site.register(Account , AccountAdmin)
Another way of achieving the result is to take email or phone number credential and password from the user and then in the views find out the username and then log the user in..
Create a form and then create a save method to do this , or diectly do it in the views.
Like this :
def loginView(request) :
if request.POST :
email = request.POST.get("email")
password = request.POST.get("password")
user = User.objects.get(email = email)
if user.check_password(password) :
login(request, user)
return redirect("home")
I've created a model using an abstract user class model class for Online flight ticket booking. I'm new to this so haven't added many functionalities to it.
I'm sharing my model.py, admin.py, serializer.py, view.py.
My question:
In views.py -> class UserRetrtieveUpdateDestroyAPIView(generics.RetrieveUpdateDestroyAPIView)
I want to give access for GET PUT DELETE for only ADMIN and USER who created this profile(owner). I'm using postman to check endpoints.
"Please do check my abstract user model if there is any mistake".
permission for "BookViewSet" and "BookingRetrtieveUpdateDestroyAPIView" I only want ADMIN and USER owner who created this booking to view or modify it.
I only want to get list of passengers associated by that particular user in "PassengerListCreateAPIView(generics.ListCreateAPIView):"
#models.py
import email
from pyexpat import model
from django.db import models
from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth.models import (
BaseUserManager, AbstractBaseUser
)
GENDER_CHOICES = (
(0, 'male'),
(1, 'female'),
(2, 'not specified'),)
class UserManager(BaseUserManager):
def create_user(self, email, name,contact_number,gender,address,state,city,country,pincode,dob ,password=None, password2=None):
if not email:
raise ValueError('User must have an email address')
user = self.model(
email=self.normalize_email(email),
name=name,
contact_number=contact_number,
gender=gender,
address=address,
state=state,
city=city,
country=country,
pincode=pincode,
dob=dob,
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, name,contact_number,gender,address,state,city,country,pincode,dob , password=None):
user = self.create_user(
email,
name=name,
contact_number=contact_number,
gender=gender,
address=address,
state=state,
city=city,
country=country,
pincode=pincode,
dob=dob,
password=password,
)
user.is_admin = True
user.save(using=self._db)
return user
class User(AbstractBaseUser):
email = models.EmailField(verbose_name='Email',max_length=255,unique=True)
name = models.CharField(max_length=200)
contact_number= models.IntegerField()
gender = models.IntegerField(choices=GENDER_CHOICES)
address= models.CharField(max_length=100)
state=models.CharField(max_length=100)
city=models.CharField(max_length=100)
country=models.CharField(max_length=100)
pincode= models.IntegerField()
dob = models.DateField()
# is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['name','contact_number','gender','address','state','city','country','pincode','dob']
def __str__(self):
return self.email
def has_perm(self, perm, obj=None):
"Does the user have a specific permission?"
# Simplest possible answer: Yes, always
return self.is_admin
def has_module_perms(self, app_label):
"Does the user have permissions to view the app `app_label`?"
# Simplest possible answer: Yes, always
return True
#property
def is_staff(self):
"Is the user a member of staff?"
# Simplest possible answer: All admins are staff
return self.is_admin
# Create your models here.
class Airport(models.Model):
Airport_name=models.CharField(max_length=100)
country=models.CharField(max_length=100)
def __str__(self):
return self.Airport_name
class Flight(models.Model):
flight_number = models.CharField(max_length=100,unique=True)
depart_date_time = models.DateTimeField(auto_now_add=True)
arrival_date_time = models.DateTimeField(auto_now_add=True)
origin = models.CharField(max_length=100, blank=True, default='')
destination = models.CharField(max_length=100, blank=True, default='')
price = models.IntegerField()
airline_name = models.CharField(max_length=100, blank=True, default='')
total_seats = models.IntegerField()
available_seats = models.IntegerField()
airport=models.ForeignKey(Airport,on_delete=models.CASCADE)
def __str__(self):
return str(self.flight_number)
class Passenger(models.Model):
name = models.CharField(max_length=100,blank=True, default='')
contact_number= models.IntegerField()
email = models.EmailField(max_length=254)
gender = models.IntegerField(choices=GENDER_CHOICES)
age= models.IntegerField()
user=models.ForeignKey(User,on_delete=models.CASCADE)
def __str__(self):
return self.name
class Booking(models.Model):
user =models.ForeignKey(User,on_delete=models.CASCADE)
flights =models.ForeignKey(Flight,on_delete=models.CASCADE)
passenger =models.ManyToManyField(Passenger)
booking_number= models.CharField(max_length= 100,default=0, blank= True)
booking_time = models.DateTimeField(auto_now_add=True)
no_of_passengers= models.IntegerField(default=0, blank= True)
def __str__(self):
return self.booking_number
Corresponding serializer
#serializers.py
from rest_framework import serializers
from myapp.models import Airport, Flight, User, Passenger, Booking
class UserRegistrationSerializer(serializers.ModelSerializer):
# We are writing this becoz we need confirm password field in our Registratin Request
password2 = serializers.CharField(style={'input_type':'password'}, write_only=True)
class Meta:
model = User
fields=['email','name','contact_number','gender','address','state','city','country','pincode','dob','password', 'password2']
extra_kwargs={
'password':{'write_only':True}
}
# Validating Password and Confirm Password while Registration
def validate(self, attrs):
password = attrs.get('password')
password2 = attrs.get('password2')
if password != password2:
raise serializers.ValidationError("Password and Confirm Password doesn't match")
return attrs
def create(self, validate_data):
return User.objects.create_user(**validate_data)
class UserLoginSerializer(serializers.ModelSerializer):
email = serializers.EmailField(max_length=255)
class Meta:
model = User
fields = ['email', 'password']
# class UserProfileSerializer(serializers.ModelSerializer):
# class Meta:
# model = User
# fields = '__all__'
class AirportSerializer(serializers.ModelSerializer):
class Meta:
model = Airport
fields = '__all__'
class FlightSerializer(serializers.ModelSerializer):
class Meta:
model = Flight
fields = '__all__'
class UserSerializer(serializers.ModelSerializer):
class Meta:
model= User
fields = '__all__'
class PassengerSerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=False)
class Meta:
model= Passenger
fields = '__all__'
class BookingSerializer(serializers.ModelSerializer):
class Meta:
model= Booking
fields = '__all__'
#views.py
from django.shortcuts import render
from django.http import Http404, JsonResponse
#from django.http import HttpResponse, JsonResponse
from rest_framework.views import APIView
from rest_framework.response import Response
#from rest_framework.parsers import JSONParser
from django.contrib.auth import authenticate
from rest_framework_simplejwt.tokens import RefreshToken
from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import SAFE_METHODS, BasePermission,IsAuthenticatedOrReadOnly,IsAuthenticated, IsAdminUser, DjangoModelPermissionsOrAnonReadOnly, DjangoModelPermissions
from myapp.renderers import UserRenderer
from rest_framework import status
from rest_framework import permissions
from rest_framework import generics
from myapp.models import Airport, Flight, User, Passenger, Booking
from myapp.serializers import *
from myapp.permissions import IsOwnerOrAdmin
from rest_framework import viewsets
def get_tokens_for_user(user):
refresh = RefreshToken.for_user(user)
return {
'refresh': str(refresh),
'access': str(refresh.access_token),
}
# Create your views here.
class UserRegistrationView(APIView):
renderer_classes = [UserRenderer]
def post(self, request, format=None):
serializer = UserRegistrationSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.save()
token = get_tokens_for_user(user)
return Response({'token':token, 'msg':'Registration Successful'}, status=status.HTTP_201_CREATED)
class UserLoginView(APIView):
renderer_classes = [UserRenderer]
def post(self, request, format=None):
serializer = UserLoginSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
email = serializer.data.get('email')
password = serializer.data.get('password')
user = authenticate(email=email, password=password)
if user is not None:
token = get_tokens_for_user(user)
return Response({'token':token, 'msg':'Login Success'}, status=status.HTTP_200_OK)
else:
return Response({'errors':{'non_field_errors':['Email or Password is not Valid']}}, status=status.HTTP_404_NOT_FOUND)
class UserProfileView(APIView):
renderer_classes = [UserRenderer]
permission_classes = [IsAuthenticated]
def get(self, request, format=None):
serializer = UserSerializer(request.user)
return Response(serializer.data, status=status.HTTP_200_OK)
class UserListCreateAPIView(generics.ListCreateAPIView):
permission_classes = [IsUserOrIsAdmin]
queryset = User.objects.all()
serializer_class = UserSerializer
class UserRetrtieveUpdateDestroyAPIView(generics.RetrieveUpdateDestroyAPIView):
permission_classes = [IsUserOrIsAdmin]
queryset = User.objects.all()
serializer_class = UserSerializer
class FlightListCreateAPIView(generics.ListCreateAPIView):
permission_classes = [IsAdminUser | IsAuthenticatedOrReadOnly]
queryset = Flight.objects.all()
serializer_class = FlightSerializer
class FlightRetrtieveUpdateDestroyAPIView(generics.RetrieveUpdateDestroyAPIView):
permission_classes = [IsAdminUser]
queryset = Flight.objects.all()
serializer_class = FlightSerializer
class AirportListCreateAPIView(generics.ListCreateAPIView):
permission_classes = [IsAdminUser | IsAuthenticatedOrReadOnly]
queryset = Airport.objects.all()
serializer_class = AirportSerializer
class AirportRetrtieveUpdateDestroyAPIView(generics.RetrieveUpdateDestroyAPIView):
permission_classes = [IsAdminUser]
queryset = Airport.objects.all()
serializer_class = AirportSerializer
class PassengerListCreateAPIView(generics.ListCreateAPIView):
permission_classes = [IsUserOrIsAdminPassenger]
queryset = Passenger.objects.all()
serializer_class = PassengerSerializer
class PassengerRetrtieveUpdateDestroyAPIView(generics.RetrieveUpdateDestroyAPIView):
permission_classes = [IsUserOrIsAdminPassenger]
queryset = Passenger.objects.all()
serializer_class = PassengerSerializer
class BookingRetrtieveUpdateDestroyAPIView(generics.RetrieveUpdateDestroyAPIView):
permission_classes = [IsUserOrIsAdmin]
# queryset = Booking.objects.all()
# serializer_class = BookingSerializer
def get_queryset(self):
if self.request.user.is_staff == False:
user_data= self.request.user
book = Booking.objects.filter(user= user_data)
return book
else:
book = Booking.objects.all()
return book
serializer_class = BookingSerializer
class BookViewSet(viewsets.ModelViewSet):
permission_classes = [IsUserOrIsAdmin]
# queryset = Book.objects.all()
serializer_class = BookingSerializer
# print(serializer_class)
def get_queryset(self):
if self.request.user.is_staff == False:
user_data= self.request.user
book = Booking.objects.filter(user= user_data)
return book
else:
book = Booking.objects.all()
return book
# book = Booking.objects.all()
def create(self, request, *args, **kwargs):
data = request.data
user = User.objects.get(id= request.user.pk)
# user = User.objects.get(id=data["user"])
flightdetails = Flight.objects.get(id=data["flights"])
# bookingdetails = Booking.objects.get(no_of_passengers=data["no_of_passengers"])
new_book = Booking.objects.create(
booking_number= data["booking_number"],
no_of_passengers= data["no_of_passengers"],
user=user,
flights=flightdetails,
)
new_book.save()
for passenger in data["passenger"]:
passenger_book= Passenger.objects.create(
user = user,
name= passenger["name"],
contact_number = passenger["contact_number"],
email = passenger["email"],
gender = passenger["gender"],
age = passenger["age"]
)
new_book.passenger.add(passenger_book)
if flightdetails.available_seats < len(data["passenger"]):
return Response({"data": "No seats available", "status": status.HTTP_400_BAD_REQUEST})
update_seats = flightdetails.available_seats - data["no_of_passengers"]
flightdetails.available_seats = update_seats
flightdetails.save()
serializers = BookingSerializer(new_book)
return Response({"data": serializers.data, "status": status.HTTP_201_CREATED})
#urls.py
from django.urls import path, include
from myapp.views import *
from rest_framework import routers
router = routers.DefaultRouter()
router.register('booking', BookViewSet, basename='MyModel')
urlpatterns = [
path('register/', UserRegistrationView.as_view(), name='register'),
path('login/', UserLoginView.as_view(), name='login'),
path('profile/', UserProfileView.as_view(), name='profile'),
path('flight/', FlightListCreateAPIView.as_view()),
path('flight_info/<int:pk>/', FlightRetrtieveUpdateDestroyAPIView.as_view()),
path('customer/', UserListCreateAPIView.as_view()),
path('customer_info/<int:pk>/', UserRetrtieveUpdateDestroyAPIView.as_view()),
path('passenger/', PassengerListCreateAPIView.as_view()),
path('passenger_info/<int:pk>/', PassengerRetrtieveUpdateDestroyAPIView.as_view()),
path('booking_info/<int:pk>/', BookingRetrtieveUpdateDestroyAPIView.as_view()),
#path('booking/', BookingAPIView.as_view()),
path('', include(router.urls)),
]
You'll need to create a custom permission for this. Since the permission is dependent on the User instance, you'll want to implement has_object_permission(), returning True if the user is_staff or if the user is the same as the User they're trying to access:
from django.contrib.auth import get_user_model
from rest_framework.permissions import BasePermission
User = get_user_model()
class IsUserOrIsAdmin(BasePermission):
"""Allow access to the respective User object and to admin users."""
def has_object_permission(self, request, view, obj):
return (request.user and request.user.is_staff) or (
isinstance(obj, User) and request.user == obj
)
You can then set your view's permission_classes to your custom permission:
class UserRetrtieveUpdateDestroyAPIView(generics.RetrieveUpdateDestroyAPIView):
permission_classes = [IsUserOrIsAdmin]
queryset = User.objects.all()
serializer_class = UserSerializer
I would like to know if it’s possible to create his own login page (from scratch) without using any Django default login forms because I want to add other fields in addition?
Thanks in advance
You could write your own view, but it's better to just subclass the Django LoginView and change as much as you need, for example:
from django.http import HttpResponseRedirect
from django.contrib.auth.views import LoginView
from django.contrib.auth import login
from .forms import MyCustomLoginForm
class SignInView(LoginView):
form_class = MyCustomLoginForm
template_name = 'path/to/my_template.html'
def form_valid(self, form):
# Form is valid, do whatever you need.
login(self.request, form.get_user())
response = HttpResponseRedirect(self.get_success_url())
return response
forms.py
from django import forms
from django.contrib.auth.forms import AuthenticationForm
class MyCustomLoginForm(AuthenticationForm):
age = forms.IntegerField(label='Age', required=True)
def clean_age(self):
age = self.cleaned_data['age']
# validate age
return age
in this user model, I have declared username field as email. User can't get login using username and password. User have to provide email and password to get login.
my models.py
class UserManager(BaseUserManager):
def create_user(self, email, password=None, active=True, is_staff=False, is_admin=False,is_superuser=False):
if not email:
raise ValueError("Users must have an email address")
if not password:
raise ValueError("Users must have password")
user_obj = self.model(
email=self.normalize_email(email)
)
user_obj.set_password(password)
user_obj.staff = is_staff
user_obj.admin = is_admin
user_obj.active = active
user_obj.superuser = is_superuser
user_obj.save(using=self._db)
return user_obj
def create_superuser(self, email, password=None):
user = self.create_user(
email,
password=password,
is_superuser=True,
is_staff=True,
is_admin=True,
)
return user
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(max_length=255, unique=True)
staff = models.BooleanField(default=False)
superuser = models.BooleanField(default=False)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = UserManager()
def _str_(self):
return self.email
#property
def is_staff(self):
return self.staff
#property
def is_superuser(self):
return self.superuser
my views.py
from .models import User
class LoginAPIView(APIView):
def post(self, request):
serializer = LoginSerializers(data=request.data)
if serializer.is_valid():
data = serializer.data
email = data['email']
password = data['password']
user = authenticate(email=email, password=password)
if user is not None:
login(request, user)
token, created = Token.objects.get_or_create(user=user)
return Response({"message": "success", "code": status.HTTP_201_CREATED, "details": serializer.data,
"Token": token.key})
return Response(
{"message": "error", "code": status.HTTP_401_UNAUTHORIZED, "details": ["Invalid credentials"]})
my serializers.py:
class LoginSerializers(serializers.Serializer):
username = serializers.CharField(max_length=255)
password = serializers.CharField(max_length=128)
may be this could help you
I am trying to implement registration in Django and faced with error:
models.py", line 20, in user_registrated_dispatcher
send_activation_notification(kwargs['instance'])
KeyError: 'instance'
I submitted registration form then received this error message.
How can I solve the error?
Could you recommend good tutorial about registration with email in Django?
Full code of the application located here (branch Login-version-2-site ): https://github.com/mascai/reg_app/tree/Login-version-2-site
models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.dispatch import Signal
from .utilities import send_activation_notification
class AdvUser(AbstractUser):
is_activated = models.BooleanField(default=True, db_index=True,
verbose_name='Пpoшeл активацию?')
send_messages = models.BooleanField(default=True, verbose_name='Слать оповещения о новых комментариях?')
class Meta(AbstractUser.Meta):
pass
user_registrated = Signal(providing_args=['instance'])
def user_registrated_dispatcher(sender, **kwargs):
send_activation_notification(kwargs['instance'])
user_registrated.connect(user_registrated_dispatcher)
forms.py
class RegisterUserForm(forms.ModelForm):
email = forms.EmailField(required=True, label="Email address")
password1 = forms.CharField(label="Password", widget=PasswordInput,
help_text=password_validation.password_validators_help_text_html())
password2 = forms.CharField(label="Password", widget=PasswordInput,
help_text=password_validation.password_validators_help_text_html())
def clean_password1(self):
password1 = self.cleaned_data['password1']
if password1:
password_validation.validate_password(password1)
return password1
def clean(self):
super().clean()
password1 = self.cleaned_data.get('password1')
password2 = self.cleaned_data.get('password2')
if password1 and password2 and password1 != password2:
errors = {'password2': ValidationError('Введённые пароли не совпадают', code='password_mismatch')}
raise ValidationError(errors)
else:
return self.cleaned_data
def save(self, commit=True):
user = super().save(commit=False)
user.set_password(self.cleaned_data['password1'])
user.is_active = False
user.is_activated = False
if commit:
user.save()
user_registrated.send(RegisterUserForm, isinstance=user)
return user
class Meta:
model = AdvUser
fields = ('username', 'email', 'password', 'password2',
'first_name', 'last_name', 'send_messages')
urls.py
from .views import BBLoginView, BBLogoutView, profile, RegisterUserView, RegisterDoneView, user_activate
urlpatterns = [
path('', views.post_list, name='post_list'),
path('accounts/login/', BBLoginView.as_view(), name='login'),
path('accounts/profile/', profile, name='profile'),
path('accounts/logout/', BBLogoutView.as_view(), name='logout'),
path('accounts/register/done', RegisterDoneView.as_view(), name='register_done'),
path('accounts/register/', RegisterUserView.as_view(), name='register'),
path('accounts/register/activate/<str:sign>/', user_activate, name='register_activate'),
]
views.py
def post_list(request):
return render(request, 'blog/post_list.html', {})
class BBLoginView(LoginView):
template_name = 'blog/login.html'
class BBLogoutView(LoginRequiredMixin, LogoutView):
template_name = 'blog/logout.html'
#login_required
def profile(request):
return render(request, 'blog/profile.html')
class RegisterUserView(CreateView):
model = AdvUser
template_name = 'blog/register_user.html'
form_class = RegisterUserForm
success_url = '/register_done'
class RegisterDoneView(TemplateView):
template_name = "blog/register_done.html"
def user_activate(request, sign):
try:
username = signer.unsign(sign)
except:
return render(request, 'blog/bad_signature.html')
user = get_object_or_404(AdvUser, username=username)
if user.is_activated:
template = 'blog/user_is_activated.html'
else:
template = 'blog/activation_done.html'
user.is_active = True
user.is_activated = True
user.save()
return render(request, template)
You passed isinstance=user instead of instance=user when you send the signal inside the save method.
(Note, all this seems like a lot of work. There's already a general post_save signal you could use; but actually there doesn't seem to be much reason to use a signal at all. Why not just send the notification directly?
I started using cookiecutter-django because it seems so much advanced than just django-admin to start my project. So I created an eCommerce website and it requires only email to log in not the username.
So, I tried to follow the docs and changes my settings.py like this:
ACCOUNT_USER_MODEL_USERNAME_FIELD = None
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_USERNAME_REQUIRED = False
ACCOUNT_AUTHENTICATION_METHOD = 'email'
ACCOUNT_EMAIL_VERIFICATION = 'mandatory'
AUTH_USER_MODEL = 'accounts.User'
LOGIN_REDIRECT_URL = 'users:redirect'
LOGIN_URL = 'account_login'
Here is my accounts.User model:
from django.db import models
from django.contrib.auth.models import (
AbstractBaseUser, BaseUserManager
)
class UserManager(BaseUserManager):
def create_user(self, email, full_name, password=None, is_active=True, is_staff=False, is_admin=False):
if not email:
raise ValueError("Users must have an email address")
if not password:
raise ValueError("Users must have a password")
if not full_name:
raise ValueError("Users must have a fullname")
user_obj = self.model(
email = self.normalize_email(email),
full_name = full_name
)
user_obj.set_password(password)
user_obj.staff = is_staff
user_obj.admin = is_admin
user_obj.active = is_active
user_obj.save(using=self._db)
return user_obj
def create_staffuser(self, email, full_name, password=None):
user = self.create_user(
email,
full_name,
password=password,
is_staff=True
)
return user
def create_superuser(self, email, full_name, password=None):
user = self.create_user(
email,
full_name,
password=password,
is_staff=True,
is_admin=True
)
return user
class User(AbstractBaseUser):
email = models.EmailField(max_length=255, unique=True)
full_name = models.CharField(max_length=255, blank=True, null=True)
active = models.BooleanField(default=True)
staff = models.BooleanField(default=False)
admin = models.BooleanField(default=False)
USERNAME_FIELD = 'email'
REQUIRED_FIELD = ['full_name']
objects = UserManager()
def __str__(self):
return self.email
def get_full_name(self):
return self.full_name
def get_short_name(self):
return self.email
def has_perm(self, perm, obj=None):
return True
def has_module_perms(self, app_label):
return True
#property
def is_staff(self):
return self.staff
#property
def is_admin(self):
return self.admin
#property
def is_active(self):
return self.active
There is default users dir which has its own models.py, views.py and urls.py but I have no knowledge to modify it.
urls.py:
from django.conf.urls import url
from . import views
urlpatterns = [
url(
regex=r'^$',
view=views.UserListView.as_view(),
name='list'
),
url(
regex=r'^~redirect/$',
view=views.UserRedirectView.as_view(),
name='redirect'
),
url(
regex=r'^(?P<username>[\w.#+-]+)/$',
view=views.UserDetailView.as_view(),
name='detail'
),
url(
regex=r'^~update/$',
view=views.UserUpdateView.as_view(),
name='update'
),
]
models.py:
from django.contrib.auth.models import AbstractUser
from django.core.urlresolvers import reverse
from django.db import models
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _
#python_2_unicode_compatible
class User(AbstractUser):
# First Name and Last Name do not cover name patterns
# around the globe.
name = models.CharField(_('Name of User'), blank=True, max_length=255)
def __str__(self):
return self.username
def get_absolute_url(self):
return reverse('users:detail', kwargs={'username': self.username})
views.py:
from django.core.urlresolvers import reverse
from django.views.generic import DetailView, ListView, RedirectView, UpdateView
from django.contrib.auth.mixins import LoginRequiredMixin
from .models import User
from enrolments.models import Enrolment
class UserDetailView(LoginRequiredMixin, DetailView):
model = User
# These next two lines tell the view to index lookups by username
slug_field = 'username'
slug_url_kwarg = 'username'
class UserRedirectView(LoginRequiredMixin, RedirectView):
permanent = False
def get_redirect_url(self):
return reverse('users:detail',
kwargs={'username': self.request.user.username})
class UserUpdateView(LoginRequiredMixin, UpdateView):
fields = ['name', ]
# we already imported User in the view code above, remember?
model = User
# send the user back to their own page after a successful update
def get_success_url(self):
return reverse('users:detail',
kwargs={'username': self.request.user.username})
def get_object(self):
# Only get the User record for the user making the request
return User.objects.get(username=self.request.user.username)
class UserListView(LoginRequiredMixin, ListView):
model = User
# These next two lines tell the view to index lookups by username
slug_field = 'username'
slug_url_kwarg = 'username'
Here is the error when I sign in, although it let me sign in when I go to the homepage. but when I click 'Sign In' button it prompts me with this error. Please guide me through it.
Thank you in advance.
As of now (June 2020) there is an easier way to do this with Django Cookie cutter.
Firstly in the Django settings config/settings/base.py we need to adjust the AllAuth settings so that firstly the email is used as the primary identifier and not username, and secondly so that the username field is hidden in the SignUp page
ACCOUNT_AUTHENTICATION_METHOD = "email"
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_USERNAME_REQUIRED = False
ACCOUNT_EMAIL_VERIFICATION = "mandatory"
The Django AllAuth documentation of this can be found here
Then in the User model, we add a signal that will fire whenever the user instance is updated and copy the email address over to the username field. This way they stay in sync. The while loop just deals with the situation if there are conflicting email addresses (which their should not be):
#receiver(pre_save, sender=User)
def update_username_from_email(sender, instance, **kwargs):
user_email = instance.email
username = user_email[:130]
n = 1
while User.objects.exclude(pk=instance.pk).filter(username=username).exists():
n += 1
username = user_email[: (129 - len(str(n)))] + "-" + str(n)
instance.username = username
This user model update was proposed by #ferrangb here
Finally, I managed to figure out the way.
Here is my new models.py:
from django.db import models
from django.contrib.auth.models import (
AbstractBaseUser, BaseUserManager
)
class UserManager(BaseUserManager):
def create_user(self, email, username, full_name, password=None, is_active=True, is_staff=False, is_admin=False):
if not email:
raise ValueError("Users must have an email address")
if not password:
raise ValueError("Users must have a password")
if not full_name:
raise ValueError("Users must have a fullname")
user_obj = self.model(
email = self.normalize_email(email),
full_name = full_name,
username = new_username
)
user_obj.set_password(password)
user_obj.staff = is_staff
user_obj.admin = is_admin
user_obj.active = is_active
user_obj.save(using=self._db)
return user_obj
def create_staffuser(self, username, email, full_name, password=None):
user = self.create_user(
email,
full_name,
username=username,
password=password,
is_staff=True
)
return user
def create_superuser(self, username, email, full_name, password=None):
user = self.create_user(
email,
full_name,
username=username,
password=password,
is_staff=True,
is_admin=True
)
return user
class User(AbstractBaseUser):
email = models.EmailField(max_length=255, unique=True)
full_name = models.CharField(max_length=255, blank=True, null=True)
username = models.CharField(max_length=255, blank=True, null=True)
active = models.BooleanField(default=True)
staff = models.BooleanField(default=False)
admin = models.BooleanField(default=False)
USERNAME_FIELD = 'email'
REQUIRED_FIELD = ['full_name']
objects = UserManager()
def __str__(self):
return self.email
def get_full_name(self):
return self.full_name
def get_short_name(self):
return self.email
def get_username(self):
return self.username
def has_perm(self, perm, obj=None):
return True
def has_module_perms(self, app_label):
return True
#property
def is_staff(self):
return self.staff
#property
def is_admin(self):
return self.admin
#property
def is_active(self):
return self.active
Here is my views.py file in 'users' dir:
from django.core.urlresolvers import reverse
from django.views.generic import DetailView, ListView, RedirectView, UpdateView
from django.contrib.auth.mixins import LoginRequiredMixin
from accounts.models import User
from enrolments.models import Enrolment
class UserDetailView(LoginRequiredMixin, DetailView):
model = User
# These next two lines tell the view to index lookups by username
slug_field = 'username'
slug_url_kwarg = 'username'
class UserRedirectView(LoginRequiredMixin, RedirectView):
permanent = False
def get_redirect_url(self):
return reverse('users:detail',
kwargs={'username': self.request.user.username})
class UserUpdateView(LoginRequiredMixin, UpdateView):
fields = ['full_name', ]
# we already imported User in the view code above, remember?
model = User
# send the user back to their own page after a successful update
def get_success_url(self):
return reverse('users:detail',
kwargs={'username': self.request.user.username})
def get_object(self):
# Only get the User record for the user making the request
return User.objects.get(username=self.request.user.username)
class UserListView(LoginRequiredMixin, ListView):
model = User
# These next two lines tell the view to index lookups by username
slug_field = 'username'
slug_url_kwarg = 'username'
and my settings.py file:
# ACCOUNT_USER_MODEL_USERNAME_FIELD = None
ACCOUNT_EMAIL_REQUIRED = True
# ACCOUNT_USERNAME_REQUIRED = False
ACCOUNT_AUTHENTICATION_METHOD = 'email'
ACCOUNT_EMAIL_VERIFICATION = 'mandatory'
AUTH_USER_MODEL = 'accounts.User'
LOGIN_REDIRECT_URL = 'users:redirect'
LOGIN_URL = 'account_login'
Although, I need to have username while signing up only but after that, it just works. I don't need to change any other files.
I hope this helps.
Thanks.