I'm stuck with the initialization of my userprofileform in the post method of a CBV and the instance receive a None object from the call to super()...
the model is quite standard : a user and his profile; the uerprofile model has an user attribute (onetoone)
models.py
[...]
class User(AbstractUser):
"""User model."""
username = None
email = models.EmailField(_('email address'), unique=True)
first_name = models.CharField(_('first name'), unique=False, max_length=100)
last_name = models.CharField(_('last name'), unique=False, max_length=100)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
phone = models.CharField(max_length=15,null=True)
website = models.URLField(blank=True, null=True) upload_to=PathAndRename("avatars/"))
photo = models.ForeignKey(Photo, null=True, on_delete=models.PROTECT)
organization = models.CharField(_('organization'), null=True,unique=False, max_length=100)
city = models.CharField(_('city'), unique=False, max_length=100, null=True)
country = CountryField(blank_label=_('select country'), null=True)
subscribe_newsletter = models.BooleanField(default=False)
language = models.CharField(max_length=10,
choices=settings.LANGUAGES,
default=settings.LANGUAGE_CODE)
User.profile = property(lambda u: UserProfile.objects.get_or_create(user=u)[0])
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
def save(self, *args,**kwargs):
#import ipdb; ipdb.set_trace()
self.city=(self.city or '').lower()
self.organization=(self.organization or '').lower()
self.website=(self.website or '').lower()
super(UserProfile, self).save(*args,**kwargs)
self.user.save()
[...]
forms.py
[...]
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('first_name', 'last_name', 'phone', 'website','city','country','organization','subscribe_newsletter')
def __init__(self, *args, **kwargs):
user = kwargs.pop('user',None)
super(UserProfileForm, self).__init__(*args, **kwargs)
try:
self.instance.user=user
except:
pass
try:
self.fields['first_name'].initial = self.instance.user.first_name
self.fields['last_name'].initial = self.instance.user.last_name
except User.DoesNotExist:
pass
[...]
views.py
class ProfileView(LoginRequiredMixin, View):
def __init__(self,*args,**kwargs):
self.context={}
super().__init__()
def get(self,request):
profile_form = UserProfileForm(instance=request.user.profile, prefix='profile')
self.context['profile_form']=profile_form
return render(request,'account/profile.html', self.context)
def post(self,request):
#import ipdb; ipdb.set_trace()
if request.POST.get('action',)=='profile':
profile_form = UserProfileForm(request.POST, user=request.user, prefix='profile')
if profile_form.is_valid():
profile_form.save()
info={'title':_('PROFILE SAVED !'),'desc':_('your profile has been successfully updated')}
self.context['infos']=[info]
return render(request,'info.html', self.context)
else:
self.context['errors']=get_form_errors_list(profile_form.errors)
elif request.POST.get('action',)=='avatar':
pass
self.context['profile_form']=profile_form
return render(request,'account/profile.html', self.context)
[...]
The error occurs in the post method of the view when I affect UserProfileForm to profile_form. The debug toolbar return "UserProfile has no user".
The init method of UserProfileForm works with the get method of the view (the instance is set whith the user.profile) but the instance is not given while posting (throwing user.profile would be nonsense as the data are contained in the resquest.POST)...
Related
I cannot login to any account, because I receive an error:
Please enter the correct email and password for a staff account. Note that both fields may be case-sensitive.(for an admin user)
And
Please enter a correct email and password. Note that both fields may be case-sensitive.
That happens after I update a profile through the profile-detail page. It just throws me to the login page after I press the Update button on the profile-update page.
Here is all the related code:
models.py
class Customer(AbstractUser):
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
objects = UserManager()
customer_id = models.AutoField(primary_key=True)
first_name = models.CharField(max_length=50, null=True, blank=True)
last_name = models.CharField(max_length=50, null=True, blank=True)
username = models.CharField(max_length=30, null=True, blank=True)
phone = models.CharField(max_length=10, default='', null=True, blank=True)
email = models.EmailField(validators=[validators.EmailValidator()], unique=True)
description = models.TextField(max_length=1000,blank=True, null=True)
gender = models.CharField('Gender', max_length=10, choices=Gender.choices,
default='Male', null=True)
featured_img = models.ImageField(verbose_name='A profile image',
upload_to='profiles',
default='products/profile_default.jpg')
password = models.CharField(max_length=100, null=True, blank=True)
date_created = models.DateTimeField(auto_now_add=True, null=True)
def __str__(self):
return f'{self.email} {self.username} {self.customer_id}'
#staticmethod
def get_customer_by_email(email):
try:
return Customer.objects.get(email=email)
except:
return False
def exists(self):
if Customer.objects.filter(email=self.email):
return True
return False
class Meta:
verbose_name = 'Customer'
verbose_name_plural = 'Customers'
# unique_together = ['email']
class Profile(models.Model):
# USERNAME_FIELD = 'email'
profile_id = models.AutoField(primary_key=True)
date_created = models.DateTimeField(auto_now_add=True, null=True)
updated = models.DateTimeField(auto_now=True, null=True)
user = models.ForeignKey(Customer, on_delete=models.CASCADE,
related_name="customer", null=True)
class Meta:
verbose_name = 'Profile'
verbose_name_plural = 'Profiles'
# unique_together = ['email']
def __str__(self):
return f' {self.profiled}'
managers.py
class UserManager(BaseUserManager):
def create_user(self, email, first_name=None, description=None, gender=None, featured_img=None, username=None, last_name=None, phone=None, password=None):
if not email:
raise ValueError("User must have an email")
if not password:
raise ValueError("User must have a password")
user = self.model(
email=self.normalize_email(email),
)
user.first_name = first_name
user.username = username
user.last_name = last_name
user.password = make_password(password) # change password to hash
user.phone = phone
user.featured_img = featured_img
user.description = description
# user.profile = profile
user.gender = gender
user.admin = False
user.staff = True
user.active = True
user.save(using=self._db)
return user
def create_superuser(self, email, username, password):
if not email:
raise ValueError("User must have an email")
if not password:
raise ValueError("User must have a password")
user = self.model(
email=self.normalize_email(email)
)
user.username = username
user.password = make_password(password) # chang password to hash
user.admin = True
user.staff = True
user.active = True
user.save(using=self._db)
return user
views.py
#csrf_exempt
def signup(request):
if request.method == 'POST':
form = SignUpForm(request.POST or None)
email = request.POST['email']
if Customer.get_customer_by_email(email=email) == False:
if form.is_valid():
user = form.save(commit=False)
# account = authenticate(request,
# username=email,
# password=request.POST['password'])
user.username = user.username.lower()
user.save()
login(request, user,
backend='allauth.account.auth_backends.AuthenticationBackend')
messages.success(request, 'The account was successfully created!!!')
return redirect(reverse_lazy('products:products'))
messages.error(request, f'{form.errors}')
return redirect(reverse_lazy('user-auth:register'))
return redirect(reverse_lazy('user-auth:login'))
form = SignUpForm()
context = {'form': form, 'user': request.user}
return render(request, 'auth/register/register.html', context)
class ProfileDetailView(
DetailView):
context_object_name = 'customer'
template_name = 'auth/profile_detail.html'
def get_object(self):
profile = Profile.objects.filter(user__customer_id=self.kwargs['pk']).first()
return profile.user
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['user'] = self.request.user
return context
class UpdateProfileView(LoginRequiredMixin,
UpdateView):
template_name = 'auth/profile_update.html'
form_class = ProfileUpdateForm
context_object_name = 'customer'
def get_success_url(self):
success_url = reverse_lazy('user-auth:profile-detail',
kwargs={'pk': self.request.user.customer_id})
return success_url
#method_decorator(ensure_csrf_cookie, name='dispatch')
def post(self, request, *args, **kwargs):
profile = self.get_object()
form = ProfileUpdateForm(instance=profile)
# if request.method == 'POST':
form = ProfileUpdateForm(request.POST, request.FILES, instance=profile)
if form.is_valid():
if profile:
form.save()
messages.success(request, 'Successfully updated!')
return redirect(self.get_success_url())
messages.error(request, 'Profile does not exist!')
return redirect(reverse_lazy('user-auth:signup'))
messages.error(request, 'Invalid data!')
return render(request, self.template_name, self.get_context_data())
def get_object(self):
profile = Profile.objects.filter(user__customer_id=self.kwargs['pk']).first()
return profile.user
# def get(self, request, *args, **kwargs):
# context = {}
# context["form"] = ProfileUpdateForm(instance=self.get_object())
# context['user'] = request.user
# return render(request, self.template_name, context)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["form"] = ProfileUpdateForm(instance=self.get_object())
context['user'] = self.request.user
return context
class DeleteProfileView(LoginRequiredMixin,
DeleteView):
context_object_name = 'customer'
template_name = 'auth/profile_confirm_delete.html'
def get_success_url(self):
success_url = reverse_lazy('user-auth:profile-detail',
kwargs={'pk': self.request.user.customer_id})
return success_url
def post(self, request, *args, **kwargs):
self.request = request
if self.get_object:
messages.success(request, 'Profile deleted successfully!')
return super().delete(request, *args, **kwargs)
messages.success(request, 'Profile does not exist!')
return redirect(reverse_lazy('user-auth:signup'))
def get_object(self):
profile = Profile.objects.filter(user__customer_id=self.kwargs['pk']).first()
return profile.user
signals.py
#receiver(post_save, sender=Customer)
def create_profile(sender, instance, created, **kwargs):
user = instance
if created:
print(user)
Profile.objects.create(
user=user
)
#receiver(pre_save, sender=Customer)
def update_profile(sender, instance, **kwargs):
# print(instance)
profile = instance
if profile.customer_id is not None:
Profile.objects.update(user=profile)
#receiver(post_delete, sender=Customer)
def delete_profile(sender, instance, **kwargs):
user = instance
customer = Customer.objects.filter(email=user.email).first()
if user and customer:
# profile.delete()
customer.delete()
print('Not exists...')
forms.py
class SignUpForm(UserCreationForm):
class Meta:
model = Customer
fields = ('username','phone', 'first_name', 'last_name', 'email', 'featured_img')
# def __init__(self, *args, **kwargs):
# super(SignUpForm, self).__init__(*args, **kwargs)
# for name, field in self.fields.items():
# field.widget.attrs.update({'class': 'input'})
class ProfileUpdateForm(forms.ModelForm):
password1 = forms.CharField(
label="Password",
strip=False,
widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}),
help_text=password_validation.password_validators_help_text_html(),
required=False
)
password2 = forms.CharField(
label="Password confirmation",
widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}),
strip=False,
help_text="Enter the same password as before, for verification.",
required=False
)
class Meta:
model = Customer
fields = ('username', 'phone', 'first_name', 'last_name', 'email', 'featured_img', 'description', 'gender')
def save(self, commit=True):
customer = super().save(commit=False)
email = self.cleaned_data.get('email')
customer.email = email.lower()
# customer.password = customer.set_password(self.cleaned_data['password1'])
if commit:
if customer.exists():
super(ProfileUpdateForm, self).save()
return customer
urls.py
from django.contrib.auth import views as auth_views
app_name = 'user-auth'
urlpatterns = [
path('register/', views.signup, name='register'),
path('accounts/login/', auth_views.LoginView.as_view(
template_name='auth/login/login.html',
success_url='products/'),
name='login'
),
path('accounts/logout/', auth_views.LogoutView.as_view(), name='logout'),
path('profile-detail/<int:pk>/', views.ProfileDetailView.as_view(), name='profile-detail'),
path('profile-update/<int:pk>/', views.UpdateProfileView.as_view(),
name='profile-update'),
path('profile-delete/<int:pk>/', views.DeleteProfileView.as_view(),
name='profile-delete'),
]
I have tried to delete my database and fill it in once again. Then I tried to find out why the email or password dis incorrect. Maybe they are wrong in the database. But I have no idea what's going on.
So I am trying to use Mixin, and the aim is to check if the requester is the owner of the object (owner is a field in my model).
However, I am unable to do such a thing, with a result of 'TweetsUpdateView' object has no attribute 'object', what is wrong in my code?
My models
class Tweets(models.Model):
description = models.TextField(blank=True, null=False, default="", max_length=255)
createdAt = models.DateTimeField(auto_now_add=True, null=True, blank=True)
updatedAt = models.DateTimeField(auto_now=True)
owner = models.ForeignKey(
settings.AUTH_USER_MODEL,
blank=True,
null=True,
on_delete=models.SET_NULL,
related_name="owner_tweets_set",
)
user_likes = models.ManyToManyField(settings.AUTH_USER_MODEL)
My view
class OwnerRequiredMixin(UserPassesTestMixin):
def dispatch(self, request, *args, **kwargs):
if self.object.owner != self.request.user:
return HttpResponseForbidden()
return super(OwnerRequiredMixin, self).dispatch(request, *args, **kwargs)
class TweetsUpdateView(
LoginRequiredMixin,
OwnerRequiredMixin,
UpdateView,
):
model = Tweets
# fields = ["description"]
# template_name = "tweets_form.html"
template_name_suffix = "_form"
form_class = TweetForm
def form_invalid(self, form):
print("form is invalid")
return HttpResponse("form is invalid.. this is just an HttpResponse object")
# slug_field = "id"
# slug_url_kwarg = "tweet_id"
# success_url = reverse_lazy("tweets:twitter")
success_url = reverse_lazy("tweets:twitter")
you can access objects by using self.object = self.get_object()
as it hasn't been called yet
I have two models staff and UserAddress and I'm trying to add the staff through models. But I get this error:
AttributeError at /accounts/signup/staff/
'NoneType' object has no attribute 'add'
What should I do now, Could some one help me?
I wanted to register a staff but this thing happened.
models.py:
class UserAddress(models.Model):
city = models.CharField(max_length=100)
address = models.CharField(max_length=200)
zip_code = models.CharField(max_length=15, blank=True)
def __str__(self):
return str(self.id)
class CustomUser(AbstractUser):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
email = models.EmailField(unique=True)
user_address = models.ForeignKey(UserAddress, on_delete=models.CASCADE, null=True)
class Staff(CustomUser):
def save(self, *args, **kwargs):
if not self.id:
self.is_staff = True
self.is_superuser = False
return super(Staff, self).save(*args, **kwargs)
class Meta:
proxy = True
forms.py:
class StaffCreationForm(UserCreationForm):
first_name = forms.CharField(max_length=100, required=True)
last_name = forms.CharField(max_length=100, required=True)
email = forms.EmailField(required=True)
city = forms.CharField(max_length=100)
address = forms.CharField(max_length=100)
zip_code = forms.CharField(max_length=15, required=True)
class Meta(UserCreationForm.Meta):
model = CustomUser
#transaction.atomic
def save(self):
user = super().save(commit=False)
user.first_name = self.cleaned_data.get("first_name")
user.last_name = self.cleaned_data.get("last_name")
user.email = self.cleaned_data.get("email")
user.is_staff = True
user.save()
address = UserAddress.objects.create(city=self.cleaned_data.get("city"),
address=self.cleaned_data.get("address"),
zip_code=self.cleaned_data.get("zip_code"))
user.user_address.add(address)
return user
views.py:
class StaffSignUpView(CreateView):
model = CustomUser
form_class = StaffCreationForm
template_name = "accounts/signup_user.html"
def get_context_data(self, **kwargs):
kwargs["user_type"] = "staff"
return super().get_context_data(**kwargs)
def form_valid(self, form):
user = form.save()
login(self.request, user)
return redirect("home")
Your user.user_address object does not contain any value that is why you're getting this error 'NoneType' object has no attribute 'add' because default it's value is None and None datatype does not contain add() method so try this
user.user_address = address
user.save() # don't forget to save user
this will assign address object to user.user_address
I've used as a model AbstractUser extended by custom fields, created form automatically by ModelForm. The problem is that, users except superuser cannot log in to system. I think it's reason, their passwords are not hashing. Where should I make it ? Here are my codes.
forms.py:
class CustomUserSignUpForm(ModelForm):
class Meta:
model = CustomUser
fields = ['username', 'password', 'user_image', 'role', 'branch', 'license_number', 'fin_number', 'first_name', 'last_name', 'patronymic', 'phone_number', 'email', 'voen_number', 'is_active']
views.py:
def sign_up(request):
if request.method == 'POST':
form = CustomUserSignUpForm(request.POST)
if form.is_valid():
form.save()
else:
form = CustomUserSignUpForm()
context = {
'form': form,
}
return render(request, 'sign_up.html', context)
models.py:
class CustomUser(AbstractUser):
patronymic = models.CharField(_('Ata adı'), max_length=150, blank=True)
role = models.ForeignKey(Role, on_delete=models.CASCADE, blank=True, null=True)
user_image = models.FileField(_('Profil şəkli'), upload_to='static/assets/images/user-images', blank=True)
branch = models.ForeignKey(Branch, on_delete=models.CASCADE, blank=True, null=True)
phone_number = models.CharField(_('Telefon'), max_length=20, blank=True)
voen_number = models.CharField(_('VÖEN'), max_length=30, blank=True)
fin_number = models.CharField(_('FİN'), max_length=20, blank=True)
license_number = models.CharField(_('Lisenziya'), max_length=40, blank=True)
def __str__(self):
return self.username
To define a function to hash that password, you must inherited save method for you user form
class CustomUserSignUpForm(forms.ModelForm):
............
def save(self, commit=True):
# Save the provided password in hashed format
user = super(CustomUserSignUpForm, self).save(commit=False)
user.set_password(self.cleaned_data["password"])
if commit:
user.save()
return user
This override of ModelForm is better off, because:
I check if the user exists.
I Hash de password if the password is not encoded.
class UsuarioAdmin(admin.ModelAdmin):
...
def save_model(self, request, obj, form, change):
try:
user_database = USUARIO.objects.get(pk=obj.pk)
except Exception:
user_database = None
if user_database is None \
or not (check_password(form.data['password'], user_database.password)
or user_database.password == form.data['password']):
obj.password = make_password(obj.password)
else:
obj.password = user_database.password
super().save_model(request, obj, form, change)
After extending an existing user model, I get RelatedObjectDoesNotExist exception with a value User has no dcf_profile. I seems that dcf_profile isn't created automatically for each user.
Please take a look at my model, view and form below and tell me how can I correct my views file?
My models.py :
class CustomUser(models.Model):
auth_user_ptr = models.OneToOneField(
User,
parent_link=True,
related_name='dcf_profile',
primary_key=True
)
phone = models.CharField(_('phone'), max_length=30, null=True, blank=True)
receive_news = models.BooleanField(_('receive news'), default=True, db_index=True)
class Meta:
app_label = 'dcf'
def allow_add_item(self):
if self.item_set.count() > settings.DCF_ITEM_PER_USER_LIMIT:
return False
else:
return True
class Item(models.Model):
slug = models.SlugField(blank=True, null=True, max_length=100)
user = models.ForeignKey(User, on_delete=models.CASCADE)
group = models.ForeignKey(Group, verbose_name=_('group'))
title = models.CharField(_('title'), max_length=100)
description = models.TextField(_('description'))
price = models.DecimalField(_('price'), max_digits=10, decimal_places=2)
phone = models.CharField(_('phone'), max_length=30)
is_active = models.BooleanField(_('display'), default=True, db_index=True)
updated = models.DateTimeField(_('updated'), auto_now=True, db_index=True)
posted = models.DateTimeField(_('posted'), auto_now_add=True)
def __unicode__(self):
return self.title
class Meta:
verbose_name = _('item')
verbose_name_plural = _('items')
ordering = ('-updated', )
def get_absolute_url(self):
return reverse('item', kwargs={
'pk': self.pk,
'slug': self.slug
})
def get_title(self):
return u'%s' % self.title
def get_description(self):
return u'%s' % self.description[:155]
def get_keywords(self):
# TODO need more optimal keywords selection
return ",".join(set(self.description.split()))
def get_related(self):
# TODO Need more complicated related select
return Item.objects.exclude(pk=self.pk)[:settings.DCF_RELATED_LIMIT]
def save(self, *args, **kwargs):
if self.slug is None:
self.slug = slugify(unidecode(self.title))
super(Item, self).save(*args, **kwargs)
My views.py :
class ItemCreateView(FormsetMixin, CreateView):
is_update_view = False
model = Item
form_class = ItemCreateEditForm
formset_class = inlineformset_factory(Item, Image, extra=3, fields=('file', ))
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
if not self.request.user.dcf_profile.allow_add_item():
messages.error(self.request, _('You have reached the limit!'))
return redirect(reverse('my'))
return super(ItemCreateView, self).dispatch(*args, **kwargs)
def form_valid(self, form, formset):
form.instance.user = self.request.user
form.save()
return super(ItemCreateView, self).form_valid(form, formset)
My forms.py
class ItemCreateEditForm(forms.ModelForm):
class Meta:
model = Item
fields = ('group', 'title', 'description', 'price', 'phone', 'is_active')
Just to share with you a solution that worked, I have created a proxy model of the User model and updated my views.py :
Updated models.py
class DcfUser(User):
class Meta:
proxy = True
def allow_add_item(self):
if self.item_set.count() > settings.DCF_ITEM_PER_USER_LIMIT:
return False
else:
return True
Updated views.py
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
user = self.request.user
my_user = DcfUser.objects.get(username=user)
if not my_user.allow_add_item():
messages.error(self.request, _('You have reached the limit!'))
return redirect(reverse('my'))
Thank you