I just added a feature to be able to register with Google. For security reasons I added a field in the db that is false if the user is registered the usal way(Username & PW) and true if he is registered with google.
class CustomUser(AbstractUser):
objects = UserManager()
REQUIRED_FIELDS = []
USERNAME_FIELD = 'email'
username = models.CharField(max_length=40, unique=False,null=True, blank=True)
email = models.EmailField(_('email address'), unique=True)
profile_image = models.ImageField(null=True,blank=True,default = "users.png",upload_to='',)
created = models.DateTimeField(auto_now_add=True)
id = models.UUIDField(default=uuid.uuid4, unique=True,primary_key=True, editable=False)
telephone = models.CharField(blank=True, max_length=20, null=True)
email_is_verified = models.BooleanField(default = False, null=False, blank = False)
currentVerificationToken = models.CharField(max_length = 6, default="000000")
withGoogleRegistered = models.BooleanField(default = False)
def __str__(self):
return self.email
So if you try to login the usal way but the email adress is bounded to an google registered account, you wont be able to. If you try to login with a usal way bounded email adress, but its connected to a google bounded account , you wont be able to. So, I made this in order to seperate betweena accounts with a password and accounts without a password... But there is a little problem now. In Order to obtain an refresh token (JWT) I have to give a username and a password, otherwise I wont be able to get one in the first place...
function fetchToken() {
if (user && !Object.hasOwn(user, "fromBackend")) {
fetch("http://127.0.0.1:8000/token/", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
email: user.email,
password: user.password,
}),
})
.then((response) => response.json())
.then((data) => {
if (data.access) {
setRemoveTokens(data);
updateUser();
}
});
} else if (localStorage.getItem("refreshToken-allkids") != null) {
getNewTokens();
updateUser();
}
For this I am using rest_framework_simplejw
from django.contrib import admin
from django.urls import path, include
from rest_framework_simplejwt.views import (
TokenObtainPairView,
TokenRefreshView,
)
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
path("user/", include("User.urls")),
path("token/", TokenObtainPairView.as_view(), name="obtain_token"),
path("token/refresh/", TokenRefreshView.as_view(), name="refresh_token"),
]
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
So that beeing said, lets move on to the question: how can I obtain a refresh token for Google registered Accounts without having a Password?
Here is the way I create a google registered account:
#api_view(["POST"])
def googleRegistrationOrLogin(request):
body = request.body.decode('utf-8')
token = json.loads(body)
CLIENT_ID = "591603096190-1fv6asqdpmm6td8t66as5temri420j8b.apps.googleusercontent.com"
try:
idinfo = id_token.verify_oauth2_token(token, requests.Request(), CLIENT_ID)
except:
print("Failure")
return Response("Failure")
try:
user = CustomUser.objects.get(email = idinfo["email"])
if user.withGoogleRegistered:
login(request, user)
user = CustomUserLoggedSerializer(user, many = False)
return Response(user.data)
else:
return Response("Email already in use, please create an account the usal way.")
except:
randomEmailToken = random.randrange(100000,999999)
try:
user = CustomUser.objects.create(username = idinfo["name"],profile_image = idinfo["picture"],withGoogleRegistered = True,password="GoogleRegistered", email = idinfo["email"],currentVerificationToken=randomEmailToken, email_is_verified = True if idinfo["email_verified"] else False)
except:
return Response("Fatal Error")
user = CustomUserLoggedSerializer(user, many = False)
return Response(user.data)
I know its currently not clean, but fair enough for now.
Related
I have two models User and Profile which is related by One to One relation. I have registered users who can login and if profile is not created for user, then user can create one profile (using POST) and if profile is created then user can update their profile (using PATCH).
Models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True, related_name='profile')
locality = models.CharField(max_length=70, null=True, blank=True)
city = models.CharField(max_length=70, null=True, blank=True)
address = models.TextField(max_length=300, null=True, blank=True)
pin = models.IntegerField(null=True, blank=True)
state = models.CharField(max_length=70, null=True, blank=True)
profile_image = models.ImageField(upload_to='user_profile_image', blank=True, null=True)
Serializer.py
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model= Profile
fields = ['user', 'locality','city','address','pin','state','profile_image']
Views.py
class UserProfileDataView(APIView):
renderer_classes = [UserRenderer]
permission_classes = [IsAuthenticated]
def get(self, request, format=None):
serializer = ProfileSerializer(request.user.profile, context={'request': request})
return Response(serializer.data, status=status.HTTP_200_OK)
def post(self, request, format=None):
serializer = ProfileSerializer(data= request.data, context={'user': request.user})
serializer.is_valid(raise_exception=True)
serializer.save()
return Response ({ 'msg':'Data Updated Successfully'},status=status.HTTP_201_CREATED
)
def patch(self, request, format=None):
item = Profile.objects.get(user = request.user)
serializer = ProfileSerializer(item ,data = request.data, partial=True, context={'user': request.user.profile})
serializer.is_valid(raise_exception=True)
serializer.save()
return Response({'msg':'Profile Updated Successfull'}, status = status.HTTP_200_OK)
API using Redux Toolkit
editProfile: builder.mutation({
query: (access_token, actualData ) => {
return {
url:'editprofile/',
method:'PATCH',
body:actualData,
headers:{'authorization' : `Bearer ${access_token}`, 'Content-type':'application/json'}
}
}
}),
createProfile: builder.mutation({
query: (access_token, actualData ) => {
return {
url:'createprofile/',
method:'POST',
body:actualData,
headers:{'authorization' : `Bearer ${access_token}`, 'Content-type':'application/json'}
}
}
}),
})
})
When I create profile from django admin for a user and tries to update its data, then I only get {"msg": "Profile Updated Successfull"} in network console but data is not reflected in database. and also when I try to create profile of user from Fronend using React, it gives error msg {"errors": {"user": ["This field is required."]}}
I think you missed setting the user_id field in the request payload. In the ProfileSerializer, you need to change the user field into user_id.
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model= Profile
fields = ['user_id', 'locality','city','address','pin','state','profile_image']
extra_kwargs = {
'user_id': {'source': 'user'}
}
And in the frontend, you need to set the user_id attribute in the actual_data object.
Or in the backend, you can add that field manually in the post method of the UserProfileDataView class.
class UserProfileDataView(APIView):
...
def post(self, request, format=None):
input_data = request.data
input_data['user_id'] = request.user.id
serializer = ProfileSerializer(data= input_data, context={'user': request.user})
serializer.is_valid(raise_exception=True)
serializer.save()
return Response ({ 'msg':'Data Updated Successfully'},status=status.HTTP_201_CREATED
)
I'm new to React and Django rest framework. I want to insert profile data into the Django model using fetch API in react. I'm continuously getting response header as:
{"user":["Incorrect type. Expected pk value, received str."]}
I've checked by printing response on console, and it gives status code '200 OK'. But it didn't update the database as well.
My submit form function in react is:
const handleSubmit = (e) => {
e.preventDefault();
const profile = profileObj(selectedProfileImg, contact, city, country, address);
localStorage.setItem('profile', JSON.stringify(profile))
let form_data = new FormData()
// *************************
// this is the foreign key in the model and it gives the problem.
// *************************
form_data.append('user',JSON.parse(localStorage.getItem('data')).id) // (foriegn key value) User added by signing up
form_data.append('profile_img', profile.prof_img)
form_data.append('contact',profile.contact)
form_data.append('city',profile.city)
form_data.append('country',profile.country)
form_data.append('address',profile.address)
form_data.append('store_title','storename') // (foriegn key value) Data with this key exists in database
form_data.append('cus_status',profile.cus_status)
// *********************************
// Also I want to know what the boundary means in content
// type. As I see it on google so I used it but removing it
// gives another boundary error.
// *********************************
fetch('http://localhost:8000/customer_apis/addCustomer/', {
method: 'POST',
headers: {
'Content-Type': 'multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW'
},
body: form_data
})
.then(res => {
console.log(res)
console.log(res.status)
if (res.status !== 200)
document.getElementById('text-error').innerHTML = res.statusText
else {
navigate('/create_store')
}
})
}
My Django model is:
class CustomerData(models.Model):
CUS_STATUS=(
('A','Active'),
('B','Blocked'),
('X','Blacklist')
)
# I imported the user as (from django.contrib.auth.models import User)
user=models.ForeignKey(User, on_delete=models.CASCADE)
store_title=models.ForeignKey(StoreData, on_delete=models.CASCADE, null=True, default='')
city=models.CharField(max_length=50, default="")
country=models.CharField(max_length=50, default="")
address=models.CharField(max_length=200, default="")
phone=models.IntegerField(default=00)
profile_img=models.ImageField(upload_to=user_directory_path, blank=True,null=True)
cus_status=models.CharField(max_length=20,choices=CUS_STATUS, default='A')
def __str__(self):
return str(self.store_title)
And Django API view is:
#api_view(['POST','GET'])
def addCustomer(request):
serializer = CustomerSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response("Success")
else:
return Response(serializer.errors)
CustomerSerializer is:
class CustomerSerializer(serializers.ModelSerializer):
class Meta:
model = CustomerData
fields='__all__'
How could I add data to the child table having foreign keys from React Apis to Django rest Framework?
Any help will be really appreciated.
I think you need to use some other field for uploading user and store_title data.
class CustomerSerializer(serializers.ModelSerializer):
user_id = serializers.IntegerField(write_only = True)
store_title_id = serializers.IntegerField(write_only = True)
user = UserSerializer(read_only = True)
store_title = StoreTitleSerializer(read_only = True)
class Meta:
model = CustomerData
fields=("user", "store_title", "city", "country", "address", "phone", "profile_img", "cus_status", "user_id", "store_title_id", )
And in frontend, you can upload user_id and store_title_id as the integer value.
const handleSubmit = (e) => {
...
form_data.append('user_id', parseInt(JSON.parse(localStorage.getItem('data')).id, 10))
...
form_data.append('store_title_id', 1) # for example
...
Update 2, 13th Jan: After doing some bug searching and trying to post the object directly in the root API using json, I've come to the realisation that the image is what's giving me the posting error.
I used the HTML form to post an object and it gave me this error:
TypeError at /rats/
'Image' object is not callable
For context, I uploaded an image. Here are my serialisers for creating the object (rat) and for images:
class ImageSerializer(FlexFieldsModelSerializer):
image = VersatileImageFieldSerializer(
sizes='rat_headshot'
)
class Meta:
model = Image
fields = ['name', 'image']
class RatSerializer(FlexFieldsModelSerializer):
user = serializers.CharField(source='user.username', required=False)
userid = serializers.CharField(source='user.id', required=False)
body_colour = BodyColourSerializer()
eye_colour = EyeColourSerializer()
image = ImageSerializer(required=False)
class Meta:
model = rat
exclude = ['bio']
def create(self, data):
request = self.context.get("request")
print("I was here", data, request)
return rat.objects.create(
name = data["name"],
body_colour = BodyColour(name=data["body_colour"]["name"]),
eye_colour = EyeColour(name=data["eye_colour"]["name"]),
# bio = data["bio"],
image = Image(name=data["image"]["name"])(required=False),
user = request.user,
)
I've updated the title. Original title was "Can't manage to post an object via axios to django - possibly due to AnonymousUser object?", now it's " "'Image' object is not callable" when posting API object "
Update 8th Jan: I fixed the AnonymousUser error. Django wanted an ID but was instead getting the user class, I fixed it by changing user = request.user into user = request.user.id
However, I'm still unable to post the object. I'm getting the same res.data error as below. So now I'm not sure what's causing the error.
I'm trying to add a button in React which posts an object to django via axios when a user clicks on it. However, it seems like something's wrong backend.
Here's the button:
<button
id="add-rat"
type="button"
className="btn homeButton"
onClick={
(e) => submit(e)
}
>
Add rat
</button>
And here's the axios, in the same page:
const submit = (e) => {
const name = "namee";
const eyeColour = "Red";
const bodyColour = "White";
const bio = "hfff";
const image = "lineart.PNG";
const data = {
name: name,
eye_colour: eyeColour,
body_colour: bodyColour,
bio: bio,
image: image,
};
e.preventDefault();
console.log(data);
const token = localStorage.getItem("token");
axios
.post("http://127.0.0.1:8000/api/addObject", data, {
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
})
.then((res) => {
console.log(res.data);
})
.catch((err) => console.log(err));
};
This is my console output:
{name: 'namee',
eye_colour: 'Red', body_colour: 'White', bio: 'hfff', image: 'lineart.PNG'}
myRats.js:86 {res: 'Error Accured'}
(myRats.js:86 btw, is console.log(res.data); )
Here's my view for the object:
class AddRat(APIView):
def post(self,request):
data = request.data
user = request.user
print(data)
try:
user = rat( name = data['name'] , body_colour = data['bodyColour'] , eye_colour = data['eyeColour'],user= user, bio = data['bio'] , image = data['image'])
user.save()
return Response({'res':"Rat Saved Successfully"})
except:
return Response({'res':"Error Accured"})
def get(self,request):
user = request.user
data = rat.objects.filter(user = user)
data = RatSerializer(data, many = True)
return Response({'data':data.data})
When I go to the url it's posting to, I get this error:
TypeError at /api/addObject
Field 'id' expected a number but got <django.contrib.auth.models.AnonymousUser object at 0x0000014641FD85B0>.
Could it possibly be due to that? What could be wrong?
The Anonymous User issue was solved by changing user = request.user into user = request.user.id
And my second issue, "'Image' object is not callable" was due to me putting (required=False) in another set of parenthesis, as if I was calling an Image as a function.
So I changed
image = Image(name=data["image"]["name"])(required=False)
into
image = Image(name=data["image"]["name"], required=False)
I am very new to Django Framework, I am trying to use Custom user model and Custom authentication for my project.
please find below code for Create Custom Model and Custom authentication.
Custom User Model
class User(AbstractUser):
userID = models.AutoField(primary_key=True)
Employee = models.ForeignKey(EMP, on_delete=models.CASCADE, null=True)
User Serializer
class ValidateUserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('Employee', 'first_name', 'last_name',
'email', 'username', 'password')
def create(self, validated_data):
User = get_user_model()
Employee = validated_data.pop('Employee')
first_name = validated_data.pop('first_name')
last_name = validated_data.pop('last_name')
email = validated_data.pop('email')
username = validated_data.pop('username')
password = validated_data.pop('password')
return User.objects.create(Employee =Employee ,
first_name=first_name, last_name=last_name, email=email, username=username, password=None, **validated_data)
Views.py
#api_view(['GET', 'POST'])
def users(request):
if request.method == 'GET':
users = Users.objects.all()
print(users.query)
data = UserTempSerializer(users, many=True).data
return JsonResponse(data, safe=False)
elif request.method == 'POST':
users_data = JSONParser().parse(request)
user_serilizer = ValidateUserSerializer(data=users_data)
if user_serilizer.is_valid():
user_serilizer.save()
return JsonResponse(user_serilizer.data, status=status.HTTP_201_CREATED)
print(user_serilizer.errors)
return JsonResponse(user_serilizer.errors, status=status.HTTP_400_BAD_REQUEST)
Custom authentication Code Here:
class CustomAuthToken(ObtainAuthToken):
def post(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data,
context={'request': request})
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
return Response({
'token': token.key,
'user_id': user.pk,
'first_name': user.first_name,
'email': user.email
})
I am not able to create the users and not able to authincate users.
I am getting below error try to authenticate the user:
Bad Request: /api/auth/
[06/Jul/2021 10:59:38] "POST /api/auth/ HTTP/1.1" 400 68
Could you please anybody help, Do I made wrong or flow are wrong.
Thanks in advance !!!!
I'm new in Django rest_framework. I'm using a custom user model and djoser as my authentication system. I want to use my custom user model in the djoser register view and I don't know how to identify that in the rest_framework settings?
settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
),
'DEFAULT_PERMISSIONS_CLASSES':(
'rest_framework.permissions.IsAuthenticated',
),
}
models.py
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
class MyAccountManager(BaseUserManager):
def create_user(self, email, username, phone, password=None):
if not email:
raise ValueError("Users must have an email address")
if not username:
raise ValueError("Users must have an username")
if not phone:
raise ValueError("Users must have a phone number")
user = self.model(
email=self.normalize_email(email),
username=username,
phone=phone
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self,email, username, phone, password):
user = self.create_user(
email=self.normalize_email(email),
username=username,
phone=phone,
password=password
)
user.is_admin = True
user.is_staff = True
user.is_superuser = True
user.save(using=self._db)
return user
class Account(AbstractBaseUser):
email = models.EmailField(verbose_name="email",max_length=60, unique=True)
username = models.CharField(max_length=60,unique=True)
phone = models.CharField(max_length=60,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)
first_name = models.CharField(max_length=60,null=True)
last_name = models.CharField(max_length=60,null=True)
wilaya = models.CharField(max_length=60,null=True)
city = models.CharField(max_length=60,null=True)
address = models.CharField(max_length=200,null=True)
store_coordinates = models.CharField(max_length=60,null=True)
documents1 = models.CharField(max_length=60,null=True)
documents2 = models.CharField(max_length=60,null=True)
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email','phone']
objects = MyAccountManager()
def __str__(self):
return self.username
def has_perm(self, perm, obj=None):
return self.is_admin
def has_module_perms(self, app_label):
return True
What should I add to use my custom user model in djoser register view?
You're doing almost everything right, but you need to tell Django in the settings what user model it should use.
In models.py use something like this
from django.contrib.auth.models import AbstractUser
from .managers import UserManager
class User(AbstractUser):
# Add whatever fields you want
# Use your custom manager
objects = UserManager()
In settings.py
# Tell Django what user model should be used
AUTH_USER_MODEL = 'auth.User' # app_label.model
make sure you use python manage.py makemigrations && python manage.py migrate to create your custom user model.
Enjoy!