So I want to create a custom message for the different fail based on the serializer validation. But I was only able to find how to create a standard custom response for success and fail only. Though the only response I want to create is for the failed prompt message.
I found one of the posts but it's not really what I wanted for the error code
source: Django Rest Framework custom response message
Basically, the error message must be based on the specific validation error in serializer.py
Here is my code
serializer.py
class UserCreate2Serializer(ModelSerializer):
email = EmailField(label='Email Address')
valid_time_formats = ['%H:%M', '%I:%M%p', '%I:%M %p']
birthTime = serializers.TimeField(format='%I:%M %p', input_formats=valid_time_formats, allow_null=True, required=False)
class Meta:
model = MyUser
fields = ['username', 'password', 'email', 'first_name', 'last_name', 'gender', 'nric', 'birthday', 'birthTime', 'ethnicGroup']
extra_kwargs = {"password": {"write_only": True}}
def validate(self, data): # to validate if the user have been used
email = data['email']
user_queryset = MyUser.objects.filter(email=email)
if user_queryset.exists():
raise ValidationError("This user has already registered.")
return data
# Custom create function
def create(self, validated_data):
username = validated_data['username']
password = validated_data['password']
email = validated_data['email']
first_name = validated_data['first_name']
last_name = validated_data['last_name']
gender = validated_data['gender']
nric = validated_data['nric']
birthday = validated_data['birthday']
birthTime = validated_data['birthTime']
ethnicGroup = validated_data['ethnicGroup']
user_obj = MyUser(
username = username,
email = email,
first_name = first_name,
last_name = last_name,
gender = gender,
nric = nric,
birthday = birthday,
birthTime = birthTime,
ethnicGroup = ethnicGroup,
)
user_obj.set_password(password)
user_obj.save()
return validated_data
views.py
class CreateUser2View(CreateAPIView):
permission_classes = [AllowAny]
serializer_class = UserCreate2Serializer
queryset = MyUser.objects.all()
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
if not serializer.is_valid(raise_exception=False):
return Response({"promptmsg": "You have failed to register an account"}, status=status.HTTP_400_BAD_REQUEST)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response({'promptmsg': 'You have successfully register'}, status=status.HTTP_201_CREATED, headers=headers)
As you can see from serializer validate section, if email exists, it will give an error message of this user has been registered. Same for username but Django has its own validation for it since it is built in.
Is there a way for the error message to be like this:
if username is taken, instead of this
"username": [
"A user with that username already exists."
]
to this
"promptmsg": [
"A user with that username already exists."
]
--updated--
models.py
class MyUser(AbstractUser):
userId = models.AutoField(primary_key=True)
gender = models.CharField(max_length=6, blank=True, null=True)
nric = models.CharField(max_length=9, blank=True, null=True)
birthday = models.DateField(blank=True, null=True)
birthTime = models.TimeField(blank=True, null=True)
ethnicGroup = models.CharField(max_length=30, blank=True, null=True)
def __str__(self):
return self.username
You can get this list of errors at serializer.errors look how validation works
Related
I have these two models: order and route. The route has a oneToMany relation with Order as you can see:
class Order(models.Model):
customer = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='customer')
retailer = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='retailer')
date_publish = models.DateField(default=datetime.date.today)
date_available = models.DateField()
weight = models.DecimalField(decimal_places=2, max_digits=5)
description = models.CharField(max_length=500, null=True)
route = models.ForeignKey(Route, related_name='orders', null=True, on_delete=models.CASCADE)
class Route(models.Model):
day = models.DateField()
warehouse = models.ForeignKey(Warehouse, on_delete=models.CASCADE)
start_time = models.TimeField()
When a route is created I want to associate orders with that route, so I've done the following serializer:
class routeSerializer(serializers.ModelSerializer):
orders = OrderSerializer(many=True)
class Meta:
model = Route
fields = ['day', 'warehouse', 'start_time', 'orders']
def create(self, validated_data):
orders_data = validated_data.pop('orders')
route = Route.objects.create(**validated_data)
for order_data in orders_data:
order_serializer = OrderSerializer(data=order_data)
order_serializer.is_valid(raise_exception=True)
orders = order_serializer.save()
orders.route = route
orders.save()
return route
class OrderSerializer(serializers.ModelSerializer):
ordertimelocation = orderTimelocationSerializer(many=True)
class Meta:
model = Order
fields = ['id', 'customer', 'retailer', 'date_available', 'weight', 'description', 'ordertimelocation']
def create(self, validated_data):
timelocations_data = validated_data.pop('ordertimelocation')
order = Order.objects.create(**validated_data)
for timelocation_data in timelocations_data:
order_time_location_serializer = orderTimelocationSerializer(data=timelocation_data)
order_time_location_serializer.is_valid(raise_exception=True)
order_time_location = order_time_location_serializer.save()
order_time_location.order = order
order_time_location.save()
return order
def update(self, instance, validated_data):
timelocations_data = validated_data.pop('ordertimelocation')
ordertimelocation = instance.ordertimelocation
for timelocation_data in timelocations_data:
order_time_location_serializer = orderTimelocationSerializer(data=timelocation_data)
order_time_location_serializer.is_valid(raise_exception=True)
order_time_location = order_time_location_serializer.save()
order_time_location.order = instance
order_time_location.save()
return instance
Views:
class GetRoutes(generics.ListAPIView):
queryset = Route.objects.all()
serializer_class = routeSerializer
class CreateRoute(generics.CreateAPIView):
queryset = Route.objects.all()
serializer_class = routeSerializer
class CreateOrder(generics.CreateAPIView):
queryset = Order.objects.all()
serializer_class = OrderSerializer
class GetOrders(generics.ListAPIView):
serializer_class = OrderSerializer
def get_queryset(self):
us = self.kwargs.get('us')
return Order.objects.filter(customer_id=us)
class GetOrder(generics.RetrieveAPIView):
serializer_class = OrderSerializer
def get_object(self, queryset=None, **kwargs):
item = self.kwargs.get('order')
return get_object_or_404(Order, id=item)
class UpdateOrder(generics.UpdateAPIView):
serializer_class = OrderSerializer
queryset = Order.objects.all()
Edit:
I also customised the default user model like this:
User models:
class UserManager(BaseUserManager):
def create_superuser(self, email, user_name, first_name, password, **other_fields):
other_fields.setdefault('is_staff', True)
other_fields.setdefault('is_superuser', True)
other_fields.setdefault('is_active', True)
if other_fields.get('is_staff') is not True:
raise ValueError(
'Superuser must be assigned to is_staff=True.')
if other_fields.get('is_superuser') is not True:
raise ValueError(
'Superuser must be assigned to is_superuser=True.')
return self.create_user(email, user_name, first_name, password, **other_fields)
def create_user(self, email, user_name, first_name, password, **other_fields):
if not email:
raise ValueError(_('You must provide an email address'))
email = self.normalize_email(email)
user = self.model(email=email, user_name=user_name, first_name=first_name, **other_fields)
user.set_password(password)
user.save()
return user
class User(AbstractBaseUser, PermissionsMixin):
GENDER_MALE = 0
GENDER_FEMALE = 1
GENDER_OTHER = 2
GENDER_CHOICES = [(GENDER_MALE, 'Male'), (GENDER_FEMALE, 'Female'), (GENDER_OTHER, 'Other')]
email = models.EmailField(_('email address'), unique=True)
user_name = models.CharField(max_length=150, unique=True)
first_name = models.CharField(max_length=150, blank=True)
last_name = models.CharField(max_length=150, blank=True)
start_date = models.DateTimeField(default=timezone.now)
is_staff = models.BooleanField(default=False)
is_retailer = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
gender = models.IntegerField(choices=GENDER_CHOICES)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['user_name', 'first_name']
def __str__(self):
return self.user_name
def isretailer(self):
return self.is_retailer
User serizalizer:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'email', 'user_name', 'first_name', 'last_name')
Views:
class CustomUserCreate(APIView):
permission_classes = [AllowAny] #when a user create an account he isn't autenticated
def post(self, request, format='json'):
serializer = RegisterUserSerializer(data=request.data)
if serializer.is_valid():
user = serializer.save()
if user:
json = serializer.data
return Response(json, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class ListUsers(generics.ListAPIView):
serializer_class = UserSerializer
queryset = User.objects.all()
class UserDetail(generics.RetrieveAPIView):
serializer_class = UserSerializer
def get_object(self, queryset=None, **kwargs):
item = self.kwargs.get('id')
return get_object_or_404(User, id=item)
I am sending a request like this:
{
"day" : "2021-12-12",
"warehouse": "1",
"start_time": "7:00",
"orders": [
{
"id": 15,
"customer": 1,
"retailer": 2,
"date_available": "2020-12-12",
"weight": "1.20",
"description": null,
"ordertimelocation": [
{
"longitude": "12.1223000000000000",
"latitude": "12.1223000000000000",
"time_interval": [
{
"start": "2021-07-21T10:10:00Z",
"end": "2021-07-21T10:10:00Z"
}
]
}
]
}
]
}
But the server returns a bad request:
{
"customer": [
"Incorrect type. Expected pk value, received User."
],
"retailer": [
"Incorrect type. Expected pk value, received User."
]
}
I'm new to django and I don't know what is a 'pk' value and why it is expecting it instead of User.
PK is primary key and in here is equal to id
For saving a record in db with it's relations, django needs PK of related object
But when you pass this pk to serializer, in validate() function of serializer, django if passed pk is existed in db and valid, if so, it would return it's model object
for example, you pass customer pk as 1, but after validation, there is a Customer object with id 1. and you are passing this object to order = Order.objects.create(**validated_data) but as i mentioned before, you should pass just PK
So one of solutions can be:
validated_data['customer'] = validated_data['customer'].id
validated_data['retailer'] = validated_data['retailer'].id
order = Order.objects.create(**validated_data)
And another solution is to overriding validate() function and control what to return
When user registers the registration is done but a profile is not created and the error is thrown. In the database the user is created but without a profile. the error is highlighted at
"p_reg_form = ProfileRegisterForm(request.POST, instance=credentials.profile)"
The bellow are the .py files for the project.
This code use to work but has suddenly stopped.
models.py:
class User(AbstractBaseUser, PermissionsMixin):
"""
An abstract base class implementing a fully featured User model with
admin-compliant permissions.
Username and password are required. Other fields are optional.
"""
username_validator = UnicodeUsernameValidator()
username = models.CharField(
"username",
max_length=150,
unique=True,
help_text="Required. 150 characters or fewer. Letters, digits and #/./+/-/_ only.",
validators=[username_validator],
error_messages={"unique": "A user with that username already exists.", },
)
first_name = models.CharField("first name", max_length=30, blank=True)
last_name = models.CharField("last name", max_length=150, blank=True)
email = models.EmailField("email", blank=True, unique=True)
is_staff = models.BooleanField(
"staff status",
default=False,
help_text=["Designates whether the user can log into this admin site."],
)
is_active = models.BooleanField(
"active",
default=True,
help_text=(
"Designates whether this user should be treated as active. "
"Unselect this instead of deleting accounts."
),
)
date_joined = models.DateTimeField("date joined", default=timezone.now)
objects = UserManager()
EMAIL_FIELD = "email"
USERNAME_FIELD = "email"
REQUIRED_FIELDS = ()
class Meta:
verbose_name = "user"
verbose_name_plural = "users"
def clean(self):
"""Try and find the new way to right this save"""
super(User, self).save()
self.email = self.__class__.objects.normalize_email(self.email)
def get_full_name(self):
"""
Return the first_name plus the last_name, with a space in between.
"""
full_name = "%s %s" % (self.first_name, self.last_name)
return full_name.strip()
def get_short_name(self):
"""Return the short name for the user."""
return self.first_name
def email_user(self, subject, message, from_email=None, **kwargs):
"""Send an email to this user."""
send_mail(subject, message, from_email, [self.email], **kwargs)
class Profile(models.Model):
"""Profile class lists the library and choices and settings for each field in the model the Profile
and also sets the database structure"""
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
image = models.ImageField(default="default.jpg", upload_to="profile_pics")
date_birth = models.DateField(max_length=75, null=True, name="date_birth")
def __str__(self):
return str(self.user)
def get_absolute_url(self):
"""Definition for the absolute path to be rendered in the profile-detail view"""
# return f'{self.user.username} Profile'
return reverse("profile-detail", kwargs={"pk": self.pk})
class Profile1(models.Model):
"""This class lists the library classes and settings in the model"""
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
image = models.ImageField(default="default.jpg", upload_to="profile_pics")
def __str__(self):
return str(self.user)
def get_absolute_url(self):
"""Definition for the absolute path to be rendered in the profile-detail view"""
# return f'{self.user.username} Profile'
return reverse("profile-detail", kwargs={"pk": self.pk})
views.py:
def register(request):
"""This function provides the logic and condition for saving the classes, the form variables used
plus the message and the redirection or rendering url """
if request.method == "POST":
form = UserRegistrationForm(request.POST)
p_reg_form = ProfileRegisterForm(request.POST)
if form.is_valid() and p_reg_form.is_valid():
credentials = form.save()
credentials.user = request.user
p_reg_form = ProfileRegisterForm(request.POST, instance=credentials.profile)
p_reg_form.full_clean()
p_reg_form.save()
credentials.is_active = False
credentials.save()
current_site = get_current_site(request)
email_subject = "Activate Your Account"
message = render_to_string(
"users/activate_account.html",
{
"user": credentials,
"domain": current_site.domain,
"uid": urlsafe_base64_encode(force_bytes(credentials.pk)),
"token": account_activation_token.make_token(credentials),
},
)
to_email = form.cleaned_data.get("email")
email = EmailMessage(email_subject, message, to=[to_email])
email.send()
messages.success(
request,
f"Account successfully created for: {first_name} {last_name} , "
f"Please verify your email: {to_email} for access to your account",
)
# return HttpResponse('We have sent you an email,
# please confirm your email address to complete registration')
return TemplateResponse(
request, "users/register_email_sent_confirmation.html"
)
else:
form = UserRegistrationForm()
p_reg_form = ProfileRegisterForm()
context = {"form": form, "p_reg_form": p_reg_form}
return render(request, "users/register.html", context)
forms.py :
BIRTH_YEAR_CHOICES = [x for x in range(1909, 2021)]
class UserRegistrationForm(UserCreationForm):
"""This class lists the model and fields for the User model Registration Form"""
email = forms.EmailField()
first_name = forms.CharField()
last_name = forms.CharField()
class Meta:
"""The model called and the field/s to be filled by the form for rendering"""
model = User
fields = [
"first_name",
"last_name",
"email",
"username",
"password1",
"password2",
]
widgets = {"country": CountrySelectWidget()}
def clean_email(self):
"""This definition checks to see if any users already exist with this email."""
# Get the email
email = self.cleaned_data.get("email")
# Check to see if any users already exist with this email.
try:
match = User.objects.get(email=email)
except User.DoesNotExist:
# Unable to find a user, this is fine
return email
# A user was found with this as a username, raise an error.
raise forms.ValidationError(f"This email ({email}) address is already in use.")
class ProfileRegisterForm(forms.ModelForm):
"""This class lists the model and fields for the User model Profile Registration Form"""
date_birth = forms.DateField(
label="Date of birth",
initial=datetime.date.today,
widget=forms.SelectDateWidget(years=BIRTH_YEAR_CHOICES),
)
class Meta:
"""The model called and the field/s to be filled by the form for rendering"""
model = Profile
fields = ["date_birth",]
signals.py :
#receiver(post_save, sender=User)
def create_profile(instance, created):
"""This function lists the model to be created and saved for the classes relating to
other apps withing the project"""
if created:
Profile.objects.create(user=instance)
Profile1.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_profile(instance):
"""This function lists the model to be created and saved for the classes relating to
other apps withing the project"""
instance.profile.save()
instance.profile1.save()
settings.py :
AUTH_USER_MODEL = "users.User"
error :
RelatedObjectDoesNotExist at /register/
User has no profile.
Request Method: POST
Request URL: http://localhost:5000/register/
Django Version: 3.0.7
Exception Type: RelatedObjectDoesNotExist
Exception Value:
User has no profile.
The code worked previous and only started failing fairly recently, and advice would be grateful.
The problem is from
p_reg_form = ProfileRegisterForm(request.POST, instance=credentials.profile)
At this stage, your credentials do not have profile, therefore, the error.
Try to replace:
p_reg_form = ProfileRegisterForm(request.POST, instance=credentials.profile)
p_reg_form.full_clean()
p_reg_form.save()
with something like: you probably need to tweak a bit
profile = p_reg_form.save()
profile.user = request.user
p_reg_form.full_clean()
p_reg_form.save()
The DoesNotExist exception is raised when an object is not found for the given parameters of a query. Django provides a DoesNotExist exception as an attribute of each model class to identify the class of object that could not be found and to allow you to catch a particular model class with try / except .
I am trying to update the user profile in django rest framework
So i am able to create the user profile simultaneously with user creation using signals
Now i am trying to update the profile created:-
//models.py
class User(AbstractBaseUser,PermissionsMixin):
phone_regex=RegexValidator(regex = r'^[6-9]\d{9}$',message='please enter the correct phonenumber')
#name_regex=RegexValidator(regex=r'/^[A-Za-z]+$/',message='Please enter the correct name')
phone=models.CharField(validators=[phone_regex],max_length=15,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)
active = models.BooleanField(default=True)
staff = models.BooleanField(default=False) # a admin user; non super-user
admin = models.BooleanField(default=False)
first_login=models.BooleanField(default=True)
USERNAME_FIELD='phone'
REQUIRED_FIELDS = []
class UserProfile(models.Model):
# There is an inherent relationship between the Profile and
# User models. By creating a one-to-one relationship between the two, we
# are formalizing this relationship. Every user will have one -- and only
# one -- related Profile model.
GENDER = (
('M', 'Homme'),
('F', 'Femme'),
)
user = models.OneToOneField(
User,related_name="profile", on_delete=models.CASCADE
)
# Each user profile will have a field where they can tell other users
# something about themselves. This field will be empty when the user
# creates their account, so we specify blank=True.
email=models.EmailField(unique=True,validators=[validate_email],max_length=254,blank=True,null=True)
name=models.CharField(max_length=15,blank=True)
dateofbirth=models.DateField(auto_now=False, null=True, blank=True)
Gender=models.CharField(max_length=1, choices=GENDER,blank=True,null=True)
address1 = models.CharField(
"Address",
max_length=1024,
blank=True,
null=True
)
address2 = models.CharField(
"Society",
max_length=1024,
blank=True,
null=True
)
address3 = models.CharField(
"Landmark",
max_length=1024,
blank=True,
null=True
)
zip_code = models.CharField(
"ZIP / Postal code",
max_length=12,
blank=True,
null=True
)
city = models.CharField(
"City",
max_length=1024,
default ="Gurugram",
blank=True,
null=True
)
country = models.CharField(
"Country",
max_length=10,
default="India",
blank=True,
null=True
)
# In addition to the `bio` field, each user may have a profile image or
# avatar. This field is not required and it may be blank.
# A timestamp representing when this object was created.
created_at = models.DateTimeField(auto_now_add=True,blank=True)
# A timestamp representing when this object was last updated.
updated_at = models.DateTimeField(auto_now=True,blank=True)
def __str__(self):
return self.user.phone
#receiver(post_save, sender=User)
def create_profile_for_user(sender, instance=None, created=False, **kargs):
if created:
UserProfile.objects.get_or_create(user=instance)
// Serializer.py
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields=("name",)
class UserSerializer(serializers.ModelSerializer):
user = UserProfileSerializer(many=True)
class Meta:
model = User
fields = ('user', 'phone',)
def create(self, validated_data):
user_data = validated_data.pop('user')
user = User.objects.create(**user_data)
profile = user_data.objects.create(user=user, **validated_data)
return profile
def update(self, instance, validated_data):
print("update function")
# profile_data = validated_data.pop('profile')
# print(profile_data)
profile_data = validated_data.pop('user')
profile = instance.profile
# print("profile is ",profile)
print(profile_data.name)
print("name in validation ",validated_data['name'], " type is ",print(type(validated_data)))
instance.username = validated_data.get('name', instance.username)
instance.email = validated_data.get('email', instance.email)
instance.save()
print("name is ",instance.profile.name )
#profile.Gender = validated_data['Gender'],
#profile.email = validated_data['email'],
#profile.dateofbirth=validated_data['dateofbirth'],
#profile.address1=validated_data['address1'],
##profile.address2=validated_data['address2'],
#profile.address3=validated_data['address3']
# print("1",profile)
#print(profile.Gender)
#print(profile.email)
#print(profile.dateofbirth)
profile.save()
print("2",profile)
print(profile.name)
print("save done")
return instance
//views.py
class ProfileCreateAPIView(CreateAPIView):
def put(self, request, *args, **kwargs):
print("my print 0",request)
header_token = request.META.get('HTTP_AUTHORIZATION', None)
print(header_token)
access_token=header_token.split(' ')[1]
status,user =validate_token(access_token)
print(request.data)
user_profile_serializer = UserProfileSerializer(
instance=user,
data=request.data
)
user_serializer = UserSerializer(
instance=user,
data=request.data)
print("my print 1",user_profile_serializer.is_valid())
print("my print 2",user_serializer.is_valid())
if user_profile_serializer.is_valid() and user_serializer.is_valid():
user_profile_serializer.save()
print("user_profile save done")
user_serializer.save()
print("user save done")
return Response(
status=rt_status.HTTP_200_OK)
# Combine errors from both serializers.
# errors = dict()
# errors.update(user_profile_serializer.errors)
#errors.update(user_serializer.errors)
else:
return Response(status=rt_status.HTTP_400_BAD_REQUEST)
'''
return Response(status=rt_status.HTTP_400_BAD_REQUEST)
'''
Now the issue is when i am hitting the api I am getting user_serializer.is_valid() as TRUE but False from UserProfile.
I feel there is some issue with my getting the profile data.
But I am not able to understand how to resolve the issue .
Any help is much appreciated.
The goal is to test creating a new custom user using APITestCase.
In models.py got two new classes inheriting from AbstractBaseUser and BaseUserManager, respectively
class MyUser(AbstractBaseUser):
objects = MyUserManager()
class Meta:
db_table = 'user_entity'
user_id = models.AutoField(primary_key=True)
#username = models.CharField(max_length=USERNAME_MAX_LENGTH, unique=True, validators=[validators.validate_username])
username = models.CharField(max_length=20, unique=True)
password = models.CharField(max_length=256)
first_name = models.CharField(max_length=25)
last_name = models.CharField(max_length=25)
email = models.EmailField(verbose_name='email', max_length=100, unique=True)
last_access = models.DateField(default=datetime.date.today)
creation_date = models.DateTimeField(default=timezone.now)
last_update = models.DateField(default=datetime.date.today)
user_type = models.IntegerField()
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
USERNAME_FIELD = 'email'
EMAIL_FIELD = 'email'
REQUIRED_FIELDS = ['username', 'first_name', 'last_name', 'user_type']
def __str__(self):
return str(self.user_id) + " (%s)" % str(self.email)
def has_perm(self, perm, obj=None):
return self.user_type == 0
def has_module_perms(self, app_label):
return True
and
class MyUserManager(BaseUserManager):
def _create_generic_user(self, email, username, password, first_name, last_name, user_type):
if not username:
raise ValueError("Username cannot be empty")
if not email:
raise ValueError("Email cannot be empty")
if not first_name:
raise ValueError("First name cannot be empty")
if not last_name:
raise ValueError("Last name cannot be empty")
if not password:
raise ValueError("Password cannot be empty")
user = self.model(
username=username,
first_name=first_name,
last_name=last_name,
email=self.normalize_email(email),
user_type=user_type,
is_staff=user_type == 0,
is_active=False
)
user.set_password(password)
user.save(user=self._db)
return user
def create_user(self, email, username, password, first_name, last_name):
return self._create_generic_user(email, username, password, first_name, last_name, 2)
def create_admin(self, email, username, password, first_name, last_name):
return self._create_generic_user(email, username, password, first_name, last_name, 0)
Then, in serializers.py created a MyUserSerializer for DRF inheriting from a ModelSerializer
class MyUserSerializer(serializers.ModelSerializer):
email = serializers.EmailField(
required=True, # make sure email is provided
validators=[UniqueValidator(queryset=MyUser.objects.all())] # make sure email is unique
)
username = serializers.CharField(
required=True,
validators=[UniqueValidator(queryset=MyUser.objects.all())],
min_length=5,
max_length=20
)
password = serializers.CharField(
write_only=True,
required=True,
max_length=256
)
first_name = serializers.CharField(
required=True,
max_length=25
)
last_name = serializers.CharField(
required=True,
max_length=25
)
def create(self, validated_data):
user = MyUser.objects.create_user(validated_data['email'], validated_data['username'], validated_data['password'],
validated_data['first_name'], validated_data['last_name'])
return user
class Meta:
model = MyUser
fields = ('user_id', 'email', 'username', 'password', 'first_name', 'last_name')
a view with APIView
class MyUserCreate(APIView):
"""
Creates the user.
"""
def post(self, request, format='json'):
serializer = MyUserSerializer(data=request.data)
if serializer.is_valid():
user = serializer.save()
if user:
return Response(serializer.data, status=status.HTTP_201_CREATED)
and urls
urlpatterns = [
url(r'api/users^$', views.MyUserCreate.as_view(), name='user-create'),
]
From this, then went on to create a simple test of creating users
class MyUserTest(APITestCase):
def setUp(self):
# Create a user
self.test_user = MyUser.objects.create_user('tiagomartinsperes#gmail.com', 'tiagoperes', 'EWFdfew45te!sadf32', 'Tiago', 'Peres')
# URL for creating user
self.create_url = reverse('user-create')
def test_create_user(self):
"""
Ensure we can create a new user and a valid token is created with it.
"""
data = {
'email': 'cnf32344#zzrgg.com',
'username': 'andresantos',
'password': 'test',
'first_name': 'André',
'last_name': 'Santos'
}
response = self.client.post(self.create_url , data, format='json')
# Make sure we have two users in the database
self.assertEqual(MyUser.objects.count(), 2)
# Return 201
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
# Return the username and email upon successful creation
self.assertEqual(response.data['email'], data['email'])
self.assertEqual(response.data['username'], data['username'])
self.assertFalse('password' in response.data)
self.assertEqual(response.data['first_name'], data['first_name'])
self.assertEqual(response.data['last_name'], data['last_name'])
Thing is, running
python manage.py test
gets the following error:
Creating test database for alias 'default'... System check identified
no issues (0 silenced). E
====================================================================== ERROR: test_create_user (user.tests.MyUserTest) Ensure we can create a
new user and a valid token is created with it.
---------------------------------------------------------------------- Traceback (most recent call last): File
"C:\Users\tiago\Desktop\letsgo\COVID19-be\django_server\user\tests.py",
line 10, in setUp
self.test_user = MyUser.objects.create_user('tiagomartinsperes#gmail.com',
'tiagoperes', 'EWFdfew45te!sadf32', 'Tiago', 'Peres') File
"C:\Users\tiago\Desktop\letsgo\COVID19-be\django_server\user\models.py",
line 38, in create_user
return self._create_generic_user(email, username, password, first_name, last_name, 2) File
"C:\Users\tiago\Desktop\letsgo\COVID19-be\django_server\user\models.py",
line 34, in _create_generic_user
user.save(user=self._db) File "C:\Users\tiago\Desktop\letsgo\unstable\lib\site-packages\django\contrib\auth\base_user.py",
line 66, in save
super().save(*args, **kwargs) TypeError: save() got an unexpected keyword argument 'user'
---------------------------------------------------------------------- Ran 1 test in 0.172s
FAILED (errors=1) Destroying test database for alias 'default'...
and no users are being created. What can I do to solve it?
The issue is at here
user.save(user=self._db)
The django Model save has the following signature.
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
Since you are passing a database as value(self._db), I think you are expected to write
user.save(using=self._db)
I want to register a new user with a associated list of categories.
I have a table called categories with list of unique categories (category id, category name).
I have a table called accounts with a list of users (username, email, password and list of associated categories).
I have a table called account_categories which is a mapping between users and categories.
Unfortunately when I am trying to create new user I receive an error that provided categories already exists. I don't want to update categories table. I want to update accounts and account_categories.
What am I doing wrong?
model.py
class Category(models.Model):
categoryname = models.CharField(unique=True, max_length=50)
def __str__(self):
return "{0}".format(self.categoryname)
class AccountManager(BaseUserManager):
def create_user(self, email, age, categories, 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.')
if not age:
raise ValueError('Users must have a valid age.')
if not categories:
raise ValueError('Users must choose at least one intrest.')
account = self.model(
email=self.normalize_email(email), age=age, categories=categories, username=kwargs.get('username')
)
account.set_password(password)
account.save()
return account
def create_superuser(self, email, age, categories, password, **kwargs):
account = self.create_user(email=email, age=age, categories=categories, password=password, **kwargs)
account.is_admin = True
account.save()
return account
class Account(AbstractBaseUser):
email = models.EmailField(unique=True)
username = models.CharField(unique=True, max_length=25)
age = models.SmallIntegerField()
categories = models.ManyToManyField(Category)
objects = AccountManager()
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['age', 'email']
def __unicode__(self):
return self.email
serializers.py
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ('id', 'categoryname')
class AccountSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True, required=False)
confirm_password = serializers.CharField(write_only=True, required=False)
categories = CategorySerializer(many=True)
class Meta:
model = Account
fields = ('id', 'email', 'username', 'age', 'categories', 'password', 'confirm_password',)
def create(self, validated_data):
return Account.objects.create(**validated_data)
views.py
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. Probably this email / username has already been used.' + str(serializer.errors) + str(request.data) #+ str(serializer.is_valid())
}, status=status.HTTP_400_BAD_REQUEST)
database scheme
http://postimg.org/image/o15ybcf9f/
error
http://postimg.org/image/4i5muo8fz/