How to make Django API with registration and login [closed] - django

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I am trying to make an API in Django that returns employee info as JSON only when a user is logged in.
My question is, how can I make a user registration and log in option without the use of html and forms?
Is there a way for me to create a registration option that takes in input sent by a client via JSON. For example, a POST request to localhost:example/register/ which contained :
{"username" : "examle", "email" : "example", "password" : "example", "password2" : "example"}?
How would this be implemented in views.py?
Any help would be appreciated.
Thank you.

Yes, there is a way, and Django Rest Framework includes almost everything you need.
To answer your first question about being logged in, you would add a permissions class to only allow access to someone who is authenticated.
To answer your API question for registration, you'll want to create an API endpoint in whichever URL file is relevant; I put my in my Users app:
urlpatterns = [
path('register/', register_user_view, name="register"),
...
]
You'll want to create a view and serializer to ingest the JSON you're sending in:
#api_view(['POST',])
def register_user_view(request):
if request.method == 'POST':
serializer = RegistrationSerializer(data=request.data)
data = {}
if serializer.is_valid():
account = serializer.save()
data['response'] = "successfully registered a new user."
data['email'] = account.email
data['response'] = account.username
else:
data = serializer.errors
# data = serializer.data
return Response(data)
Serializer:
class RegistrationSerializer(serializers.ModelSerializer):
password2 = serializers.CharField(style={'input_type': 'password'}, write_only=True)
class Meta:
model = CustomUser
fields = ['email', 'username', 'password', 'password2']
extra_kwargs = {
'password': {'write_only': True}
}
def save(self):
account = CustomUser(
email=self.validated_data['email'],
username=self.validated_data['username'],
)
password = self.validated_data['password']
password2 = self.validated_data['password2']
if password != password2:
raise serializers.ValidationError({'password': 'Passwords do not match!'})
account.set_password(password)
account.save()
return account

Related

django rest framework update method

should i use is_valid() function for validating my user data in put() function in drf?
because when i use it ,is_valid.errors says that model with this username and email is already exists!i can't understand the errors meaning,because i think should be something saved before i want to update
serializers.py
class UserCreateSerializers(ModelSerializer):
class Meta:
model = User
fields = ('name','email','family',"username","password","profile_pic")
def update(self, instance, validated_data):
print("from update try")
#more dry
for i in validated_data.keys():
if hasattr(instance,str(i)) and str(i) !="password":
setattr(instance,str(i),validated_data.get(str(i)))
elif hasattr(instance,str(i)) and str(i) =="password":
instance.set_password(validated_data.get('password'))
setattr(instance,"username",validated_data.get('new_username'))
instance.save()
views.py
def put(self,request):
username = request.data['username']
user = User.objects.get(username = username)
serialized = UserCreateSerializers(data = request.data)
if serialized.is_valid():
serialized.update(user,serialized.validated_data)
return Response(data ={"status":"api_user_update_ok"} , status = status.HTTP_201_CREATED)
else:
print(serialized.errors)
return Response(data = {"status":"api_user_update_failed","error":serialized.errors.get('email')[0]},status = status.HTTP_400_BAD_REQUEST)
client data with put method :
name:user
family:useri
username:user2
password:1234
new_username:user22
email:user#gmail.com
error is :
{'email': [ErrorDetail(string='user with this email already exists.',
code='unique')], 'username': [ErrorDetail(string='user with this
username already exists.', code='unique')]} Bad Request:
/api/v0/registration/signup
and the server response is :
{
"status": "api_user_update_failed",
"error": "user with this email already exists."
}
thanks for your help.
Short answer: yes you should use is_valid() before try to save received data into DB. You see this error because DRF add uniqueness validator to unique fields by default. This behavior described in the doc.
To tell django that you are updating object and don't need uniqueness validator you have to provide instance to serializer:
serialized = UserCreateSerializers(user, data=request.data)

Django Custom User Email Account Verification

I am looking to add email account verification in Django. I have attempted using the django-registration app to do so, but it doesn't appear that it has been updated to be fully compatible with custom user models which causes too many problems. Is there another reliable and well-documented app out there which will allow me to send a verification email on user registration in django?
How I handle the email registration personally:
First of all, my Profile extending Django Users (models.py):
class Profile(models.Model):
user = models.OneToOneField(User, related_name='profile') #1 to 1 link with Django User
activation_key = models.CharField(max_length=40)
key_expires = models.DateTimeField()
In forms.py, the Registration class :
class RegistrationForm(forms.Form):
username = forms.CharField(label="",widget=forms.TextInput(attrs={'placeholder': 'Nom d\'utilisateur','class':'form-control input-perso'}),max_length=30,min_length=3,validators=[isValidUsername, validators.validate_slug])
email = forms.EmailField(label="",widget=forms.EmailInput(attrs={'placeholder': 'Email','class':'form-control input-perso'}),max_length=100,error_messages={'invalid': ("Email invalide.")},validators=[isValidEmail])
password1 = forms.CharField(label="",max_length=50,min_length=6,
widget=forms.PasswordInput(attrs={'placeholder': 'Mot de passe','class':'form-control input-perso'}))
password2 = forms.CharField(label="",max_length=50,min_length=6,
widget=forms.PasswordInput(attrs={'placeholder': 'Confirmer mot de passe','class':'form-control input-perso'}))
#recaptcha = ReCaptchaField()
#Override clean method to check password match
def clean(self):
password1 = self.cleaned_data.get('password1')
password2 = self.cleaned_data.get('password2')
if password1 and password1 != password2:
self._errors['password2'] = ErrorList([u"Le mot de passe ne correspond pas."])
return self.cleaned_data
#Override of save method for saving both User and Profile objects
def save(self, datas):
u = User.objects.create_user(datas['username'],
datas['email'],
datas['password1'])
u.is_active = False
u.save()
profile=Profile()
profile.user=u
profile.activation_key=datas['activation_key']
profile.key_expires=datetime.datetime.strftime(datetime.datetime.now() + datetime.timedelta(days=2), "%Y-%m-%d %H:%M:%S")
profile.save()
return u
#Sending activation email ------>>>!! Warning : Domain name is hardcoded below !!<<<------
#The email is written in a text file (it contains templatetags which are populated by the method below)
def sendEmail(self, datas):
link="http://yourdomain.com/activate/"+datas['activation_key']
c=Context({'activation_link':link,'username':datas['username']})
f = open(MEDIA_ROOT+datas['email_path'], 'r')
t = Template(f.read())
f.close()
message=t.render(c)
#print unicode(message).encode('utf8')
send_mail(datas['email_subject'], message, 'yourdomain <no-reply#yourdomain.com>', [datas['email']], fail_silently=False)
Now, in views.py, we need to handle all that, let's go :
The register view:
def register(request):
if request.user.is_authenticated():
return redirect(home)
registration_form = RegistrationForm()
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
datas={}
datas['username']=form.cleaned_data['username']
datas['email']=form.cleaned_data['email']
datas['password1']=form.cleaned_data['password1']
#We generate a random activation key
salt = hashlib.sha1(str(random.random())).hexdigest()[:5]
usernamesalt = datas['username']
if isinstance(usernamesalt, unicode):
usernamesalt = usernamesalt.encode('utf8')
datas['activation_key']= hashlib.sha1(salt+usernamesalt).hexdigest()
datas['email_path']="/ActivationEmail.txt"
datas['email_subject']="Activation de votre compte yourdomain"
form.sendEmail(datas)
form.save(datas) #Save the user and his profile
request.session['registered']=True #For display purposes
return redirect(home)
else:
registration_form = form #Display form with error messages (incorrect fields, etc)
return render(request, 'siteApp/register.html', locals())
The activation views :
#View called from activation email. Activate user if link didn't expire (48h default), or offer to
#send a second link if the first expired.
def activation(request, key):
activation_expired = False
already_active = False
profile = get_object_or_404(Profile, activation_key=key)
if profile.user.is_active == False:
if timezone.now() > profile.key_expires:
activation_expired = True #Display: offer the user to send a new activation link
id_user = profile.user.id
else: #Activation successful
profile.user.is_active = True
profile.user.save()
#If user is already active, simply display error message
else:
already_active = True #Display : error message
return render(request, 'siteApp/activation.html', locals())
def new_activation_link(request, user_id):
form = RegistrationForm()
datas={}
user = User.objects.get(id=user_id)
if user is not None and not user.is_active:
datas['username']=user.username
datas['email']=user.email
datas['email_path']="/ResendEmail.txt"
datas['email_subject']="Nouveau lien d'activation yourdomain"
salt = hashlib.sha1(str(random.random())).hexdigest()[:5]
usernamesalt = datas['username']
if isinstance(usernamesalt, unicode):
usernamesalt = usernamesalt.encode('utf8')
datas['activation_key']= hashlib.sha1(salt+usernamesalt).hexdigest()
profile = Profile.objects.get(user=user)
profile.activation_key = datas['activation_key']
profile.key_expires = datetime.datetime.strftime(datetime.datetime.now() + datetime.timedelta(days=2), "%Y-%m-%d %H:%M:%S")
profile.save()
form.sendEmail(datas)
request.session['new_link']=True #Display: new link sent
return redirect(home)
Finally, in urls.py:
url(r'^register/$', 'register'),
url(r'^activate/(?P<key>.+)$', 'activation'),
url(r'^new-activation-link/(?P<user_id>\d+)/$', 'new_activation_link'),
With all that you should have something to start with, use the appropriate templatetags in the .txt emails and HTML and it should work.
NB: This code isn't perfect, there is duplication (for instance, the generation of the random key could be defined in a function), but it does the job. Also: the activation key is not generated using proper cryptographic functions. An alternative is to use a function like the following to generate the keys:
from django.utils.crypto import get_random_string
def generate_activation_key(username):
chars = 'abcdefghijklmnopqrstuvwxyz0123456789!##$%^&*(-_=+)'
secret_key = get_random_string(20, chars)
return hashlib.sha256((secret_key + username).encode('utf-8')).hexdigest()
NB2: Django send_mail doesn't provide any tools to authenticate your emails. If you want to authenticate your emails (DKIM, SPF), I advise you to look into this: https://djangosnippets.org/snippets/1995/
NB3: There is a security issue with the view new_activation_link: it should check if the user requesting the re-send is the right one and also if he isn't already authenticated. I let you correct that.
You may also be interested in the simple but powerful django-verified-email-field.
Simply use VerifiedEmailField in Your forms:
from django import forms
from verified_email_field.forms import VerifiedEmailField
class RegistrationForm(forms.ModelForm):
email = VerifiedEmailField(label='email', required=True)
Or in Your models:
from django.db import models
from verified_email_field.models import VerifiedEmailField
class User(models.Model):
email = VerifiedEmailField('e-mail')
It renders two input fields: e-mail and verification code. The verification code is sent to the e-mail address using AJAX or during field's clean if there is no valid code for given e-mail, so it works even without javascript.

Username already exists, when want to update userprofile in django

Whenever I try try to update a userprofile on django powered web, I get the error: "username already exists, please provide another one." I am trying to get it to recognize the authenticated user. Although every other thing works, it will not update until I specify a new username.
views.py
#login_required
def editprofile(request):
registeredmember = request.user.get_profile()
if request.method == 'POST':
userprofile_edit = RegistrationForm(request.POST, instance = registeredmember)
if userprofile_edit.is_valid():
userprofile_edit.save()
return HttpResponseRedirect('/profile/')
else:
userprofile_edit = RegistrationForm(instance = registeredmember)
return render_to_response('carloan/editprofile.html', {'userprofile_edit': userprofile_edit}, context_instance=RequestContext(request))
You have to exclude the username field when you are Editing the profile.
Something like this in your RegistrationForm.
class RegistrationForm(forms.form):
#other code
class Meta:
exclude = ['username',]
You can add multiple field names which you don't want to be included in the form

Django 1.4 Modifying Custom Account Model for Uniqueness of E-mail Addresses

I've already defined a custom user account that utilizes several built in's from the auth User model and, using the user link, links these with some additional fields that I needed to register a user on the database.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
relevant from my models.py
# additional model to incorporate our custom fields to the auth user model
class Account(models.Model):
user = models.OneToOneField(User) #link (pointer) to the users other information in User model
birthdate = models.DateField(blank = True, ) # True makes this field optional
gender = models.CharField(max_length = 1, choices = GENDER_CHOICE, null = True, blank = True)
def __unicode__(self): # define a unicode for the user to access
return u'%s %s' % (self.user.first_name, self.user.last_name) # return first and last name in shell
# custom form to gather information from the user for a new account
class UserRegistration(UserCreationForm):
#class RegistrationForm(forms.ModelForm):
class Meta:
model = User
fields = ("first_name", "last_name", "email", "username", "password1", "password2",)
# ensures uniqueness of user email addresses when registering
def clean_email(self):
print "In custom creation"
email = self.cleaned_data.get(email = 'email')
username = self.cleaned_data.get(username = 'username')
# checks if email address already exists
if User.objects.filter(email__iexact = self.cleaned_data['email']):
print "Email exists"
# if email and User.objects.filter(email__iexact = email).exclude(username=username).exists():
raise forms.ValidationError(u'Email Address is currently used by another user.')
return email
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
relevant from views.py
def Main(request):
if request.user.is_authenticated():
latest_events = Event.objects.all().order_by('-created')[:10] # Returns latest 10 events
my_events = Event.objects.filter(creator=request.user)[:10] # Returns up to 10 events created by current User
my_calendars = Calendar.objects.filter(creator=request.user) # Returns all calendars created by the user
authForm = None
loginForm = None
headerType = "header.html"
else:
latest_events = None
my_events = None
my_calendars = None
headerType = "header_main.html"
authForm = UserRegistration(request.POST or None)
print "Creating account UserRegistration" # TESTING PRINT
print "User email = %s " %(User._meta.get_field('email'))
if request.method == 'POST':
if authForm.is_valid():
newUser = authForm.save(commit=False)
newUser.save()
newUser = authenticate(username=request.POST['username'], password=request.POST['password1'])
login(request, newUser)
return HttpResponseRedirect('/signup/')
....
....
more code on success redirection
....
....
~~~~~~~~~~~~~~~~~~~~~~~~~~~
(I hope I didn't post too much code, just wanted to be thorough)
As you can see there are a few commented out attempts I've made recently. I tried using thee built in RegistrationFormUniqueForm() by downloading 'registration' but I don't actually want to make a new registration form since I already have a working one.
I moved on to trying another suggestion, the code under the comment
# custom form to display additional sign up information
When I tried registering a new user with an already registered email it did not throw any error and allowed the registration. I also tried changing the users email and it allowed the change to an already taken email with no objection.
Can anyone suggest a method for making user registration maintain a unique lock on each individual attempting to register with an email address that may already be taken? As well as preventing them from changing their email to one that is taken by a current user?
Thanks in advance.
EDIT: I made changes to the Models registration form def clean_email() and the def in views to reflect what I currently have that still does not work.
The indentation of your clean_email methods is wrong for both forms. At the moment, they are methods of the Meta class, so will never be called. For example, the registration form should be:
class RegistrationForm(UserCreationForm):
#class RegistrationForm(forms.ModelForm):
class Meta:
model = User
fields = ("first_name", "last_name", "email", "username", "password1", "password2",)
def clean_email(self):
"""ensures uniqueness of user email addresses when registering"""
email = self.cleaned_data.get('email')
This might not be the real problem -- it's easy to get the indentation wrong when pasting code into stack overflow. If that's the case, I'll delete the answer.

How to avoid creating 'username' in django-auth

In my django project I need to add registration function. Problem is that in registration process I can't use a 'userprofile' anywhere. My user is defined by 'first name' , 'last name' and some other data. How to achieve this ? Apart of enabling contrib.auth and 'registration' I've created a 'user' application. In user.models I have an extended user model with additional fields. In user.forms I have created extended registration form :
class ExtendedRegistrationForm(RegistrationForm):
first_name = forms.CharField(
label="First name",
error_messages={'required': 'Please fill the first name field'},
)
last_name = forms.CharField(
label="Last name",
error_messages={'required': 'Please fill the last name field'},
)
def save(self, profile_callback=None):
user = super(ExtendedRegistrationForm, self).save()
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.save()
In user.views I have a custom register view :
def custom_register(request, success_url=None,
form_class=ExtendedRegistrationForm, profile_callback=None,
template_name='registration/registration_form.html',
extra_context=None):
def _create_profile(user):
p = UserProfile(user=user)
p.is_active = False
p.first_name = first_name
p.last_name = last_name
p.save()
return register(request,
success_url="/accounts/register/complete",
form_class=ExtendedRegistrationForm,
profile_callback=_create_profile,
template_name='registration/registration_form.html',
extra_context=extra_context,
)
and also I've overridden registration urls for my project :
url(r'^accounts/password/reset/$',
auth_views.password_reset, { 'post_reset_redirect' : '/',
'email_template_name' : 'accounts/password_reset_email.html' },
name='auth_password_reset', ),
url(r'^accounts/password/reset/confirm/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$',
auth_views.password_reset_confirm, { 'post_reset_redirect' : '/accounts/login/'},
name='auth_password_reset_confirm'),
url(r'^accounts/password/reset/complete/$',
auth_views.password_reset_complete,
name='auth_password_reset_complete'),
url(r'^accounts/password/reset/done/$',
auth_views.password_reset_done,
name='auth_password_reset_done'),
url(r'^accounts/register/$',
'user.views.custom_register',
name='registration_register'),
(r'^accounts/', include('registration.urls')),
So I have a good base to start but how to get rid of 'username' ? Can I just treat username as first_name (so many users with the same name) or will django complain ?
When I had to tackle this, the easiest way is to not include the "extended user profile" stuff in the registration process. When they log in for the first time, redirect them or send them a message to fill out forms. This should at least get you going on it. I'm going to be tackling this myself soon enough, so when I find a more concrete solution, I'll post it.
I'm still also not sure what you mean by being unable to access username... that's part of auth.models.User, so it IS available. Are you ignoring the basic fields already available in User?...
Why not generate a username on save, based on first_name and last_name?