Save two model instances in one updateview - django

Am trying to update the User model and UserProfile model in one view but it's not working. No error is shown and no changes are made to the objects. What am I not doing right.
Here is my models.py:
class UserProfile(models.Model):
"""User information not related to authentication"""
user = models.OneToOneField(User, related_name='user_profile', on_delete=models.CASCADE)
age = models.IntegerField()
# other fields ignored
Here is my serializer.py:
class UserSerializer(ModelSerializer):
first_name = CharField(max_length=20)
last_name = CharField(max_length=20)
email = EmailField(required=True, validators=[UniqueValidator(queryset=User.objects.all())])
username = CharField(max_length=32,validators=[UniqueValidator(queryset=User.objects.all())])
password = CharField(min_length=8, write_only=True)
confirm_password = CharField(write_only=True)
def create(self, validated_data):
user = User.objects.create_user(
validated_data['username'],
email = validated_data['email'],
first_name = validated_data['first_name'],
last_name = validated_data['last_name']
)
password = validated_data['password']
confirm_password = validated_data['confirm_password']
if password != confirm_password:
raise ValidationError({'password': 'Passwords must match'})
else:
user.set_password(password)
user.save()
return user
class Meta:
model = User
fields = ('username', 'first_name', 'last_name', 'email', 'password', 'confirm_password')
class UserProfileSerializer(ModelSerializer):
username = CharField(source='user.username')
first_name = CharField(source='user.first_name')
last_name = CharField(source='user.last_name')
email = CharField(source='user.email')
class Meta:
model = UserProfile
exclude = ('user',)
# fields = '__all__'
# depth = 1
def update(self, instance, validated_data):
user = instance.user
instance.user.username = validated_data.get('username', instance.user.username)
instance.user.email = validated_data.get('email', instance.user.email)
instance.user.first_name = validated_data.get('first_name', instance.user.first_name)
instance.user.last_name = validated_data.get('last_name', instance.user.last_name)
instance.save()
user.save()
return instance
Here is view.py:
class UserProfileUpdate(UpdateAPIView):
queryset = UserProfile.objects.all()
serializer_class = UserProfileSerializer
lookup_field = 'user'

#Eric
Try changing your update method to this, the actual update data is under validated_data['user']
def update(self, instance, validated_data):
user = instance.user
instance.user.username = validated_data['user'].get('username', instance.user.username)
instance.user.email = validated_data['user'].get('email', instance.user.email)
instance.user.first_name = validated_data['user'].get('first_name', instance.user.first_name)
instance.user.last_name = validated_data['user'].get('last_name', instance.user.last_name)
instance.save()
user.save()
return instance

Related

Django rest framework updating a OneToOne field

I have a User model that inherits from AbstractUser which has an email field. And a profile model that has an OneToOne relation with the User model
class User(AbstractUser):
email = models.EmailField(unique=True)
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
phone = models.CharField(max_length=13, validators=[phone_regex], unique=True, null=True, blank=True)
birth_date = models.DateField(blank=True, null=True)
about = models.TextField(max_length=2000, blank=True)
def __str__(self):
return f"{self.user.first_name} {self.user.last_name}"
view.py
class ProfileViewSet(ModelViewSet):
....
#action(detail=False, methods=["GET", "PUT"], permission_classes=[IsAuthenticated])
def me(self, request, *args, **kwargs):
profile = Profile.objects.get(user_id=request.user.id)
if request.method == "GET":
serializer = ProfileSerializer(profile)
return Response(serializer.data)
elif request.method == "PUT":
serializer = ProfileSerializer(profile, request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data)
serializers.py
class ProfileSerializer(serializers.ModelSerializer):
user = CurrentUserSerializer()
def update(self, instance, validated_data):
user_data = validated_data.pop('user')
user_ser = CurrentUserSerializer(instance=instance.user, data=user_data)
if user_ser.is_valid():
user_ser.save()
instance.phone = validated_data.get('phone', instance.phone)
instance.birth_date = validated_data.get('birth_date', instance.birth_date)
instance.about = validated_data.get('about', instance.about)
instance.save()
return instance
class Meta:
model = Profile
fields = [
'id',
'user',
'phone',
'birth_date',
'about',
]
Now when I try to update a user profile I get status: 400 Bad Request error
{
"user": {
"email": [
"user with this email already exists."
]
}
}
using patch instead of put or partial=True doesn't change anything I still get this error. What can I do here?
The thing is you are creating new record on data base and It is not permitted
class ProfileSerializer(serializers.ModelSerializer):
user = CurrentUserSerializer()
def update(self, instance, validated_data):
user_data = validated_data.pop('user')
try:
user=User.objects.get(pk=instance.user.id)
except User.DoesNotExist:
raise ValidationError("User already exist")
#now with user instance you can save the email or do whatever
you want
user.email = "xyaz#gmail.com")
user.save()
instance.phone = validated_data.get('phone', instance.phone)
instance.birth_date = validated_data.get('birth_date', instance.birth_date)
instance.about = validated_data.get('about', instance.about)
instance.save()
return instance
class Meta:
model = Profile
fields = [
'id',
'user',
'phone',
'birth_date',
'about',
]
Instead of a nested serializer I used serializer fields and my problem was solved. I'm not sure if it's a good approach or not.
class ProfileSerializer(serializers.ModelSerializer):
email = serializers.EmailField(source='user.email')
first_name = serializers.CharField(source='user.first_name')
last_name = serializers.CharField(source='user.last_name')
def update(self, instance, validated_data):
user_data = validated_data.pop('user')
user = instance.user
user_ser = CurrentUserSerializer(instance=user, data=user_data)
if user_ser.is_valid():
user_ser.save()
instance.phone = validated_data.get('phone', instance.phone)
instance.birth_date = validated_data.get('birth_date', instance.birth_date)
instance.about = validated_data.get('about', instance.about)
instance.save()
class Meta:
model = Profile
fields = [
'id',
'email',
'first_name',
'last_name',
'phone',
'birth_date',
'about',
]

Django Attach variables from one step to another

How would I grab the 2nd form and add it to the first form then selectively not allow that user to login. In the Doctorwizard done function.
maybe add a variable status?
etc.
username,password,email,first_name,last_name,verified
views.py
from django.core.files.storage import FileSystemStorage
import os
from django.conf import settings
class DoctorWizard(SessionWizardView):
file_storage = FileSystemStorage(location=os.path.join(settings.MEDIA_ROOT, 'doctor'))
template_name = "registration/signup.html"
form_list = [SignUpForm,verify]
def done(self, form_list, **kwargs):
data=process_data(form_list)
return redirect('home')
forms.py
class SignUpForm(UserCreationForm):
first_name = forms.CharField(max_length=30, required=False, help_text='Optional.')
last_name = forms.CharField(max_length=30, required=False, help_text='Optional.')
email = forms.EmailField(max_length=254, help_text='Required. Inform a valid email address.')
class Meta:
model = Profile
fields = ('username', 'first_name', 'last_name', 'email', 'password1', 'password2', )
class verify(forms.Form):
verified = forms.ImageField(required=True)
class Meta:
model = Profile
fields = ('verified',)
models.py
class Profile(AbstractUser):
bio = models.TextField(max_length=100, blank=True)
phone_number = PhoneNumberField(max_length=25, region="US")
birth_date = models.DateField(blank = True, null = True)
is_doctor = models.BooleanField(default=False)
verified = models.ImageField(upload_to='media/doctor')
date_created = models.DateTimeField(auto_now_add=True)
avatar = models.ImageField(default='default.png', upload_to='')
def done(self, form_list, **kwargs):
process_data(form_list)
userCreate = form_list[0]
userCreate.save()
username = userCreate.cleaned_data.get('username')
raw_password = userCreate.cleaned_data.get('password1')
user = authenticate(username=username, password=raw_password)
if user:
user.verified=form_list[1].cleaned_data.get('verified')
user.is_doctor=True
user.is_active=False
user.save()
Just grab the user and access it's fields.

Django multiple serializers with relation

I have two tables of users and I am creating a form to store the user information.
Models.py
class MyUser(User):
address = models.CharField(max_length=200)
city = models.CharField(max_length=200)
expire_date = models.DateField()
This creates a table with user_ptr_id to the auth_user table of django.
I created two serializers: one for the User:
class UserSerializer(serializers.ModelSerializer):
first_name = serializers.CharField(min_length=2, required=True)
last_name = serializers.CharField(min_length=2, required=True)
email = serializers.EmailField(min_length=5, required=True)
password = serializers.CharField(min_length=8, write_only=True, required=True)
class Meta:
model = User
fields = ('email', 'first_name', 'last_name', 'password')
def create(self, validated_data):
return UserSerializer(**validated_data)
And MyUser class:
class MyUserSerializer(serializers.ModelSerializer):
address = serializers.CharField(max_length=200)
city = serializers.CharField(max_length=200)
class Meta:
model = MyUser
fields = ('city', 'address')
def create(self, validated_data):
return MyUser(**validated_data)
As I am using Django Rest-Framework-Auth, I craeted a serializer to catch the data, but I don't know how to let the things work together. In the "MyUserSerializer" class, I also perform many validate checks, that I omitted here to keep the code clean.
Code below doesn't work
class UserSignup(serializers.Serializer):
user = UserSerializer()
my_user = MyUserSerializer()
confirm_psw = serializers.CharField(min_length=8, write_only=True, required=True)
def validate(self, data):
if not data["user"].get('password') or not data.get('confirm_psw'):
raise serializers.ValidationError("Please enter a password and confirm it.")
if data["user"].get('password') != data.get('confirm_psw'):
raise serializers.ValidationError("Those passwords don't match.")
return data
def save(self, validated_data):
user_data = self.validated_data["user"]
my_user = self.validated_data["my_user"]
return user_data, my_user
def create(self, validated_data):
user_data = validated_data.pop('user')
user = User.objects.create(user_data)
my_user = validated_data.pop('my_user')
my_user["user_ptr_id"] = user.id
MyUser.objects.create(**ua_user)
return user
How do I merge the two serializers to correctly perform the user registration?
Thanks

How to update fields of custom user model using Django serializer

I am trying to update the various fields of a user model when the user wants to update it which was created earlier. I am having a custom user model and I am using django rest framework for the update api.
views.py
class UserUpdate(generics.UpdateAPIView):
"""
Update user.
"""
parser_class = (FileUploadParser,)
permission_classes = (AllowAny,)
queryset = User.objects.all()
serializer_class = UserUpdateSerializer
def update(self, request, *args, **kwargs):
instance = self.get_object()
instance.user_id = request.data.get("user_id")
instance.save()
serializer = self.get_serializer(instance, data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
return Response(serializer.data)
models.py
class User(models.Model):
USER_CHOICES = (
(1, u'ADMIN'),
(2, u'SALES'),
(3, u'KITCHEN'),
(4, u'EMPLOYEE'),
)
image = models.ImageField(upload_to='employees/', null = True, blank = True)
name = models.CharField(max_length=50)
user_id = models.CharField(max_length=30, primary_key = True, blank = False)
email = models.CharField(max_length=50)
password = models.CharField(max_length=100)
is_active = models.BooleanField(default=True)
user_group = models.PositiveSmallIntegerField(default=4, choices=USER_CHOICES)
firebase_token = models.CharField(max_length=150, default=None, null = True)
shop = models.ForeignKey(Shop, on_delete=models.SET_DEFAULT, default=None)
phone = PhoneField(blank=False, default=0)
serializers.py
class UserUpdateSerializer(serializers.ModelSerializer):
shop = serializers.CharField()
class Meta:
model = User
fields = ('image', 'url', 'phone', 'name', 'user_id','email', 'password', 'is_active', 'user_group', 'shop')
def update(self, instance, validated_data):
shop = validated_data.pop('shop')
user_id = validated_data.pop("user_id")
print(user_id)
shopId = Shop.objects.filter(name=shop).values('shop_id').first()
if shopId is not None:
shop_id = shopId['shop_id']
try:
if user_id is not None:
instance.name = validated_data.get('name', instance.name)
instance.image = validated_data.get('image', instance.image)
instance.email = validated_data.get('email', instance.email)
instance.phone = validated_data.get('phone', instance.phone)
instance.password = validated_data.get('password', instance.password)
instance.user_group = validated_data.get('user_group', instance.user_group)
instance.shop_id = validated_data.get('shop_id', instance.shop_id)
instance.is_active = True
instance.save()
return instance
else:
print("Test")
except Exception as e:
return e
else:
return None
This is throwing an error saying that the user already exists!
Please assist!
Just Change your serializer like below
class UserUpdateSerializer(serializers.ModelSerializer):
shop = serializers.CharField()
class Meta:
model = User
fields = ('image', 'url', 'phone', 'name', 'user_id','email', 'password', 'is_active', 'user_group', 'shop')
def update(self, instance, validated_data):
shop = validated_data.pop('shop')
shop_obj = Shop.objects.filter(name=shop).first()
if shop_obj:
instance.shop = shop_obj
return super().update(instance, validated_data)
When you are calling UserUpdate API, it will create every new User for every new user_id.
I don't know why you are updating user_id. Any new user_id will create a new User instance.
If you want to update other than user_id, you need to remove the following line.
Remove these lines from UserUpdate API View.
instance.user_id = request.data.get("user_id")
instance.save()
Remove this line from UserUpdateSerializer
instance.user_id = validated_data.get('user_id', instance.user_id)
Add lookup_field = 'user_id' to UserUpdate API view
Add user_id at url, like
path('userupdate/<str:user_id>/', UserUpdate.as_view())

Include auto increment field to JSON response

New to Django and Django Rest.
I am looking to modify the JSON return response of my view so that it also returns the id of the created user.
The things is, the id is an auto increment integer and so I don't know how I can retrieve the info to add it to the JSON.
Any help would be appreciated.
Thanks.
models.py
class CustomUsers(AbstractUser):
email = models.EmailField(unique=True)
username = models.CharField(max_length=100)
USERNAME_FIELD = 'email'
class Meta:
db_table = "custom_users"
serializers.py
class CustomUsersCreateSerializer(serializers.ModelSerializer):
def create(self, validated_data):
last_name = validated_data['last_name']
first_name = validated_data['first_name']
username = validated_data['username']
email = validated_data['email']
password = validated_data['password']
user_obj = USER(
last_name=last_name,
first_name=first_name,
username=username,
email=email,
)
user_obj.set_password(password)
user_obj.save()
return validated_data
class Meta:
model = USER
fields = ('id', 'last_name', 'first_name', 'username', 'password', 'email')
extra_kwargs = {'password': {'write_only': True, 'min_length': 10}}
views.py
class UserCreateAPIView(CreateAPIView):
serializer_class = serializers.CustomUsersCreateSerializer
queryset = CustomUsers.objects.all()
I usually use Model.objects.create() to add new record but i think it's the same as your way of using save() so you can get created user id after save:
def create(self, validated_data):
last_name = validated_data['last_name']
first_name = validated_data['first_name']
username = validated_data['username']
email = validated_data['email']
password = validated_data['password']
user_obj = User.objects.create(
last_name=last_name,
first_name=first_name,
username=username,
email=email,
)
user_obj.set_password(password)
user_obj.save()
validated_data['user_id'] = user_obj.id
return validated_data