I'm trying to use the set_password() function but this error
'Member' object has no attribute 'set_password'
comes up when I use it. If I take out the set_password() function the password is stored in the database but without being hashed.
view.py
user = Member(username=u, password=p, email=e, security=s)
user.set_password(p)
user.save()
models.py
class Member(models.Model):
username = models.CharField(max_length=16,primary_key=True)
password = models.CharField(max_length=16)
email = models.CharField(max_length=325)
security = models.CharField(max_length=16)
profile = models.OneToOneField(Profile, null=True)
following = models.ManyToManyField("self", symmetrical=False)
from_member_id = models.CharField(max_length=16)
def __str__(self):
return self.username
The set_password function is not automatically provided by models.Model.
You have to define it by yourself or derive Member from django Usermodel
The documentation on providing your own user model is quite clear and comprehensive. Among other things, your model must be a subclass of AbstractBaseUser, which is what provides the set_password method.
Also note that 16 characters is not nearly long enough to store a hashed, salted password.
As the error message tells you, the method set_password is not defined.
Either you implement it yourself, or (better) create your Member model by subclassing django's AbrstactBaseUser:
class MyUser(AbstractBaseUser):
username = models.CharField(max_length=16,primary_key=True)
password = models.CharField(max_length=16)
email = models.CharField(max_length=325)
security = models.CharField(max_length=16)
profile = models.OneToOneField(Profile, null=True)
following = models.ManyToManyField("self", symmetrical=False)
from_member_id = models.CharField(max_length=16)
USERNAME_FIELD = 'username'
You can find more about custom user models in the django docs
Not sure if your doing this but it is probably easier to make the model with OneToOneField(User) and give it additional fields. You just need to remember to save in the new fields or the fields wont show up when u call.
Where you set the user_form=Member(request.POST)
user = user_form.save()
user.set_password(user.password)
profile = user.userprofile
profile.bio = request.POST['bio']
profile.save()
Related
I have created a CustomUser model and it has a one to relationship with my two other models.
class User(AbstractUser):
is_learner = models.BooleanField(default=False)
is_teacher = models.BooleanField(default=False)
class Teacher(models.Model):
#teacher_name = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, primary_key=True)
class Learner(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, primary_key=True)
They do not show up when I try to create learner and teacher objects in their respective databases, as shown:
class LearnerSignUpForm(UserCreationForm):
email = forms.EmailField()
class Meta(UserCreationForm.Meta):
#User = CustomUser
model = CustomUser
fields = ["username", "email", "password1", "password2"]
#fields = UserCreationForm.Meta.fields + ("username", "email", "password1", "password2")
#transaction.atomic
def save(self, *args, **kwargs):
user = super().save(commit=False)
user.is_learner = True
user.save()
learner = Learner.objects.create(user=user)
return user
How do I get it to save in learner and teacher tables respectively?
Welcome to StackOverflow!
It is worth pointing that using model.objects.create(...) you're implicitly telling Django that you want to create the object with the specific values you give and save it. check the docs page here
If you wish to save an object for which has associated items, you can create a form and use a ModelChoiceField and provide the model and default choices using the choices kwarg. An example of a choices value would be YourUserModel.objects.all() However, I think in your case you may not want to give the user the freedome to make this choice, in which it would be correct to override the save() method of your form and create the intended logic.
Secondly, I have used Django for quite some time and never seen a Model.Meta class used in this way so forgive me if I'm mistaken, but I also think you need have your save() method directly on your LearnerSignupForm and not on the Meta class.
Thirdly, if you setup your forms correctly, using the correct types of fields, Django forms will deal with all the messy stuff for you and complain at you when you do something wrong (usually gracefully).
Lastly, I would highly recommend having a read through the docs page for creating new objects
Free code:
class LearnerSignUpForm(UserCreationForm):
email = forms.EmailField()
class Meta(UserCreationForm.Meta):
model = CustomUser
fields = ["username", "email", "password1", "password2"]
#transaction.atomic
def save(self, *args, **kwargs):
user = super().save(commit=False)
user.is_learner = True
# create the relationship between the user and learner
learner = user.learner_set.create(user=user)
# user.learner_set.add(learner) # usually this way with FK
user.save()
return user # should this return the Learner object?
One final bit of advice:
Try to make your code as readable as possible! There's no reason that code can't be functional and beautiful! And where possible, you should make you class names and variable names as appropriate as possible, I would for example maybe use Student instead of Learner
I am new to Django, Please forgive any silly mistakes in code or logic,
Intro: I am trying to create a user follower model in Django. Where users can follow and unfollow other users on the sites
Error: I have made the models for my follow/unfollow I have also made the views I am getting this error
AttributeError at /accounts/admin/follow/
Cannot use add() on a ManyToManyField which specifies an intermediary model. Use accounts.Contact's Manager instead.
The obj.followers.add(user) is highlighted in the traceback as the origin of the error
Below are my models.py
from django.contrib.auth.models import User
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
city = models.CharField(max_length=100)
country = models.CharField(max_length=100)
def get_absolute_url(self):
return reverse('accounts:profile', kwargs={'username': self.user.username})
class Contact(models.Model):
user_from = models.ForeignKey(User, related_name='suppporter')
user_to = models.ForeignKey(User, related_name='leader')
def __str__(self):
return '{} follows {}'.format(self.user_from, self.user_to)
User.add_to_class('following',
models.ManyToManyField('self', through=Contact, related_name='followers', symmetrical=False))
I think the models.py may be good. The fault I believe is in my views.
Below is my view.py
class FollowToggle(LoginRequiredMixin, RedirectView):
def get_redirect_url(self, *args, **kwargs):
username = self.kwargs.get('username')
print(username + " This is the user who will be followed") # This prints correct
profile = get_object_or_404(Profile, user__username=username)
print(profile) # This prints correct
obj = get_object_or_404(User, username=username)
print(obj) # This prints correct
url_ = profile.get_absolute_url()
print(url_) # This prints correct
user = self.request.user
print(user) # This prints correct
if user.is_authenticated():
if user in obj.followers.all(): # I know this is the source of the error.
obj.followers.remove(user)
else:
obj.followers.add(user)
return url_
Below are the Urls.py just in case
url(r'^(?P<username>[-\w]+)/follow/$', views.FollowToggle.as_view(), name='follow'),
You cannot use add and remove method for manytomany relation defined through third model. From the docs:
Unlike normal many-to-many fields, you can’t use add(), create(), or set() to create relationships
Instead you should use Contact manager:
if user.is_authenticated():
if user in obj.followers.all(): # I know this is the source of the error.
Contact.objects.filter(user_to=obj, user_from=user).delete()
else:
Contact.objects.create(user_to=obj, user_from=user)
In Django 2.2 you can use add, remove and set methods (Docs)
You can also use add(), create(), or set() to create relationships, as long as your specify through_defaults for any required fields
In django, how do we set fields that were not defined in class Meta?`
I have this model:
class User(models.Model):
user_name = models.CharField(max_length=32, unique=True)
email = models.EmailField(unique=True)
first_name = models.CharField(max_length=32)
last_name = models.CharField(max_length=32)
password_hash = models.CharField(max_length=64)
password_salt = models.CharField(max_length=64)
and this form:
class ContactForm(ModelForm):
password = forms.CharField(widget=forms.PasswordInput())
repeat_password = forms.CharField(widget=forms.PasswordInput())
class Meta:
model = User
fields = ['user_name', 'email', 'first_name', 'last_name']
def clean(self):
cleaned_data = super(ContactForm, self).clean()
password = cleaned_data.get('password')
repeat_password = cleaned_data.get('repeat_password')
if password is not None and repeat_password is not None:
if password != repeat_password:
raise forms.ValidationError("Passwords do not match.")
password_validate = password_validate = password_validation.validate_password(password)
if password_validate is not None:
raise password_validate
I only want the user to be able to set user_name, email, first_name and last_name fields. In clean(self) I check if the entered passwords match and if they are valid.
What I want to do now is to set password_hash and password_salt of my model User. How can this be done?
The clean method in the ModelForm only validates the fields in a form.
It does not set any data to the models. What you are doing in your clean method is that, you are checking whether the posted data is in the required format you intended or expected. You could look more into that here in the docs..
For "setting" fields as you said you may need to override the form save method.
Here in the docs..
The save() method¶
Every ModelForm also has a save() method. This method creates and saves a database object from the data bound to the form. A subclass of ModelForm can accept an existing model instance as the keyword argument instance; if this is supplied, save() will update that instance.
From what I understand you could do something like this,
def save(self, commit=True):
instance = super(ContactForm, self).save(commit=False)
instance.password_hash = #...How you want to do your password_hash is here.
instance.save()
If you are looking for manually creating/managing passwords, I think this maybe what you are looking for :
Setting passwords manually
make_password(password[, salt, hashers])
Creates a hashed password in the format used by this application. It takes one mandatory argument: the password in plain-text. Optionally, you can provide a salt and a hashing algorithm to use, if you don’t want to use the defaults (first entry of PASSWORD_HASHERS setting). Currently supported algorithms are: 'pbkdf2_sha256', 'pbkdf2_sha1', 'bcrypt_sha256' (see Using bcrypt with Django), 'bcrypt', 'sha1', 'md5', 'unsalted_md5' (only for backward compatibility) and 'crypt' if you have the crypt library installed. If the password argument is None, an unusable password is returned (a one that will be never accepted by check_password()).
I have made Custom User model in my Django project. Here it is:
class CustomUser(User):
avatar = models.ImageField(upload_to='avatars')
about_myself = models.TextField(max_length=300)
USERNAME_FIELD = 'username'
def __str__(self):
return self.username
def is_author(self):
return 'blog.change_post' and 'blog.add_post' in self.get_all_permissions()
And after it, I changed all Foreign Keys of user to new CustomUser model. It works OK. But I make one new migration and django cause error, when I want to migrate it:
ValueError: Lookup failed for model referenced by field blog.Comment.author: main.CustomUser
My blog.Comment model:
class Comment(models.Model):
content = models.TextField()
author = models.ForeignKey(CustomUser)
date_create = models.DateTimeField(auto_now_add=True)
post = models.ForeignKey(Post)
What should I do?
Thanks!
Judging from the code you posted, you might be might be better served by extending the user model rather than replacing it. This pattern is usually called a profile model and works via a one-to-one relationship with User.
Profiles provides application specific fields and behaviors, while allowing User to go about it's usual business unchanged. It doesn't require you to muck around with rewriting auth or even necessarily change your foreign keys.
Here's an example of your code written as a profile:
class Profile(models.Model):
# Link to user :
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
avatar = models.ImageField(upload_to='avatars')
about_myself = models.TextField(max_length=300)
def __str__(self):
return self.user.username
def is_author(self):
return 'blog.change_post' and 'blog.add_post' in self.user.get_all_permissions()
Comment model:
class Comment(models.Model):
content = models.TextField()
author = models.ForeignKey(settings.AUTH_USER_MODEL)
date_create = models.DateTimeField(auto_now_add=True)
post = models.ForeignKey(Post)
# How to access the profile:
def check_author(self):
self.author.profile.is_author()
You'll also want to add a signal to create a new profile when a user is registered:
#receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_profile_for_new_user(sender, created, instance, **kwargs):
if created:
profile = Profile(user=instance)
profile.save()
Django docs on extending users.
If a profile approach doesn't work for you, try inheriting from AbstractUser or AbstractBaseUser instead of User. The abstract models provide the same basic functionality as User and are the preferred technique for recent Django versions.
There are a handful of additional steps however, check out the docs on creating custom users for a run down.
I use the django.auth system and I've this:
class RegisterForm(UserCreationForm):
username = forms.RegexField(label= "Username" , max_length = 30, regex = r'^[\w]+$', error_messages = {'invalid': "This value may contain only letters, numbers and _ characters."})
email = forms.EmailField(label = "Email")
first_name = forms.CharField(label = "First name", required = False)
last_name = forms.CharField(label = "Last name", required = False)
class Meta:
model = User
fields = ("username", "first_name", "last_name", "email", )
def save(self, commit = True):
user = super(RegisterForm, 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
I want to set emails as uniques and check the form for this validation. How can I do it?
Somewhere in your models:
from django.contrib.auth.models import User
User._meta.get_field('email')._unique = True
Notice the underscore before unique. This is where the information is actually held. User._meta.get_field('email').unique is just a #property which looks into it.
This should work for syncdb too, so you will have consistency with the database.
Note too, that from Django 1.5 you will not have to do such things, as User model will be pluggable.
add this to your form. But this isn't perfect way. race condition is available by only using this form. I recommend you to add unique constraint at db level.
def clean_email(self):
data = self.cleaned_data['email']
if User.objects.filter(email=data).exists():
raise forms.ValidationError("This email already used")
return data
SQL to add unique constraint:
ALTER TABLE auth_user ADD UNIQUE (email)
I am not sure how to use this, but
from django.contrib.auth.models import User
User._meta.get_field_by_name('email')[0].unique=True
should do it. I guess this goes in your models.py before you run syncdb on the auth model. Bout to try this myself.
Overriding the clean() method as suggested by mumimo is described here:
http://docs.djangoproject.com/en/dev/topics/forms/modelforms/#overriding-the-clean-method
Ordinarily you could use unique=True on your field definition and then use ModelForm, so that it'll automatically run clean() for all of the fields in your form, but if you're using the django.auth user class you can't modify the fields.
You can do the same thing using this in Abstract User Model:
class User(AbstractUser):
...
class Meta:
unique_together = ('email',)
While doing the registration, i found one thing that email is not required in django auth but while simple validation, if we don't provide the email it gives 500 error as we must be checking for the email at the backend.
So to make it required, add to the registrationSerializer;
extra_kwargs = {'email': {'required': True}}
For unique email, other answers are showing that.
You can make the email unique and even allow it to be null by adding the following code in your models.py
from django.db import models
from django.contrib.auth.models import User
User.email = models.EmailField(_("email address"), blank=True, null=True, unique=True)
Tested on Django 4.0.3