I am facing one issue with django forms
Here is my model :
class User(models.Model):
email = models.EmailField(unique=True)
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
class UserProfile(AuditFields):
user = models.ForeignKey(User, on_delete=models.CASCADE)
designation = models.CharField(max_length=200, blank=True)
contact_number = models.CharField(max_length=20, blank=True)
team = models.CharField(max_length=200, blank=True)
manager = models.CharField(max_length=200, blank=True)
joining_date = models.DateField(default=datetime.now)
I need to create a form for editing profile details of the current user
This is my form. But it is a model Form so only getting the detauls from the User Profile table only
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
exclude = ['user']
How can I get first_name , last_name from User table and save it
Just add the fields as a CharField in form, and use cleaned_data attribute to fetch the data and save it:
class UserProfileForm(forms.ModelForm):
first_name = forms.CharField(max_length=30,required=True)
last_name = forms.CharField(max_length=30,required=True)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.instance:
self.fields['first_name'].initial = self.instance.user.first_name
self.fields['last_name'].initial = self.instance.user.last_name
class Meta:
model = UserProfile
exclude = ['user']
def save(self, commit=False):
instance = super().save(commit=True)
user = instance.user
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.save()
return instance
Related
I have a custom user class and a profile class. Profile class has a OneToOne relation with the custom User. the Serializer is having User as Meta model with adding Profile model in a new field profile extended to the fields tuple. but When I try to get the detail view it returns an error saying Profile field is not an attribute of CustomUser.
I would appreciate if you go over the code that I added below and help me through this.
The User model:
class CustomUser(AbstractBaseUser, PermissionsMixin):
class Types(models.TextChoices):
DOCTOR = "DOCTOR", "Doctor"
PATIENT = "PATIENT", "Patient"
# what type of user
type = models.CharField(_("Type"), max_length=50, choices=Types.choices, null=True, blank=False)
avatar = models.ImageField(upload_to="avatars/", null=True, blank=True)
email = models.EmailField(max_length=255, unique=True)
name = models.CharField(max_length=255)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
objects = CustomBaseUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['name', 'type'] #email is required by default
def get_full_name(self):
return self.name
def __str__(self):
return self.email
The Profile Model:
class DoctorProfile(models.Model):
"""Model for Doctors profile"""
class DoctorType(models.TextChoices):
"""Doctor will choose profession category from enum"""
PSYCHIATRIST = "PSYCHIATRIST", "Psychiatrist"
PSYCHOLOGIST = "PSYCHOLOGIST", "Psychologist"
DERMATOLOGIST = "DERMATOLOGIST", "Dermatologist"
SEXUAL_HEALTH = "SEXUAL HEALTH", "Sexual health"
GYNECOLOGIST = "GYNECOLOGIST", "Gynecologist"
INTERNAL_MEDICINE = "INTERNAL MEDICINE", "Internal medicine"
DEVELOPMENTAL_THERAPIST = "DEVELOPMENTAL THERAPIST", "Developmental therapist"
owner = models.OneToOneField(CustomUser, on_delete=models.CASCADE, related_name='doctor_profile')
doctor_type = models.CharField(
_("Profession Type"),
max_length=70,
choices=DoctorType.choices,
null=True,
blank=False)
title = models.IntegerField(_('Title'), default=1, choices=TITLES)
date_of_birth = models.DateField(null=True, blank=False)
gender = models.IntegerField(_('Gender'), default=1, choices=GENDERS)
registration_number = models.IntegerField(_('Registration Number'), null=True, blank=False)
city = models.CharField(_('City'), max_length=255, null=True, blank=True)
country = models.CharField(_('Country'), max_length=255, null=True, blank=True)
def __str__(self):
return f'profile-{self.id}-{self.title} {self.owner.get_full_name()}'
Serializer:
class DoctorProfileFields(serializers.ModelSerializer):
"""To get the fields from the DoctorProfile. it will be used in the DoctorProfileSerializer"""
class Meta:
model = DoctorProfile
fields = ('doctor_type', 'title', 'date_of_birth', 'registration_number', 'gender', 'city', 'country', )
class DoctorProfileSerializer(serializers.ModelSerializer):
"""retrieve, update and delete profile"""
profile = DoctorProfileFields()
class Meta:
model = User
fields = ('name', 'avatar', 'profile', )
#transaction.atomic
def update(self, instance, validated_data):
ModelClass = self.Meta.model
profile = validated_data.pop('profile', {})
ModelClass.objects.filter(id=instance.id).update(**validated_data)
if profile:
DoctorProfile.objects.filter(owner=instance).update(**profile)
new_instance = ModelClass.objects.get(id = instance.id)
return new_instance
View:
class DoctorProfileAPIView(generics.RetrieveUpdateDestroyAPIView):
"""To get the doctor profile fields and update and delete"""
serializer_class = DoctorProfileSerializer
queryset = User.objects.all()
def get_object(self):
return get_object_or_404(User, id=self.request.user.id, is_active=True)
What I want is a json response in the detail view like below:
{
"name": the name,
"avatar": avatar,
"profile": {
"doctor_type": "PSYCHIATRIST",
"title": 1,
"date_of_birth": 11-11-1990,
"registration_number": 21547,
}
}
Can Anybody guide me through this..? Or is there any other design approach that meets my objective. My objective is to have the user info + profile info combined in a single endpoint as a whole Profile in the frontend from which the user will see/edit profile.
First of all move the foreign key OneToOne in the CustomUser model, add:
owner = models.OneToOneField('DoctorProfile', on_delete=models.CASCADE, related_name='doctor_profile')
and delete from DoctorProfile:
owner = models.OneToOneField(CustomUser, on_delete=models.CASCADE, related_name='doctor_profile')
Make all migrations, and now you have to set new data in the db.
In the serializers you are using Nested relationships correctly, so add the attribute many set to False:
profile = DoctorProfileFields(many=False)
Edit
If you cant edit the structure of your models, you can work with SerializerMethodField (not tested):
class DoctorProfileSerializer(serializers.ModelSerializer):
class Meta:
model = DoctorProfile
fields = ('doctor_type', 'title', 'date_of_birth', 'registration_number')
class CustomDoctorProfileSerializer(serializers.Serializer):
name = serializers.SerializerMethodField()
avatar = serializers.SerializerMethodField()
profile = DoctorProfileSerializer(many=False)
def get_name(self, obj)
return obj.doctor_profile.name
def get_avatar(self, obj)
return obj.doctor_profile.avatar
I am trying to update Customer Profile also updating main Customuser first_name and last_name field at the same time using nested serialization. But I am getting customer field must be unique error.
I have posted the pics here.
My models:
class CustomUser(AbstractUser):
# username = None
first_name = models.CharField(max_length=255, verbose_name="First name")
last_name = models.CharField(max_length=255, verbose_name="Last name")
email = models.EmailField(unique=True)
is_seller = models.BooleanField(default=False)
is_customer = models.BooleanField(default=False)
USERNAME_FIELD = "email"
REQUIRED_FIELDS = ["first_name", "last_name"]
objects = CustomUserManager()
def __str__(self):
return self.email
class Customer(models.Model):
customer = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, blank=True, null=True)
full_name = models.CharField(max_length=100, blank=True)
phone_num = models.CharField(max_length=50, blank=True)
#dob = models.CharField(max_length=255,blank=True,null=True)
region = models.CharField(max_length=255, blank=True,null=True)
city = models.CharField(max_length=255, blank=True, null=True)
area = models.CharField(max_length=255,blank=True,null=True)
address = models.CharField(max_length=255, blank=True, null=True)
def __str__(self):
return self.customer.email
My serializers:
class CustomerProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Customer
fields = '__all__'
# depth = 1
class CustomerUpdateSerializer(serializers.ModelSerializer):
customer = CustomerProfileSerializer()
class Meta:
model = User
fields = ('id', "first_name", "last_name",'customer')
def update(self,request, instance, validated_data):
user = self.request.user
instance.user.first_name=user.get('first_name')
instance.user.last_name = user.get('last_name')
instance.user.save()
customer_data = validated_data.pop('customer',None)
if customer_data is not None:
instance.customer.region = customer_data['region']
instance.customer.city = customer_data['city']
instance.customer.area = customer_data['area']
instance.customer.address = customer_data['address']
instance.customer.save()
return super().update(instance,validated_data)
My views:
class CustomerUpdateView(UpdateAPIView):
permission_classes = [IsAuthenticated]
queryset = User.objects.all()
serializer_class = CustomerUpdateSerializer
The url is visible in postman put request. I have sent the raw json data in the postman, but it throws this error. How to update those 4 fields in customer and 2 fields (first_name and last_name) from User model??
My code worked after I made some changes to the serializer.
Here is the working code:
class CustomerUpdateSerializer(serializers.ModelSerializer):
customer = CustomerProfileSerializer(many=False)
class Meta:
model = User
fields = ('id', "first_name", "last_name",'customer')
depth = 1
def update(self, instance, validated_data):
user = self.context['request'].user
user.first_name = validated_data.get('first_name')
user.last_name = validated_data.get('last_name')
user.save()
customer_data = validated_data.pop('customer',None)
if customer_data is not None:
instance.customer.region = customer_data['region']
instance.customer.city = customer_data['city']
instance.customer.area = customer_data['area']
instance.customer.address = customer_data['address']
instance.customer.save()
return super().update(instance,validated_data)
Its because I was using instance of user and same instance to customer model. Now, I get the fields first_name and last_name separately and use instance for customer only.It worked.
how to add current active user as foreign key to the create post model in djangorestframework ?
models:
class DoctorProfile(AbstractBaseUser, PermissionsMixin):
id=models.AutoField(primary_key=True)
name = models.CharField(_('name'), max_length=50, blank=True)
mobile = models.CharField(_('mobile'), unique=True, max_length=10, blank=False)
email = models.EmailField(_('email address'), blank=True)
password = models.CharField(_('password'),max_length=25,blank=False)
otp = models.IntegerField(null=True, blank=True)
class Doctor_clinic(models.Model):
clinic_id = models.AutoField(primary_key=True)
doc_profile = models.ForeignKey(DoctorProfile,on_delete=models.CASCADE)
clinic_name = models.CharField(max_length=150)
clinic_address = models.CharField(max_length=150)
City = models.CharField(max_length=50)
state = models.CharField(max_length=50)
pincode = models.IntegerField()
#how to get the forign key in serializers
I wrote in this way, is this correct/relevent?
class UserSerializer(serializers.ModelSerializer):
# mobile = serializers.RegexField("[0-9]{10}",min_length=10,max_length=10)
password = serializers.CharField(write_only=True)
email=serializers.EmailField(max_length=155,min_length=3,required=True)
name=serializers.CharField(max_length=55,min_length=3,required=True)
class Meta:
model = DoctorProfile
fields = ("name", "email", "password", "mobile","otp")
class ClinicSerializer(serializers.ModelSerializer):
class Meta:
model = Doctor_clinic
fields =('clinic_name','clinic_address', 'City', 'state', 'pincode','doc_profile')
views:
class ClinicRegistrationView(generics.ListCreateAPIView):
serializer_class = ClinicSerializer
queryset = Doctor_clinic.objects.all()
permission_classes = (IsAuthenticated,)
When serializing relations you need to define a seperate field depending on the representation you want, for example write your serializer like this:
class ClinicSerializer(serializers.ModelSerializer):
doc_profile = serializers.StringRelatedField()
class Meta:
model = Doctor_clinic
fields =('clinic_name','clinic_address', 'City', 'state', 'pincode','doc_profile')
permissions.py
use permission classes
class IsOwner(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
return obj.doc_profile == request.user
views.py
def perform_create(self, serializer):
return serializer.save(doc_profile=self.request.user)
I'm looking to delete or empty a specific row in my table/model in my UpdateView. I have a team and employees in the team. I have made an update view that when "yes" is pressed, the team becomes archived. I want to additionally delete or empty the employee's numbers when doing so. How would I approach that?
I know it might be weird, but the idea is that the employee's numbers should be destroyed once the team is archived, while the rest of the data still stands.
Team Model
class Team(models.Model):
slug = models.SlugField(max_length=200)
teamname = models.CharField(max_length=50, help_text="Indtast holdnavn.", null=False, primary_key=True)
is_active = models.BooleanField(default=True)
Employee Model
class Employee(models.Model):
id = models.AutoField(primary_key=True)
slug = models.SlugField(max_length=200)
emp_num = models.IntegerField(help_text="Indtast medarbejderens MA-nummer. (F.eks 123456)")
firstname = models.CharField(max_length=30, help_text="Indtast medarbejderens fornavn.")
lastname = models.CharField(max_length=30, help_text="Indtast medarbejderens efternavn.")
teamname = models.ForeignKey('Hold', on_delete=models.CASCADE, null=True)
UpdateView
My updateView is using team, as its that model I'm updating.
class ArchiveHoldView(UpdateView):
template_name = 'evalsys/medarbejder/archive_hold.html'
model = Team
form_class = ArchiveForm
def archive_view_team_with_pk(self, slug=None):
if slug:
team = Team.objects.get(slug=slug)
else:
team = self.team
args = {'team': team}
return render(self, 'evalsys/medarbejder/archive_hold.html', args)
def get_context_data(self, **kwargs):
context = super(ArchiveHoldView, self).get_context_data(**kwargs)
context['is_active'] = Team.objects.get(slug=self.kwargs.get('slug'))
return context
def get_success_url(self):
return reverse_lazy("evalsys:home")
Update View form
class ArchiveForm(forms.ModelForm):
def save(self, *args, **kwargs):
self.instance.is_active = False
return super(ArchiveForm, self).save(*args, **kwargs)
is_active = BooleanField(required=False, widget=forms.HiddenInput())
class Meta:
model = Team
fields = ['is_active', ]
labels = {'is_active': 'Is Active'}
I cannot update using an inline form.
I thought it was possible before, but it wasn't.
I'm trying to solve it, but it doesn't work.
I added.
I will post any other necessary items.
"Id
This field is required.
user
A Profile with this User already exists.
"
I got an error.
#view
class UserEdit(generic.UpdateView):
model = User
form_class = forms.UserUpdateForm
template_name = 'accounts/accounts_edit.html'
success_url = reverse_lazy('person:myaccount')
def get_object(self):
return get_object_or_404(User, pk=self.request.user.user_id)
#model
class User(AbstractBaseUser, PermissionsMixin):
username_validator = UnicodeUsernameValidator()
user_id = models.UUIDField(default=uuid_lib.uuid4,
primary_key=True, editable=False)
username = models.CharField(_('username'), unique=True, max_length=50,validators=[username_validator],error_messages={
'unique': _("A user with that username already exists."),
},)
class profile(models.Model):
image = models.ImageField(upload_to='profile/',default='profile/default.jpg')
first_name = models.CharField(_('first name'), max_length=30, blank=True,null=True)
last_name = models.CharField(_('last name'), max_length=150, blank=True,null=True)
birthday = models.DateField(_('birthday',),null=True)
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE,)
#form
class ProfileUpdateForm(BaseModelForm):
class Meta:
model = profile
fields = ('first_name','last_name','birthday',)
ProfileFormSet = inlineformset_factory(User,profile,form=ProfileUpdateForm,extra=0)
class UserUpdateForm(mixins.ModelFormWithFormSetMixin,BaseModelForm):
formset_class = ProfileFormSet
class Meta:
model = User
fields = ('username','email',)
#mixin
class ModelFormWithFormSetMixin:
def __init__(self, *args, **kwargs):
super(ModelFormWithFormSetMixin, self).__init__(*args, **kwargs)
self.formset = self.formset_class(
instance=self.instance,
data=self.data if self.is_bound else None,
)
def is_valid(self):
return super(ModelFormWithFormSetMixin, self).is_valid() and self.formset.is_valid()
def save(self, commit=True):
saved_instance = super(ModelFormWithFormSetMixin, self).save(commit)
self.formset.save(commit)
return saved_instance