Why doesn't DRF enforce non-empty field? - python-3.x

I am using a custom user model:
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
username = models.CharField(max_length=120,
unique=True,
blank=False,
validators=[MinLengthValidator(6)])
email = models.EmailField('Email Address', unique=True, blank=False)
FCM_ID = models.CharField(max_length=300)
I was hoping that I won't be allowed to create a user with blank email field, but for some reason, I can do so without any errors.
User().save() successfully adds a user with all blank fields. The unique condition, however, is enforced as expected. Why is blank=False not enforced?
Here's the custom user manager:
from django.contrib.auth.base_user import BaseUserManager
class CustomUserManager(BaseUserManager):
def create_user(self, email, **kwargs):
if not email:
raise ValueError("The email must be set")
email = self.normalize_email(email)
user = self.model(email=email, **kwargs)
user.save()
return user
def create_superuser(self, email, password, **kwargs):
...
Edit: I made the email field blank=False as well; but to no avail.

Unfortunately, blank=False is not enforced on the database level, it is only checked when you're validating model in your code, which is not happening by itself when calling save() on it. You need to invoke clean_fields at least before saving the model.
Read about validating models in Django docs.
If you are using Django models through DRF serializers or Django forms, they may do some validation as well, so refer to their documentation to find out how it will be handled.

Related

How do I implement user authentication in Django using rest_framework?

I'm using Django/Python3 to make a simple API. I already made my models and views (also using serializers). Currently this is my login view:
class LoginView(generics.CreateAPIView):
queryset = User_Login.objects.all()
serializer_class = LoginUserSerializer
def post(self, request, *args, **kwargs):
id = request.data['id']
name = request.data['name']
password = request.data['password']
email = request.data['email']
User_Login.objects.create(id=id, name=name, password=password, email=email)
return HttpResponse({'message': 'User Created', 'id': id}, status=200)
All my views are basic like this, just to implement a database that I previously modeled.
I need to implement authentication (the one that generates simple tokens that the user need to add to their request's header) and I know It's easy, but I followed this tutorial and I got a lot of errors.
Any advice?

Update django model database with ForeignKey by using serializer

I have created a django model which includes a foreign key to a user as follows:
from authentication.models import User
from django.db import models
class Event(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
dr_notice_period = models.IntegerField(blank=True, null=True)
dr_duration = models.IntegerField(blank=True, null=True)
dr_request = models.FloatField(blank=True, null=True)
My serializers.py file is as follows:
class EventSerializer(serializers.ModelSerializer):
user = UserSerializer(many=True, read_only=True)
class Meta:
model = Event
fields = ['user', 'dr_notice_period', 'dr_duration', 'dr_request']
What I need to do is to go to a url and with a POST request to upload the data to the database, but without specifically specifying the user.
My views.py is as follows:
from rest_framework.response import Response
from rest_framework.decorators import api_view
from rest_framework import status
from vpp_optimization.serializers import EventSerializer
#api_view(['POST'])
def event(request):
serializer = EventSerializer(data=request.data)
if serializer.is_valid():
instance = serializer.save(commit=False)
instance.user = request.user
instance.save()
return Response({"status": "success", "data": serializer.data}, status=status.HTTP_200_OK)
else:
return Response({"status": "error", "data": serializer.errors}, status=status.HTTP_400_BAD_REQUEST)
As I study I thought that by using commit=False in save would solve the problem, but I am getting the following error:
'commit' is not a valid keyword argument to the 'save()' method. If you need to access data before committing to the database then inspect 'serializer.validated_data' instead. You can also pass additional keyword arguments to 'save()' if you need to set extra attributes on the saved model instance. For example: 'serializer.save(owner=request.user)'.'
Is there a better way to do what I intent to do?
You pass the user as parameter, so:
if serializer.is_valid():
instance = serializer.save(user=request.user)
return Response({'status': 'success', 'data': serializer.data}, status=status.HTTP_200_OK)
Note: It is normally better to make use of the settings.AUTH_USER_MODELĀ [Django-doc] to refer to the user model, than to use the User modelĀ [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.

using custom model fo django djoser

i am creating api endpoints for user management using Djoser and i want to use a custom model to create user and login i only want to use email.
the user entity given to me does not have a username field
below i will share the various settings i have set up for my apps
#accounts/model.py
from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here.
class CustomUser(AbstractUser):
username = None
email = models.EmailField(unique=True)
REQUIRED_FIELDS = ['first_name', 'last_name']
USERNAME_FIELD = 'email'
def __str__(self):
return self.email
My serializer file
#accounts/serializers.py
from djoser.serializers import UserCreateSerializer, UserSerializer
from rest_framework import serializers
from rest_framework.fields import CurrentUserDefault
from .models import CustomUser
class UserCreateSerializer(UserCreateSerializer):
class Meta:
model = CustomUser
fields = ['id', 'email', 'first_name', 'last_name']
#settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
# 'rest_framework_simplejwt.authentication.JWTAuthentication',
'rest_framework.authentication.TokenAuthentication',
),
'DEFAULT_PERMISSIONS_CLASSES': (
'rest_framework.permissions.IsAuthenticated'
)
}
AUTH_USER_MODEL = 'accounts.CustomUser'
DJOSER = {
'LOGIN_FIELD': 'email',
'USER_CREATE_PASSWORD_RETYPE': True,
'SERIALIZERS': {
'user_create': 'accounts.serializers.UserCreateSerializer',
'user': 'accounts.serializers.UserCreateSerializer',
# 'current_user': 'accounts.serializers.CurrentUserSerializer'
}
when i try to register user i get
TypeError at /auth/users/
create_user() missing 1 required positional argument: 'username'
Request Method: POST
Request URL: http://127.0.0.1:8000/auth/users/
Django Version: 3.1
Exception Type: TypeError
Exception Value:
create_user() missing 1 required positional argument: 'username'
Exception Location: /home/femiir/.virtualenvs/codegarage/lib/python3.8/site-packages/djoser/serializers.py, line 73, in perform_create
Python Executable: /home/femiir/.virtualenvs/codegarage/bin/python
Python Version: 3.8.5
please what i my doing wrong ?
You need to have a custom user manager, probably something like this:
from django.contrib.auth.base_user import BaseUserManager
class MyUserManager(BaseUserManager):
def create_user(self, email, password=None, **extra_fields):
"""
Creates and saves a User with the given email, first name,
last name and password.
"""
if not email:
raise ValueError("Users must have an email address")
user = self.model(
email=self.normalize_email(email),
**extra_fields,
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password=None, **extra_fields):
"""
Creates and saves a superuser with the given email, first name,
last name and password.
"""
user = self.create_user(
email,
password=password,
**extra_fields,
)
user.is_admin = True
user.save(using=self._db)
return user
And in your custom user model:
class CustomUser(AbstractBaseUser):
# [...]
objects = MyUserManager()
# [...]
I've taken the code from the django documentation about customizing the User model. They provide an example using the email as the username field (which is what you want).
You may keep the inheritance from AbstractUser but if you do not need most of the things that are in that model, you can also inherit your model from AbstractBaseUser, as in the example.

Django login/register issue

I am new to Django and am building a database-driven website using PyCharm.
I am having an issue with users registering/logging in. What is happening is, when a user registers, I check the "Database" tab to the right, and the information will be passed into a table named "SavBlock_user", which will have the users name, password, etc.. Then, when I try to log in, it won't allow me to login due to incorrect username/password. However, if I try to login using a username/password from a different table named "auth_user" (like username: admin / password: admin), then I can successfully login. I'm not sure how to fix this.
Ideally, what I would like to do is completely remove the "SavBlock_user" table and strictly use "auth_user" for all of my users, but I'm not sure how to do this. I may have created a 'custom' user model back when I was learning the system, but I can't remember.
My files:
Project\register\forms.py
from django import forms
from SavBlock.models import * # <--- Contains User
''' Form for users to register '''
class RegisterForm(forms.ModelForm):
email = forms.EmailField(
initial='myemail#savagez.com'
)
uso_validate = forms.BooleanField(
label='Are you a PSMC member? (Chief, Uso, Anak)',
initial=False
)
class Meta:
model = User
widgets = {
'password': forms.PasswordInput(),
}
fields = '__all__'
Project\register\views.py
from django.http import request
from django.shortcuts import render, redirect
from .forms import RegisterForm
# Create your views here.
def register(response):
if response.method == "POST":
form = RegisterForm(response.POST or None)
if form.is_valid():
form.save()
return redirect('/dashboard/')
else:
form = RegisterForm()
return render(response, 'register/register.html', {'form': form})
Project\SavBlock\models.py
from django.db import models
class User(models.Model):
username = models.CharField("user name", max_length=50, default='')
email = models.EmailField("email address", unique=True, default='DEFAULT VALUE')
first_name = models.CharField("first name", max_length=50)
last_name = models.CharField("last name", max_length=50)
password = models.CharField("password", unique=True, max_length=50, default='')
rank = {
0: 'Supporter',
1: 'Anak',
2: 'Uso',
3: 'Chief'
}
#TODO: FIT __INIT__
'''
def __init__(self, first_name, last_name, *args, **kwargs):
super().__init__(*args, **kwargs)
self.first_name = first_name.title()
self.last_name = last_name.title()
'''
# Magic method returns string of self
def __str__(self):
return f"User {self.first_name} {self.last_name} rank {self.rank}".strip()
#property
def get_full_name(self):
return f"{self.first_name} {self.last_name}".strip()
class Anak(User):
def __init__(self, first_name, last_name, tribe):
super().__init__(first_name, last_name)
self.tribe = tribe.title()
self.rank = User.rank[1]
class Uso(User):
def __init__(self, first_name, last_name, tribe):
super().__init__(first_name, last_name)
self.tribe = tribe.title()
self.rank = User.rank[2]
----EDIT----
I fixed the different user tables and basically reset the migrations. Now, all users are showing up under a single user table SavBlock_user. However, the login issue is still there.
Admin was created using the terminal manage.py createsuperuser command. I am able to login on the webpage using this account.
testing123 was created using the registration form. It pulls up a message that says "Please enter a correct username and password".
Anyone have any ideas?

How can i add registration data in another model and can login from that data in django

Recently i am facing a problem in registration. I have made a ** accounts** app in my project for registrations. I don't want to save data in default User model in dB. i am trying to make an eCommerce site where people can register to buy products. What is the best way for registrations and log in? Extending the user model could have solved the problem but I want a totally different model for that user who wants to buy products for registration and login. Is it the best way what i am thinking for an eCommerce site registration? How can i do it?
I think that is what You need:
Make your custom User model where you can add any fields you want (so thats the way for you to make a "totally different model for user who wants to buy products"):
from django.db import models
from django.contrib.auth.models import User
class MyUser(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
myField1 = models.CharField(max_length=100)
myField2 = models.CharField(max_length=100)
myField3 = models.CharField(max_length=100)
In settings.py add (myapp - name of your app):
AUTH_USER_MODEL = 'myapp.MyUser'
Make your custom UserCreationForm:
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
class MyRegisterForm(UserCreationForm):
class Meta:
model = MyUser
fields = UserCreationForm.Meta.fields + ('myField1', 'myField2', 'myField3')
class MyLoginForm(forms.ModelForm):
class Meta:
model = MyUser
fields = UserCreationForm.Meta.fields + ('myField1', 'myField2', 'myField3')
In views.py add:
from . import forms
from django.contrib.auth import views as auth_views
def register_view(request, *args, **kwargs):
if request.method == "POST":
form = forms.MyRegisterForm(request.POST)
if form.is_valid():
form.save()
else:
form = forms.MyRegisterForm()
return render(request, 'register.html', {'form': form})
class MyLoginView(auth_views.LoginView):
redirect_authenticated_user = True
form_class = forms.MyLogInForm
In urls.py add:
urlpatterns = [
path('login/', views.MyLoginView.as_view(template_name='login.html'), name='login'),
path('register/', views.register_view, name='register'),
]

Resources