AttributeError at /accounts/signup/ 'str' object has no attribute 'add' - python

Am new using django so i used practical example on this tutorial on extending user model using User Profile, am having trouble on form.py it gives that error, on removing this line;
supervisor.su_mobile_number.add(*self.cleaned_data.get('su_mobile_number'))
it works smoothly but no data for su_mobile_number was inserted o the database
view.py
class SupervisorSignUpView(CreateView):
model = User
form_class = SupervisorSignUpForm
template_name = 'registration/signup_form.html'
def get_context_data(self, **kwargs):
kwargs['user_type'] = 'supervisor'
return super().get_context_data(**kwargs)
def form_valid(self, form):
user = form.save()
login(self.request, user)
return redirect('home')
model.py
class User(AbstractUser):
is_supervisor = models.BooleanField(default=False)
is_student = models.BooleanField(default=False)
class Supervisor(models.Model):
user = models.OneToOneField('User', on_delete=models.CASCADE, primary_key=True, related_name='supervisor')
su_mobile_number = models.CharField(max_length=200)
forms.py
class SupervisorSignUpForm(UserCreationForm):
su_mobile_number = forms.CharField()
class Meta(UserCreationForm.Meta):
model = User
#transaction.atomic
def save(self):
user = super().save(commit=False)
user.is_supervisor = True
user.save()
supervisor = Supervisor.objects.create(user=user)
supervisor.su_mobile_number.add(*self.cleaned_data.get('su_mobile_number'))
return user

Use = operator to assign value and then call save() method
#transaction.atomic
def save(self):
user = super().save(commit=False)
user.is_supervisor = True
user.save()
supervisor = Supervisor.objects.create(user=user)
supervisor.su_mobile_number = self.cleaned_data.get('su_mobile_number')
supervisor.save()
return user
more shorter way is (as #Abdul Niyas P M mentioned)
#transaction.atomic
def save(self):
user = super().save(commit=False)
user.is_supervisor = True
user.save()
supervisor = Supervisor.objects.create(user=user, su_mobile_number=self.cleaned_data.get('su_mobile_number'))
return user

Related

how to populate user field with current user in django models via forms?

hi i am working on a django app. functionality that i am implementing is to let my user buy a internet pack from the website.
i have implemented the model, view, template and url so far. but in the form i am getting a drop down list of all the users registered on the app. i automatically want django to link the user with current logged in user and let him select the pack he wants to buy and populate the model(table) automatically.
My models.py
def get_deadline():
return dt.today() + timedelta(days=30)
class CustomUser(AbstractUser):
Address = models.CharField(max_length=500)
def __str__(self):
return self.username
class Plans(models.Model):
plan_name = models.CharField(max_length=50)
speed = models.IntegerField()
price = models.FloatField()
def __str__(self):
return self.plan_name
class Orders(models.Model):
user = models.ForeignKey(CustomUser, on_delete = models.CASCADE)
pack = models.ForeignKey(Plans, on_delete = models.CASCADE)
start_date = models.DateField(auto_now_add=True)
end_date = models.DateField(default=get_deadline())
is_active = models.BooleanField(default=True)
def __str__(self):
name = str(self.user.username)
return name
my views.py
class UserBuyPlan(LoginRequiredMixin, View):
template = 'plans/plan.html'
#success_url = reverse_lazy('autos:all')
success_url = reverse_lazy('home-home')
def get(self, request):
form = BuyPlanForm()
ctx = {'form': form}
return render(request, self.template, ctx)
def post(self, request):
form = BuyPlanForm(request.CustomUser,request.POST)
if not form.is_valid():
ctx = {'form': form}
return render(request, self.template, ctx)
make = form.save()
return redirect(self.success_url)
my forms.py (i tried searching online and found this init implementation but it doesnt work)
class BuyPlanForm(forms.ModelForm):
class Meta():
model = Orders
fields = "__all__"
def __init__(self, *args, **kwargs):
self.user = CustomUser
super(BuyPlanForm, self).__init__(*args, *kwargs)
self.fields['user'].initial = self.user
the photo of resulting form is attached below
ok so i found the answer.
just had to change my view function a bit.
if you want a detailed tutorial then please visit https://www.youtube.com/watch?v=-s7e_Fy6NRU&t=1840s
he explains in a much better way.
class UserBuyPlan(LoginRequiredMixin, CreateView):
model = Orders
template_name = 'plans/plan.html'
fields = ['pack']
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
and also had to add absolute url method to my orders model
class Orders(models.Model):
user = models.ForeignKey(CustomUser, on_delete = models.CASCADE)
pack = models.ForeignKey(Plans, on_delete = models.CASCADE)
start_date = models.DateField(auto_now_add=True)
end_date = models.DateField(default=get_deadline())
is_active = models.BooleanField(default=True)
def __str__(self):
name = str(self.user.username)
return name
def get_absolute_url(self):
return reverse('home-home')
rest everything is same.

str has no object add in django

Getting this error message when running manage.py
AttributeError at /accounts/signup/teacher/ 'str' object has no
attribute 'add'
This is the code I have written->
forms.py
class TeacherSignUpForm(UserCreationForm):
email = forms.EmailField(max_length=100)
firstname = forms.CharField(max_length=100)
lastname = forms.CharField(max_length=100)
phonenumber = forms.IntegerField(required=True)
linkedin = forms.URLField(max_length=200)
class Meta(UserCreationForm.Meta):
model = User
def save(self, commit=True):
user = super().save(commit=False)
user.is_teacher = True
if commit:
user.save()
mentor = Mentor.objects.create(user=user)
mentor.email.add(*self.cleaned_data.get('email'))
mentor.firstname.add(*self.cleaned_data.get('firstname'))
mentor.lastname.add(*self.cleaned_data.get('lastname'))
mentor.phonenumber.add(*self.cleaned_data.get('phonenumber'))
mentor.linkedin.add(*self.cleaned_data.get('linkedin'))
return user
models.py
#mentor model
class Mentor(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
email = models.EmailField(max_length=100)
firstname = models.CharField(max_length=100)
lastname = models.CharField(max_length=100)
linkedin = models.URLField(max_length=200,null=True,blank=True)
phonenumber = models.IntegerField(null=True,unique=True)
and views
class TeacherSignUpView(CreateView):
model = User
form_class = TeacherSignUpForm
template_name = 'registration/signup_form.html'
def get_context_data(self, **kwargs):
kwargs['user_type'] = 'teacher'
return super().get_context_data(**kwargs)
def form_valid(self, form):
user = form.save()
login(self.request, user)
return redirect('teachers:app-instructor-dashboard')
I get this error message when I try and login as a mentor/teacher. Which should then redirect me to app-instructor-dashboard
Your view does not make much sense. An EmailField and a UrlField are not much more than CharFields with some extra validation. These are not collections (lists, etc.) of emails.
You thus should assign the values like:
class TeacherSignUpForm(UserCreationForm):
email = forms.EmailField(max_length=100)
firstname = forms.CharField(max_length=100)
lastname = forms.CharField(max_length=100)
phonenumber = forms.IntegerField(required=True)
linkedin = forms.URLField(max_length=200)
class Meta(UserCreationForm.Meta):
model = User
def save(self, commit=True):
self.instance.is_teacher = True
user = super().save()
mentor = Mentor.objects.create(
user=user,
email=self.cleaned_data['email'],
firstname=self.cleaned_data['firstname'],
lastname=self.cleaned_data['lastname'],
phonenumber=self.cleaned_data['phonenumber'],
linkedin=self.cleaned_data['linkedin']
)
return user
Note that it will be necessary to first save the user object. A User object that is not created at the database side, can not be used to create a Mentor object, since then it has no primary key to work with.

Django Rest Framework: Issue with extended User model and serialization

I' extending the default Django user model to make a customised user profile with additional fields.The following are the related components.
models.py
class CandidateProfile(models.Model):
user = models.OneToOneField(
User, on_delete=models.CASCADE, related_name="user")
exp = models.IntegerField(null=True, blank=True)
serilaizers.py
class CandidateProfileSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(source='pk', read_only=True)
username = serializers.CharField(source='user.username')
email = serializers.CharField(source='user.email')
groups = serializers.RelatedField(read_only=True)
password = serializers.CharField(max_length=128, source='user.password,read_only=True')
class Meta:
model = CandidateProfile
fields = ('id', 'username', 'password', 'email', 'groups')
depth = 1
def update(self, instance, validated_data):
print("In Update" + '*' * 50)
user = User.objects.get(pk=instance.user.pk)
user = instance.user
user.email = validated_data.get('user.email', user.email)
user.first_name = validated_data.get('user.first_name',
user.first_name)
user.last_name = validated_data.get('user.last_name', user.last_name)
user.save()
instance.gender = validated_data.get('gender', instance.gender)
instance.save()
return instance
def create(self, validated_data):
print('*' * 100)
print(validated_data)
user_data = validated_data.pop('user')
print(user_data)
user = User.objects.create_user(**user_data)
g = Group.objects.get(name="Candidate")
g.user_set.add(user)
user.save()
print(validated_data)
print('*' * 100)
profile = CandidateProfile.objects.create(user=user, **validated_data)
return user
views.py
class CandidateRegister(APIView):
def get(self, request, format=None):
candidate_list = User.objects.filter(groups=Group.objects.get(
name="Candidate"))
serializer = CandidateProfileSerializer(candidate_list, many=True)
return Response(serializer.data)
def post(self, request, format=None):
serializer = CandidateProfileSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
I've succcessfully created the user profile as well as the extended Candidate profile.But i'm encoutering an error on doing the same as follows :
Got AttributeError when attempting to get a value for field `username` on serializer `CandidateProfileSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `User` instance.
Original exception text was: 'CandidateProfile' object has no attribute 'username'.
Even with this execpiton the User profile and the related Candidate profile is created.
You can use the SerializerMethodField from docs like -
class CandidateProfileSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(source='pk', read_only=True)
username = serializers.SerializerMethodField()
def get_username(self, obj):
return obj.user.username

Anonymous user error

I'm trying to save form data to an anonymous user, however I get the below error when trying to save some form data in a CreateView". I'm not clear what the issue is?
ValueError: Cannot assign "<SimpleLazyObject: <django.contrib.auth.models.AnonymousUser object at 0x11126bc18>>": "EUser.user" must be a "User" instance.
Models:
class EUser(models.Model):
online_account = models.BooleanField()
supplier1 = models.OneToOneField(SupplierAccount)
supplier2 = models.OneToOneField(SupplierAccount)
supplier3 = models.OneToOneField(SupplierAccount)
address = models.OneToOneField(Address)
user = models.ForeignKey(settings.AUTH_USER_MODEL)
class SupplierAccount(models.Model):
supplier = models.ForeignKey(Supplier)
username = models.CharField(max_length=255)
password = models.CharField(max_length=255)
Form:
class ServiceTypeForm(forms.ModelForm):
# BOOL_CHOICES = ((False, 'No'), (True, 'Yes'))
# online_account = forms.BooleanField(widget=forms.RadioSelect(choices=BOOL_CHOICES))
def __init__(self, *args, **kwargs):
super(ServiceTypeForm, self).__init__(*args, **kwargs)
self.fields['service_type'].initial = 'D'
class Meta:
model = EUser
fields = ('service_type', 'online_account')
View:
class ServiceTypeView(CreateView):
form_class = ServiceTypeForm
template_name = "standard_form.html"
success_url = '/'
def form_valid(self, form):
form.instance.user = self.request.user
super().form_valid(form)
online_account = form.cleaned_data['online_account']
if online_account:
return redirect('../online')
else:
return redirect('../address')
If the user is not logged in, then request.user is an anonymous user. It doesn't make sense to assign an anonymous user to form.instance.user, because an anonymous user does not exist in the database or have a primary key.
How you change your code depends on how you want your application to work.
If you want to allow anonymous users to create service types, then
# if self.request.user.is_authenticated(): # Django < 1.10
if self.request.user.is_authenticated:
form.instance.user = self.request.user
For this to work, you would need to change the user field to make it optional.
user = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True)
After making this change, you'll need to run makemigrations and then migrate, to update the database.
Another option would be to restrict the view to logged in users. In Django 1.9+, You can do this with the LoginRequiredMixin.
from django.contrib.auth.mixins import LoginRequiredMixin
class ServiceTypeView(LoginRequiredMixin, CreateView):
...
I think you can not use the AnonymousUser as value for a ForeignKey to a User.
You should keep is as Null in this case.
class EUser(models.Model):
...
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, default=None)
class ServiceTypeView(CreateView):
...
def form_valid(self, form):
if self.request.user.is_authenticated():
form.instance.user = self.request.user
...

Extending the User Model

I want to extend the User model with custom fields, but I have a little problem
I started creating a Profiles model, with the extra fields that I want:
"models.py":
class Profiles(models.Model):
user = models.OneToOneField(User)
url = models.URLField()
avatar = models.ImageField(upload_to='profile_avatar')
def __str__(self):
return self.user.username
Then, I made a custom UserCreationForm, and I override the save function with the data that I want in the original User model:
"forms.py":
class UserForm(UserCreationForm):
first_name = forms.CharField()
last_name = forms.CharField()
email = forms.EmailField()
url = forms.URLField()
avatar = forms.ImageField()
class Meta:
model = User
fields = ("username", "first_name", "last_name", "email",)
def save(self, commit=True):
user = super(UserForm, self).save(commit=False)
user.first_name = self.cleaned_data["first_name"]
user.last_name = self.cleaned_data["last_name"]
user.email = self.cleaned_data["email"]
if commit:
user.save()
return user
Finally, I made a FormView to create the new user and save the extra data in the custom Profiles model:
"views.py":
class SignIn(FormView):
template_name = 'index/signin.html'
form_class = UserForm
success_url = reverse_lazy('signin')
def form_valid(self, form):
user = form.save()
profile = Profiles()
profile.user = user
profile.url = form.cleaned_data['url']
profile.avatar = form.cleaned_data['avatar']
profile.save()
return super(SignIn, self).form_valid(form)
The first user that I sign in with the UserForm creates correctly and the profile model is created too, but after the second sign in, Django prints this Integrity Error: '(1062, "Duplicate entry '' for key 'mail'")'; creating correctly the user, but no the profile one
Do you have any idea of what I'm doing bad?
Thank you

Categories