Iam new to django rest framework. I have created two registration models - user model and doctor model. Now trying to create lopin API. How to achieve two different models under 1 API. Please suggest me in validating login credentials of user and doctor in same login API.
models.py
class User(AbstractUser):
username = models.CharField(_('username'), max_length=100)
email = models.EmailField(_('email address'),max_length=100,unique=True)
is_doctor = models.BooleanField(_('doctor status'),default=False)
is_admin = models.BooleanField(_('admin status'),default=False)
phone = models.CharField(_('phone number'), max_length=50)
address = models.TextField(_('address'))
dob = models.CharField(_('date_of_birth'), max_length=20)
education = models.CharField(_('education'), max_length=100, default='', editable=True)
work_status = models.CharField(_('work status'), max_length=100, default='', editable=True)
gender = models.CharField(_('gender'), max_length=10)
city = models.CharField(_('city'), max_length=50)
role = models.CharField(_('role'), max_length=10, default='user', editable=False)
class Meta(AbstractUser.Meta):
swappable = 'AUTH_USER_MODEL'
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
def get_email(self):
return f'{self.email}'
def __str__(self):
return f'{self.email}'
def create_superuser(self, username, email, password):
"""
Creates and saves a superuser with the given email and password.
"""
# user = self.CreateUser(username, email,
# password=password,
# )
user = self.model(
email=self.normalize_email(email)
)
user.username = username
user.set_password(password)
user.is_admin = True
user.save(using=self._db)
return user
class Doctor(models.Model):
doctor_name = models.CharField(max_length=20)
doctor_reg_id = models.CharField(max_length=100)
qualification = models.CharField(max_length=200)
hospital_address = models.TextField()
phone = models.CharField(max_length=200)
email = models.EmailField(max_length=100, unique=True)
password = models.CharField(max_length=100)
specialization = models.CharField(max_length=100, default='', editable=True)
about_me = models.TextField()
role = models.CharField(max_length=10, default='doctor', editable=False)
serializers.py
class UserRegisterSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id','username','email','password', 'phone', 'address', 'dob', 'education',
'work_status', 'gender', 'city', 'role']
extra_kwargs = {'password':{'write_only':True}}
def create(self, validated_data):
user = User.objects.create_user(validated_data['username'], validated_data['email'],
validated_data['password'])
return user
views.py
class Register_Users(APIView):
permission_classes = [AllowAny]
serializer_class = UserRegisterSerializer
def post(self, request):
data = {}
serializer = UserRegisterSerializer(data=request.data)
if serializer.is_valid():
account = serializer.save()
account.save()
token = Token.objects.get_or_create(user=account)[0].key
data['message'] = "Successful Registration"
# data['email'] = account.email
# data['username'] = account.username
# data['token'] = token
return Response(serializer.data)
else:
return Response(serializer.errors)
class Doctor_Registration(APIView):
permission_classes = [AllowAny]
serializer_class = DoctorSerializer
def post(self, request):
data = {}
serializer = DoctorSerializer(data=request.data)
if serializer.is_valid():
account = serializer.save()
account.save()
token = Token.objects.get_or_create(user=account)[0].key
# data['email'] = account.email
# data['username'] = account.username
# data['token'] = token
return Response(serializer.data)
else:
return Response(serializer.errors)
class LoginUser(APIView):
permission_classes = [AllowAny]
# queryset = User.objects.all()
serializer_class = UserLoginSerializer
def post(self, request):
data = {}
serializer = UserLoginSerializer(data=request.data)
if serializer.is_valid():
try:
Account = User.objects.get(email=serializer.validated_data['email'],
password=serializer.validated_data['password'])
token = Token.objects.get_or_create(user=Account)[0].key
login(request, Account)
data["message"] = "Login Successful"
data["token"] = token
data["username"] = request.user.username
data["role"] = request.user.role
return Response(data)
except:
return Response(serializer.errors)
Related
login() takes 1 positional argument but 2 were given
views.py
from django.shortcuts import render,redirect
from django.contrib.auth import login
def login(request):
if request.method == 'POST':
username = request.POST['username']
password = request.POST['password']
print(username)
print(password)
employee = Employees_Model.authenticate(username=username, password=password)
if employee is not None:
login(request, employee)
return redirect('index')
else:
return render(request, 'login.html', {'error': 'Invalid credentials'})
#print("")
#print("testing")
return render(request, 'login.html')
models.py
class Employees_Model(models.Model):
firstname = models.CharField(max_length=100)
lastname = models.CharField(max_length=100)
username = models.CharField(max_length=100)
email = models.CharField(max_length=50)
password = models.CharField(max_length=100)
password2 = models.CharField(max_length=100)
Employee_ID = models.CharField(max_length=20)
joining_date = models.CharField(max_length=20)
phone_number = models.IntegerField(null=True)
Role = models.CharField(max_length=30)
active = models.BooleanField(default=False)
inactive = models.BooleanField(default=False)
def authenticate(username, password):
try:
employee = Employees_Model.objects.filter(username=username).first()
if employee.password == password:
return employee
except Employees_Model.DoesNotExist:
return None
def __str__(self):
return self.firstname
i try to create a custom login with another model called Employee model but i keep getting errors in login(request, employee) takes 1 positional argument but 2 were given,
My models:
//custom user class
class CustomUser(AbstractUser):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, unique = True)
bio = models.CharField(max_length=300, null=True, )
private_account = models.BooleanField(default=False)
dob = models.DateField(max_length=8, null=True)
profilepic
//each user has a Friendslist
class Friendslist(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, unique = True)
user = models.OneToOneField(
CustomUser, null=True, on_delete=models.CASCADE, related_name='friendslist')
friends = models.ManyToManyField(
CustomUser, related_name='friends', symmetrical=False)
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = CustomUser
fields= ['id','bio', 'profilepic', 'friends', 'private_account', ]
depth: 3
currently i get:
...friends: [a228b0f-452f-4e46-a08b-e8fd01aaf78, e1d6237-5bd4-41eb-b925-8f0b1dc885], private_account: false, ....
but I want a specific User to show up instead of the users ID. how do i go about doing this?
I am making follow-following logic in DRF Below are my codes.
Models.py
class CustomUser(AbstractUser):
email = models.EmailField(_('email address'), unique=True)
userId = models.UUIDField(primary_key = True,default = uuid.uuid4,editable = False,unique=True)
gender = models.CharField(max_length=1,default='M')
profilePic = models.URLField(max_length=200,default='https://cdn.pixabay.com/photo/2015/10/05/22/37/blank-profile-picture-973460_960_720.png')
bio = models.TextField(null=True)
viewCount = models.IntegerField(default=0)
followers = models.IntegerField(default=0)
followings = models.IntegerField(default=0)
countryCode = models.CharField(max_length=255,default='+91')
country = models.CharField(max_length=255,default="India")
phoneNumber = models.CharField(max_length=10,default="0000000000")
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = CustomUserManager()
def __str__(self):
return self.email
class followAssociation(models.Model):
user = models.ForeignKey(CustomUser,related_name='user',on_delete=CASCADE)
follows = models.ForeignKey(CustomUser,related_name='follows',on_delete=CASCADE)
class Meta:
unique_together = ('user', 'follows')
Below are my serializers.
Serializers.py
from django.db.models import fields
from rest_framework import serializers
from users.models import CustomUser,followAssociation
class userSerializer(serializers.ModelSerializer):
class Meta:
model = CustomUser
fields = ('first_name','last_name','email','username','password','is_active','is_staff','is_superuser','bio','gender',
'viewCount','profilePic','userId','followers','followings','countryCode','country','phoneNumber')
read_only_fields = ['is_active', 'is_staff', 'is_superuser']
extra_kwargs = {'password': {'write_only': True, 'min_length': 4,'required': False},'username': {'required': False},'email': {'required': False}}
class followAssociationSerializers(serializers.ModelSerializer):
class Meta:
model = followAssociation
fields = ['user','follows']
Now my APIVIEW class
views.py
class followAssociationAPIView(APIView):
parser_classes = [JSONParser]
authentication_classes = [TokenAuthentication]
permission_classes = [IsAuthenticated]
def get(self, request,format = None):
data = {"user":request.user.userId}
follows= get_object_or_404(CustomUser.objects.all(),userId = request.query_params["id"])
data["follows"] = follows.userId
followAssociation = followAssociationSerializers(data = data)
if followAssociation.is_valid(raise_exception=True):
followAssociation.save()
return Response(followAssociation.data,status=status.HTTP_202_ACCEPTED)
return Response(followAssociation.errors,status= status.HTTP_400_BAD_REQUEST)
def delete(self,request,format = None):
relation = get_object_or_404(followAssociation.objects.all(),follows = request.query_params["id"])
try:
data = followAssociationSerializers(relation)
relation.delete()
return Response(data.data,status=status.HTTP_200_OK)
except:
return Response(status=status.HTTP_302_FOUND)
In response, I get userId and FollowsId but I want the full user Model.
I tried depth = 1 and models.PrimaryRelatedFields() it works but only one time after I delete follow association object and next time I try to insert it says the username already exists. Please Help.
Try using Nested Serializers
You can create a new serializer class for the nested serializer and include only the fields you want. You can use the same serializer for both fields
I have two models "Users" and "UserHasMachine" which are connected by primary key
I wanted to create queryset with fields from both of the tables filtered by a field "machine" field in "UserHasMachine" table
I tried to use select_releated but it seems to return just full "User" table without filtering by a field in "UserHasMachine"
models.py
class User(models.Model):
id = models.BigAutoField(primary_key=True)
email = models.CharField(unique=True, max_length=128)
name = models.CharField(max_length=256, blank=True, null=True)
pwd_hash = models.CharField(max_length=128, blank=True, null=True)
pwd_salt = models.CharField(max_length=256, blank=True, null=True)
is_builtin = models.IntegerField()
has_account = models.IntegerField()
is_verified = models.IntegerField()
is_enabled = models.IntegerField()
verifycode = models.CharField(max_length=128, blank=True, null=True)
user_creator = models.ForeignKey('self', models.DO_NOTHING)
is_deleted = models.IntegerField()
deleted_at = models.DateTimeField(blank=True, null=True)
created_at = models.DateTimeField(blank=True, null=True, auto_now_add=True)
updated_at = models.DateTimeField(blank=True, null=True, auto_now=True)
class Meta:
managed = False
db_table = 'user'
class UserHasMachine(models.Model):
user = models.ForeignKey(User, models.DO_NOTHING, primary_key=True)
machine = models.ForeignKey(Machine, models.DO_NOTHING)
role = models.CharField(max_length=5)
created_at = models.DateTimeField(blank=True, null=True, auto_now_add=True)
updated_at = models.DateTimeField(blank=True, null=True, auto_now=True)
class Meta:
managed = False
db_table = 'user_has_machine'
unique_together = (('user', 'machine'),)
views.py
class UserMachineViewSet(ListRetrieveUpdateModelViewSet):
queryset = UserHasMachine.objects.select_related('user')
serializer_class = UserMachineSerializer
filter_backends = (filters.DjangoFilterBackend, )
filter_fields = ('machine_id', )
permission_classes = (partial(UserInAnyGroup, {
'update': ['admin'],
'partial_update': ['admin']
}),)
class UserViewSet(ListRetrieveUpdateModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
filter_backends = (filters.DjangoFilterBackend, )
filter_fields = ('id', )
permission_classes = (partial(UserInAnyGroup, {
'update': ['admin'],
'partial_update': ['admin']
}),)
serializers.py
class UserMachineSerializer(serializers.ModelSerializer):
class Meta:
model = UserHasMachine
fields = '__all__'
read_only_fields = (
'user',
'machine',
'role',
'created_at',
'updated_at'
)
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
read_only_fields = (
'id',
'email',
'name',
'created_at',
'updated_at'
)
I expect to return a queryset containing name and the email from "User" table filtered by the machine field in the "UserHasMachines"
Here is the response I get then I call UserMachineViewSet:
...
{"JSON":[{"user":13,"role":"user","created_at":"2018-01-29T12:02:35","updated_at":"2018-01-29T12:02:35","machine":7},{"user":14,"role":"admin","created_at":"2018-01-29T12:02:35","updated_at":"2018-01-29T12:02:35","machine":7},{"user":39,"role":"user","created_at":"2018-10-02T13:39:12","updated_at":"2018-10-02T13:39:12","machine":7},
...
I think you have a misconception regarding select_related. All it does is to load user data from database when you are querying, so that you don't have to hit the database when accessing user information. For example:
queryset = UserHasMachine.objects.select_related('user')
for q in queryset:
q.user # will not hit database
queryset = UserHasMachine.objects.all()
for q in queryset:
q.user # will hit database
Now, if you want to show the user information in API, just add depth=1 in serializer meta class:
class UserMachineSerializer(serializers.ModelSerializer):
class Meta:
model = UserHasMachine
fields = '__all__'
read_only_fields = (
'user',
'machine',
'role',
'created_at',
'updated_at'
)
depth = 1
Then it will show nested fields inside user field.
BTW I am not clear about what you meant by:
I expect to return a queryset containing name and the email from "User" table filtered by the machine field in the "UserHasMachines"
But the User information is already there in the queryset, and you can access it via:
# method 1
queryset = UserHasMachine.objects.select_related('user')
for q in queryset:
print(q.user.id)
# method 2
queryset.values('user__id', 'user__email')
# method 3
# you can annotate any value of user with queryset like this:
from django.db.models import F
queryset = queryset.annotate(user_email=F('user__email'))
class ProjectTimeAllocation(models.Model):
id = models.IntegerField(primary_key=True)
project_id = models.ForeignKey(Projects, models.DO_NOTHING, related_name='pj_id', blank=True, null=True)
project_manager_id = models.ForeignKey(ProjectManagers, models.DO_NOTHING, blank=True, null=True)
hours = models.IntegerField(blank=True, null=True)
week = models.DateTimeField(blank=True, null=True)
date_created = models.DateTimeField(auto_now=True, null=True)
last_updated = models.DateTimeField(blank=True, null=True)
class Meta:
managed = False
db_table = 'project_time_allocation'
views.py
def timeallocation(request, project_manager_id):
#time_list = ProjectTimeAllocation.objects.order_by('-id')
if request.method == "POST":
form = ProjectTimeAllocationForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
#post.project_manager_id = project_manager_id
post.save()
messages.success(request, "Updated Time!")
return HttpResponseRedirect('/dashboard/')
else:
form = ProjectTimeAllocationForm()
return render(request, 'dashboard/timeallocation.html', {'form':form})
forms.py
class ProjectTimeAllocationForm(forms.ModelForm):
week = forms.DateField(widget=forms.SelectDateWidget())
class Meta:
model = ProjectTimeAllocation
fields = (
'hours',
'week',
'project_manager_id',
'project_id',
)
widgets = {
}
labels = {
}
I get an operational error (1054, "Unknown column 'project_id_id' in field list. I don't understand where the extra _id is coming from? I have tried clearing the migrations and making new migrations.
It appears that django appends _id on ForeignKeys and thus I had to remove _id from my column name in the model and in the form and then django appended it back on when it generated the sql insert statement and the problem is now resolved.