I'm trying to store the First name and last name straight from the Facebook API to a User Auth model (which is extended with FacebookProfile model, containing webpull, id and year_formed)
Models.py
class FacebookProfile(models.Model):
user = models.OneToOneField(User, on_delete = models.CASCADE)
id = models.PositiveIntegerField(primary_key = True)
#name = models.CharField(max_length = 200, null = True)
year_formed = models.PositiveIntegerField(default = 0)
webpull= models.CharField(max_length =1000, null = True)
Views.py
if request.method == 'GET':
print 'im here'
return render(request, "logV2.html")
if request.method == "POST":
first_name = request.POST.get('first_name')
last_name = request.POST.get('last_name')
print first_name, last_name
facebook_user = FacebookUserForm(data=request.POST)
facebook_profile = FacebookProfileForm()
has_account = authenticate(first_name = first_name, last_name = last_name)
if has_account:
print 'this has account'
login(request, has_account)
return HttpResponseRedirect('/music/home/')
else:
id_ = request.POST.get('id')
birthday = request.POST.get('year_formed')
webpull = request.POST.get('webpull')
if birthday == "undefined":
print 'im emplty'
year_formed = random.randint(1993,1998)
else:
year_formed = re.findall(r"[0-9][0-9][0-9][0-9]$", birthday)[0]
print id_, year_formed, webpull
print facebook_user
user = facebook_user.save()
profile = facebook_profile.save(commit = False)
profile.user = user
profile.webpull = webpull
profile.id = id_
## steal birtday fucntion from log
# move to new database facebook (neeed to change all artists to facebookprofile)
profile.year_formed = year_formed
profile.save()
#authenticate user. then log him in.
#user = authenticate(username = profile.user.username)
now_has_account = authenticate(first_name = first_name, last_name = last_name)
login(request, now_has_account)
#profile.save()
return HttpResponseRedirect('/music/home/')
In the views the code brakes at user = facebook_user.save()
I tried clearing the whole database,
also
What I'm receiving from the html is a form with first_name,last_name,id,year_formed,webpull. The data gets to the backend fine.
Forms.py
class FacebookUserForm(forms.ModelForm):
class Meta:
model = User
fields = ('first_name', 'last_name')
class FacebookProfileForm(forms.ModelForm):
class Meta:
model = FacebookProfile
fields = ('id', 'year_formed', 'webpull',)
what im authenticating
Auth_Backend.py
class OnlynameandsurnameAuth(ModelBackend):
def authenticate(self, first_name = None, last_name = None):
try:
return User.objects.get(first_name = first_name, last_name = last_name)
except:
return None
then
auth.py
admin.site.register(FacebookProfile)
the backend authentification
settings.py
AUTHENTICATION_BACKENDS = (
# ... your other backends
#'music.auth_backend.PasswordlessAuthBackend',
'music.auth_backend.OnlynameandsurnameAuth',
)
any ideas how to save the first_name and last_name without having a UNIQUE error?
Thanks! :D
If you use the default authenticate function not providing a password should always (?) fail, which means that
has_account = authenticate(first_name = first_name, last_name = last_name)
always will be None.
But the main problem is that you do not set a username for the new User, only first_name and last_name. This will work once, but after one User with an empty username was created the next attempt will fail, as Users need an unique username.
So: Add a username!
Besides that, I think that
user = facebook_user.save()
does not assign the User to "user" but the Form.
You should use facebook_user.instance.
Related
This is a learning project and I've been trying to create a custom user model with the possibility of having multiple accounts with the same nickname. The USERNAME_FIELD would be username = f"{nickname}#{random_number}" (just like the Blizzard and Discord format), but I can't figure out at which step I should include the function that would automatically create the username.
I tried generating the username in the CustomAccountManager.create_user() method but it doesn't seem to be called while filling the RegistrationForm from the view and it fails to create the superuser with manage.py createsuperuser (TypeError: create_superuser() got an unexpected keyword argument 'username'). I'd be glad if someone could explain how to procede in this case or link a resource, I couldn't find one with this kind of example. Any additional insight on the code will be appreciated.
I have registered the model in admin.py with admin.site.register(Account), included the accounts app in the settings and set AUTH_USER_MODEL = 'account.Account'
account/models.py
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
from random import randint
from django.contrib.auth import get_user_model
### ACCOUNT
class CustomAccountManager(BaseUserManager):
def create_user(self, email, nick, password = None):
if not email:
raise ValueError("Email was not provided during user creation. (account/models.py/CustomAccountManager.create_user)")
if not nick:
raise ValueError("Username was not provided during user creation. (account/models.py/CustomAccountManager.create_user)")
if len(nick) >= 20:
raise ValueError("Username cannot be longer than 20 characters")
user_id = self.generate_id(nick)
user = self.model(
nick = nick,
username = f"{nick}#{user_id}",
user_id = user_id,
email = self.normalize_email(email),
)
user.set_password(password)
user.save(using = self._db)
return user
def create_superuser(self, nick, email, password = None):
user = self.create_user(
email = self.normalize_email(email),
nick = nick,
password = password,
)
user.is_admin = True
user.is_staff = True
user.is_superuser = True
user.save(using = self._db)
return user
def generate_id(self, nick: str) -> int: # I want this to be a random number
database = []
accounts = self.model.objects.filter(nick = nick)
if len(accounts) > 995:
raise ValueError("Cannot create an account with this username, please choose another one. (account/models.py/CustomAccountManager.generate_id)")
for account in accounts:
database.append(int(getattr(account, "user_id")))
while True:
potential_id = randint(0, 999)
if potential_id not in database:
return potential_id
class Account(AbstractBaseUser):
### PERSONAL INFO
first_name = models.CharField(max_length = 20, blank = True, null = True)
second_name = models.CharField(max_length = 40, blank = True, null = True)
last_name = models.CharField(max_length = 20, blank = True, null = True)
### FUNCTIONAL PERSONAL INFO
email = models.EmailField(max_length = 60) #/#
username = models.CharField(max_length = 25, unique = True) #/# = f"{self.nick}#{self.user_id}"
nick = models.CharField(max_length = 20, default = "")
user_id = models.IntegerField(default = 777)
### FUNCTIONAL DJANGO
USERNAME_FIELD = "username" #/#
REQUIRED_FIELDS = ["email",]
is_active = models.BooleanField(default = True) #/#
is_admin = models.BooleanField(default = False) #/#
is_staff = models.BooleanField(default = False) #/#
is_superuser = models.BooleanField(default = False) #/#
objects = CustomAccountManager()
### LOGS
date_joined = models.DateTimeField(auto_now_add = True) #when account was made
last_login = models.DateTimeField(blank = True, null = True) #/#
rehabilitate = models.DateTimeField(blank = True, null = True) #date when user gets unbanned
banned_amount = models.IntegerField(default = 0) #collective hours banned
banned_times = models.IntegerField(default = 0) #amount of times someone was banned
logins = models.JSONField(default = dict)
logouts = models.JSONField(default = dict)
bans = models.JSONField(default = dict)
unbans = models.JSONField(default = dict)
### PERMISSIONS - REQUIRED
def has_perm(self, perm, obj) -> bool:
return self.is_admin
def has_module_perms(self, app_label) -> bool:
return self.is_admin
account/views.py:
from django.http.request import HttpRequest
from django.shortcuts import render, redirect
from django.contrib.auth import login, authenticate, get_user_model
from account.forms import RegistrationForm
from account.models import Account
def RegistrationView(request):
context = {}
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
form.save()
return redirect('index')
else:
context['registration_form'] = form
elif request.method == 'GET':
form = RegistrationForm()
context['registration_form'] = form
return render(request, 'account/registration.html', context)
account/forms.py
from django.contrib.auth.forms import UserCreationForm
from django.forms import ModelForm
from django.core.exceptions import ValidationError
from .models import Account
class RegistrationForm(UserCreationForm):
class Meta:
model = Account
fields = ("email", "nick", 'password1', 'password2',
"first_name", "second_name", "last_name")
I have overwritten the save() method in the Account model this way:
def save(self, *args, **kwargs):
if not int(self.user_id):
self.user_id = self.generate_id(self.nick) # moved from CustomAccountManager class
self.username = f"{self.nick}#{self.user_id}"
super().save(*args, **kwargs)
I'm still not sure this is the best way, but it seems to work.
I am trying to post a simle form to create a user. but whenever i try to save the form data it always gives me UNIQUE constraint failed error even if i pass the new mobile number that does not exist on database.
ERROR IS: UNIQUE constraint failed: core_user.mobile_no
models.py
Manager Class is:
class UserManager(BaseUserManager):
def create_user(self, username, password=None, **extra_fields):
"""Creates and saves a new user"""
if not password:
raise ValueError("User must have a password")
if not username:
raise ValueError("User must have an username")
user = self.model(username=username, **extra_fields)
user.set_password(password)
user.save(using=self.db)
return user
def create_staff_user(self, username, password=None, **kwargs):
user = self.create_user(username, password, is_staff=True, **kwargs)
return user
def create_super_user(self, username, password=None):
user = self.create_user(self, username=username, password=password, is_staff=True, is_super_user=True)
return user
Model class is:
class User(AbstractBaseUser):
user_types = (
("staff", "Staff"),
("super_user", "Super User"),
)
first_name = models.CharField(max_length=100)
middle_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
username = models.CharField(max_length=100, unique=True)
email = models.EmailField()
mobile_no = models.CharField(max_length=10, unique=True)
is_active = models.BooleanField(default=True) # can login
is_staff = models.BooleanField(default=False) # staff user
is_super_user = models.BooleanField(default=False) # super user
created_date = models.DateTimeField(auto_now_add=True)
USERNAME_FIELD = 'username'
objects = UserManager()
# USERNAME_FIELD and password are required by default
REQUIRED_FIELDS = [] # e.g full_name
def __str__(self):
return self.username
Views.py
class UserCreationView(CreateView):
template_name = "form.html"
form_class = UserCreationForm
success_url = "/"
def form_valid(self, form):
username = form.cleaned_data['username']
password = form.cleaned_data['password']
first_name = form.cleaned_data['first_name']
middle_name = form.cleaned_data['middle_name']
last_name = form.cleaned_data['last_name']
mobile_no = form.cleaned_data['mobile_no']
email = form.cleaned_data['email']
user_type = form.cleaned_data['user_type']
user_data = {
"first_name": first_name,
"middle_name": middle_name,
"last_name": last_name,
"mobile_no": mobile_no,
"email": email
}
if user_type == 'super-user':
user = User.objects.create_super_user(username, password, **user_data)
else:
user = User.objects.create_staff_user(username, password, **user_data)
form.instance.user = user
form.instance.is_active = True
form.save()
return super().form_valid(form)
Questions are:-
As far as i tried to debug, it could be the reason that create_staff_user and create_super_user functions have already created a row in database and now form.save() is also trying to insert the row again. (not sure)
do i need to do form.save() as i found that super().form_valid(form) also have implemented form saving function within it ?
In your view, you should only call save once, but you are calling it twice
form.save() # this line saves it to the database
super().form_valid(form) # and this line does that too
so after calling form.save() return response.
update, your code to
class UserCreationView(CreateView):
template_name = "form.html"
form_class = UserCreationForm
success_url = "/"
def form_valid(self, form):
username = form.cleaned_data['username']
password = form.cleaned_data['password']
first_name = form.cleaned_data['first_name']
middle_name = form.cleaned_data['middle_name']
last_name = form.cleaned_data['last_name']
mobile_no = form.cleaned_data['mobile_no']
email = form.cleaned_data['email']
user_type = form.cleaned_data['user_type']
user_data = {
"first_name": first_name,
"middle_name": middle_name,
"last_name": last_name,
"mobile_no": mobile_no,
"email": email
}
if user_type == 'super-user':
user = User.objects.create_super_user(username, password, **user_data)
else:
user = User.objects.create_staff_user(username, password, **user_data)
form.instance.user = user
form.instance.is_active = True
form.save()
return HttpResponseRedirect(self.get_success_url())
mobile number should be unique for every user
class UserForm(forms.ModelForm):
class Meta:
model = User
exclude = ('is_staff', 'is_superuser',)
def clean_mobile_no(self):
mobile_number = self.cleaned_data.get('mobile_no')
user = User.objects.filter(mobile_no=mobile_number)
if user:
raise forms.ValidationError(
"mobile no is taken"
)
return mobile_number
Well, as for your first question, it could be the problem. I guess before you created a custom User model, you makes migrations and pushed them to your database. So, that is the part where Django also creates its own User model, with all the available columns and attributes. I would suggest to DELETE your currently Users table, and run the makemigrations and migrate again.
As for your second question, the best practice and advice that I could give you is to first add an if clause, to check if the form data are valid, and then save the form and post the data. Although my advice does not really relate to your question, the point is to always validate the form's data and after you retrieve them, save the form (post the data).
In the documentation it says that the FormView class on success will redirect the user and on error, it will redisplay the form. However, the CreateView will only display the errors and save the object, it will not redirect to anything. Although with the CreateView you can automatically save the form and its data, it will not redirect the user. I suggest you using the FormView class that will show if there any errors and will redirect the user on success, but be careful and save the form data at the end of the POST function.
I hope that helps! Please let me know if there is anything else I can help you with.
I have a model named 'Exhibition', where i enable users to create a new exhibition/event after filling out a form they are asked to pay some amount and are redirected to a payment gateway (paypal as per django-paypal), after successful payment i want to update my 'Exhibition' table only for those who have paid, users are requested to login before creating an event and i am also saving the user with the form in my db.
This is my view for saving the form:
def new_event(request):
form = NewEventForm(request.POST or None)
if form.is_valid():
save_it = form.save(commit=False)
save_it.user = request.user
save_it.save()
return render_to_response('new_event.html',locals(), context_instance=RequestContext(request))
and my model:
class Exhibition(models.Model):
user = models.ForeignKey(User, blank=True, null = True)
Type = models.ForeignKey('home.Type')
Category = models.ForeignKey('home.Category')
Country = models.ForeignKey('home.Country')
City = models.ForeignKey('home.City')
Event_name = models.CharField(max_length = 250)
Video_url = models.URLField(blank = True)
Event_start_date = models.DateField(blank = False)
Event_end_date = models.DateField(blank = False)
Discription = RichTextField(blank = False)
Contact_person = models.CharField(max_length=50,blank = True )
Company = models.CharField(max_length = 100,blank = True)
Company_website = models.URLField(blank = True)
Email = models.EmailField(blank = True)
Event_website = models.URLField(blank = True)
Event_email = models.EmailField(blank = True)
Venue = models.CharField(max_length = 250, blank = False)
Address = models.CharField(max_length = 250, blank = False)
Paid = models.IntegerField(max_length=2, default='0')
def __unicode__(self):
return '%s' % self.Event_name
#permalink
def get_absolute_url(self):
return ('view_category', None, { 'slug': self.Event_name })
If i use django-paypal view, i would be able to process payment but i don't know how to get the correct 'Exhibition - id' to update the db, shall i store it in cookies and how to do it?
I would try adding a post_save signal handler to django-paypal's model PayPalStandardBase and then use the transaction record's fields to identify your exhibition record. You could use the primary key field of your model and tell paypal that it is your item number for the transaction, or just add any other unique field you want to use as common identifier.
Check out the model here:
https://github.com/spookylukey/django-paypal/blob/master/paypal/standard/models.py
The signal handler would look like:
#receiver(post_save, sender=PayPalStandardBase)
def mark_as_paid(sender, instance=None, created=False, **kwargs):
if created:
e = Exhibition.objects.get(pk=instance.item_number)
e.Paid = ...
e.save()
You can add it to a models.py of yours or a signals.py which you then import elsewhere.
I would suggest adding a custom field containing the user id , and recover it when paypal notify you back.
See custom below :
https://developer.paypal.com/webapps/developer/docs/classic/ipn/integration-guide/IPNandPDTVariables/
Someone wants me to make a registration form with no email, and just first name, last name and date of birth. So I decided to do this:
username = request.POST['first_name'] + '__' + request.POST['last_name']
I know we need to have a unique field in Django, so a person with the same name will cause problems. But this is how he wants it done (not many users will be using it).
So how do I populate Django's AbstractBaseUser with a username as above and a default password?
The idea is to get a Doctor who is already logged in to register a new Patient with just first name, last name, and date of birth.
models.py:
class MyUser(AbstractBaseUser):
USER_TYPE = (('doc', 'Doctor'), ('pat', 'Patient'))
id = models.AutoField(primary_key=True)
first_name = models.CharField(max_length=200)
last_name = models.CharField(max_length=200)
username = models.CharField(max_length=200, unique=True, )
joined = models.DateTimeField(auto_now_add=True)
usertype = models.CharField(max_length=254, choices=USER_TYPE)
first_time = models.NullBooleanField()
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
USERNAME_FIELD = 'username'
def __unicode__(self):
return self.username
forms.py:
class patientForm(forms.ModelForm):
class Meta:
model = MyUser
fields = ('username', 'first_name', 'last_name')
class patientProfileForm(forms.ModelForm):
class Meta:
model = Patients
fields = ('dob', 'condition')
views.py:
#login_required
def new_patient(request):
context = RequestContext(request)
registered = False
if request.method == 'POST':
patient_form = patientForm(data=request.POST)
patient_profile = patientProfileForm(data=request.POST)
if patient_form.is_valid() and patient_profile.is_valid():
username = request.POST['first_name'] + '__' + request.POST['last_name']
user = patient_form.save() #Error here: (1062, “Duplicate entry '' for key 'username'”)
user.set_password(user.password)
user.save()
profile = patient_profile.save(commit=False)
profile.user = user
profile.doctor = request.user
profile.save()
registered = True
else:
print patient_form.errors, patient_profile.errors
else:
patient_form = patientForm()
patient_profile = patientProfileForm()
return render_to_response(
'new-patient.html',
{'patient_form': patient_form, 'patient_profile': patient_profile, 'registered': registered},
context)
How do I add the username in the format mentioned earlier when saving the form? I get the error:
(1062, “Duplicate entry '' for key 'username'”) (see above where this happens)
One way to do this would be to override the clean method to set the username field until found. This might not be the exact code you need, but you get the idea.
class patientForm(forms.ModelForm):
class Meta:
model = MyUser
fields = ('username', 'first_name', 'last_name')
def clean(self):
cd = self.cleaned_data
fullname = "%s-%s" % (cd.get('first_name'), cd.get('last_name'))
username = slugify(fullname)
while True:
try:
user = User.objects.get(username=username)
except:
username = username + '-copy' #Or changes this to a counter.
else:
break
cd['username'] = fullname
return cd
Ok so here is the code, I will explain what I try to do at the bottom of the page. I am just starting out with python and django please keep that in mind.
Models.py (there is more in here but its not important):
STAFF_TYPES = (
('OG', _('Organizer')),
('MO', _('Moderator')),
('SC', _('Scanner'))
)
class StaffMember(models.Model):
"""
Staff member class
"""
user = models.ForeignKey(User, verbose_name=_('user'))
staff_type = models.CharField(
max_length=3,
choices=STAFF_TYPES,
verbose_name=_('staff type')
)
forms.py:
class StaffMemberForm(forms.ModelForm):
user = forms.ModelChoiceField(User.objects.all(), label=_('User'), required=False)
username = forms.CharField(label=_('Username'), required=False)
first_name = forms.CharField(label=_('First name'), required=False)
last_name = forms.CharField(label=_('Last name'), required=False)
email = forms.EmailField(label=_('Email'), required=False)
def passwordRandom(string_length=10):
random = str(uuid.uuid4())
random = random.upper()
random = random.replace("-", "")
return random[0:string_length]
class Meta:
model = StaffMember
fields = ('staff_type',)
def clean(self):
if self.errors:
return self.cleaned_data
super(StaffMemberForm, self).clean()
cleaned_data = self.cleaned_data
username = cleaned_data.get("username")
first_name = cleaned_data.get("first_name")
last_name = cleaned_data.get("last_name")
email = cleaned_data.get("email")
user = cleaned_data.get("user")
staff_type = cleaned_data.get("staff_type")
check = [username, user]
if staff_type:
if any(check) and not all(check):
return cleaned_data
raise forms.ValidationError(_('Choose a user from the dropdown OR make a new user'))
def save(self, commit=True):
if self.cleaned_data.get("user") is None:
User.username = self.cleaned_data['username']
User.first_name = self.cleaned_data['first_name']
User.last_name = self.cleaned_data['last_name']
User.email = self.cleaned_data['email']
User.password = self.passwordRandom(8)
StaffMember.user = User
User.save()
else:
StaffMember.user = self.cleaned_data.get("user")
StaffMember.save()
Ok so essentially what I am trying to do here is create the option to either choose an existing user from the dropdown or create one by using the forms, therefore the fields can't be required because they can both be empty (not at the same time though).
So I want the form to save a new user and use that user to connect to the staffmember when they choose to make a new one. How would I do this because so far I'm only getting an error.
Thanks in advance,
Aaron
PS. Sorry for the messy code, just starting out as I stated before
You need to create a new User object in your save() method
def save(self, commit=True):
# Run the default save method, commit=False stops the
# model saving to the db
staff_member = super(StaffMemberForm, self).save(commit=False)
if self.cleaned_data.get("user") is None:
# Create a new User object
user = User()
user.username = self.cleaned_data['username']
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.email = self.cleaned_data['email']
user.password = self.passwordRandom(8)
# Save new user
user.save()
# Apply the new user to the staff_member object
staff_member.user = user
else:
staff_member.user = self.cleaned_data.get("user")
# If the form was expecting to save the StaffMember then save
if commit:
staff_member.save()
return staff_member
UPDATE: Modified code to reflect comments