I have my first project as junior in my work. It is old (Django 1.8) and it is normal django framework... not REST.
It supports web, and mobile.
I have to create endpoint for mobile to create user.
I think it is not a problem (to create) but I want to make sure it will be save.
First of all I thought that I will create normal ModelForm (RegisterAPIForm based on model=User with full validation (I mean init all fields that are in "backend" not visible for user | cleaned_data for all fields | special overwriten method save() that in addition hashes password, and send email) and in Views I'll add something like this:
class RegistrationAPITestView(View):
def post(self, request):
form = RegistrationAPIForm(
request.POST
)
if form.is_valid():
form.save()
return JsonResponse({})
else:
#check errors and send error code back
Or I should do it other way, by using User object?
class RegistrationAPITestView(View):
def post(self, request):
#check if user does not exist
#password1 and password2 validation
user = User.objects.create()
user.name = request.POST['username']
user.set_password(request.POST['password'])
#init all fields that user can't choose like groups etc
user.save()
What do you think? Do I need ModelForm that I won't even render? It seems to be safer, but maybe I should check it other way? with User object?
Btw. Registration form already exists for web but there is a lot of "web" stuff that I don't need and I don't have to check and there is another method of saving password, so I believe I should create new one.
My code will be revieved in 2 weeks (senior vacations) but now I'm alone and want to do my best.
There is nothing wrong with the second option, but here is the problem that you as a junior should avoid. This line will make the server return a 500 error request.POST['username'] because python will throw a key error if the user doesn't provide the username, to fix just change to request.POST.get('username', 'value if doesn\'t exit') also make sure that everything is ready before create the user or you will have records in the database that wont be useful. Call validators to the password too and try to cover all possible scenario. Remember never trust the user
Related
I'm building a Django server for my company and I'm still unfamiliar with some processes. I'm sure this is super simple, I'm just completely unaware of how this works.
How do I differentiate between user's data so it doesn't get mixed up?
If Jill is a user and she requests a page of her profile data, how do I not send her Jack's profile data, especially if there are multiple models invovled?
For example, the code in the view would look like this:
def display_profile(request)
profile = Profile.objects.get(???) # What do I put in here?
I understand that I can do:
def display_profile(request, user)
profile = Profile.objects.get(user_id=user)
But that's not my design intention.
Thank you in advance.
As documented
Django uses sessions and middleware to hook the authentication system into request objects.
These provide a request.user attribute on every request which
represents the current user. If the current user has not logged in,
this attribute will be set to an instance of AnonymousUser, otherwise
it will be an instance of User.
So in your case (notice field not being called user_id )
profile = Profile.objects.get(user=user)
In your Django view, you can access the current user with request.user.
So if you want to get a Profile instance matching your current logged in user, just do a query as follow:
profile = Profile.objects.get(user=request.user)
This assumes you have a user foreign key field (or OneToOne) in your Profile model.
I have made a custom user model using the AbstractUser , removed the username and replaced it with email and extended the model, tried creating superuser and it worked and also created some users by a signup form , logged in the admin interface and it worked however when tried to create a login form for the users it fails
I tried this but it didn't work
def LoginView(request):
if request.method == 'POST':
form = AuthenticationForm(data=request.POST)
if form.is_valid():
user = form.get_user()
login(request,user)
return redirect('accounts:login')
else:
form = AuthenticationForm()
return render(request,'accounts/login.html', {'form':form})
then i tried this
class LoginView(FormView):
form_class = AuthenticationForm
template_name = 'login.html'
def form_valid(self, form):
email = form.cleaned_data['email']
password = form.cleaned_data['password']
user = authenticate(email=email, password=password)
# Check here if the user is an admin
if user is not None and user.is_active:
login(self.request, user)
return HttpResponseRedirect(self.success_url)
else:
return self.form_invalid(form)
Obviously i expect the user to be logged in
i think the code in this post is badly formatted. mainly it's my fault as i'm new to this platform
I've developed almost the same setup that you're describing (I didn't remove the username field, just stopped using it). If you haven't already seen it, Django's documentation at https://docs.djangoproject.com/en/2.2/topics/auth/customizing/#substituting-a-custom-user-model is quite helpful.
There are a couple of important things that need to be set up correctly for this to work.
The USERNAME_FIELD on your model should be set to the name of your email field.
The AUTH_USER_MODEL needs to point to your custom user model.
class MyUser(AbstractUser):
USERNAME_FIELD = 'email'
AUTH_USER_MODEL = 'customauth.MyUser'
Since you've removed the username field altogether you might need to subclass django.contrib.auth.forms.AuthenticationForm and django.contrib.auth.views.LoginView to avoid breaking things, but Django should handle a different authentication field quite well.
If you do wind up needing to subclass the view, https://ccbv.co.uk/projects/Django/2.2/django.contrib.auth.views/LoginView/ is a great place to look over all the methods to see what's going on.
Edit - On Subclassing and it's necessity
What I was saying about possibly needing to subclass certain things was influenced by https://docs.djangoproject.com/en/2.2/topics/auth/customizing/#writing-a-manager-for-a-custom-user-model. I wasn't sure if there were other parts of the authentication system that would need you to customize them because you removed the username field.
I've read through some of the source code for Django's authentication system. Here's the path that's being followed.
When the POST request is made to Django's authentication view the authentication form is validated. https://github.com/django/django/blob/2.2.2/django/contrib/auth/forms.py#L191
The authenticate function is called. This iterates through the backends set up and tries to authenticate on each of them. https://github.com/django/django/blob/2.2.2/django/contrib/auth/__init__.py#L62
Django's built-in authentication backend gets the user if it exists using the natural key. https://github.com/django/django/blob/2.2.2/django/contrib/auth/backends.py#L16
We can see in the base manager that the natural key used is the field named by USERNAME_FIELD. https://github.com/django/django/blob/2.2.2/django/contrib/auth/base_user.py#L43
If the form is valid, meaning that the user is authenticated properly, the user is then logged in. https://github.com/django/django/blob/2.2.2/django/contrib/auth/views.py#L88
My reaction is that it looks like Django should work out of the box for your use case. You shouldn't need to write a backend. Here's the extent of the code my gut says you should have to write.
from django.contrib.auth import views as auth_views
from django.shortcuts import resolve_url
class LoginView(auth_views.LoginView):
template_name = 'accounts/login.html'
def get_success_url(self):
return resolve_url('accounts:login')
I don't know if this could be of any use to somebody but I can confirm that Django can authenticate you well with its own Login view if you just replace the username with an email field on your custom user model (as long as you specify the USERNAME_FIELD on the custom user model and are indeed using it by declaring it in the settings).
As I was expecting this behavior I designed a custom HTML form with email/password inputs and used the same principles I would use with the original user model authentication. It was failing though and I understood it was because I wasn't adapting my form to the original Login view expectations or would have worked from the start.
Just make sure to remember that the form HTML input tag for the email address needs to have "type" set to "email" but "id" set to "id_username" and "name" to "username".
This means you can just replace username with an email field and authenticate normally. I did not even declare a Login view for my login form to work, the Django view automacally used from the core is just being called at /accounts/login and working on its own. I'm working on Django 3.2
My scenario:
My app, handles signup at /profile/signup/ using SingupView(FormView).
class SignupView(FormView):
template_name = "profile/signup.html"
template_name_signup_confirmed = "profile/created.html"
template_name_inactive = "profile/inactive.html"
def form_valid(self, form):
# Create and save inactive user
self.created_user = self.create_user(form, commit=False)
self.created_user.is_active = False
self.created_user.save()
# Create and save user's profile
profile = self.create_profile(form)
# Send registration email
self.send_registration_email([self.created_user.email], registration_code=profile.registration_code)
# Response
return render_to_response(self.template_name_signup_confirmed, {'email': self.created_user.email})
def form_invalid(self, form):
if 'email' in form.errors and form.errors['email'].as_text() == \
'* An inactive user is registered with this email address.':
return render_to_response(self.template_name_inactive, {'email': form.data["email"]})
return render_to_response(self.template_name, self.get_context_data(form=form))
In SingupView().form_valid(form) the User and his Profile are created, user is signed up, but inactive.
After that, in my case there is not success url to redirect, but render a new page, at the same address (/profile/signup/) with a new html saying "An email was sent at youremail#email.com, check and activate".
The same case when an inactive, registered user, tries to signup again, he will get a new page, rendered at the same adress /profile/signup/ saying 'Your email youremail#email.com is already in our db but is not active.. .'
My questions:
Can anyone explains if this is a good way to go, or I need to redirect to new urls, controlled by a new views?
Is there any security risk by using render instead of redirect? Especially on user sign in/sign up?
What is the difference when using redirect or render a new template at the same address? What is the best practice in Django?
Is it ok, to display user's email, in the signup confirmation page and also in the alert page that says that user is registered but inactive in db?
The common pattern for this sort of behaviour is to use a redirect.
I would personally prefer that as it avoid the confuson of one view doing two things - one view displays and processes the form, the other displays a success message.
If the user manages to POST the form data a second time, what do you do?
https://en.wikipedia.org/wiki/Post/Redirect/Get
I am not aware of any greater security risk from using render rather than redirect (though someone with more expertise may know more than me about that).
Bit of a strange one and I have my reasons for doing this, I know Django does this out-of-the-box so put that to the side when I ask..... is it possible to create a authenticated session in Django for a user that does not exist in the standard user model. i.e I want a one off login (session) created for access that allows me to use request.tempUser.is_authenticated() Almost like anonymous access, but authenticated! I'm not talking about custom user models here, but I want do want use the standard auth stuff in Django, if thats possible?
This is what I have so far where I have tried request.session.save() but that won't ... log-in the user.
if member.check_password(password):
# Start new session for member???????
request.session.save()
return self.create_response(request, {
'success': True
})
I've done this before, we have a session middleware (we wrote) that looks to see if the current user logged in is a valid user or not.
On the login we do the following
def login(request, username, password):
# not the actual code, but you get the gist
logged_in_user = authenticate(username, password)
request['cur_user'] = logged_in_user.username
If that variable is not set or is not set to a proper username we bounce the user and clear out the session.
This will log in the user, essentially you just have to track that variable in your code to ensure that the session has a valid user attached to it.
I want to add user in a Django app. here's my code, it fires an exception in this line
user = User.objects.create(userName, userMail, userPass)
Here's the whole code:
def createUser(request):
userName = request.REQUEST.get('username', None)
userPass = request.REQUEST.get('password', None)
userMail = request.REQUEST.get('email', None)
# TODO: check if already existed
user = User.objects.create(userName, userMail, userPass)
user.save()
return render_to_response('home.html', context_instance=RequestContext(request))
Any help?
Use the create_user helper function instead of the create method. It takes care of hashing the password for you, amongst other things.
user = User.objects.create_user(userName, userMail, userPass)
As an aside, fetching the values out of the request data dictionary isn't best practice. It's a good idea to learn about django forms and model forms, which will validate input data for you. Once you understand that, there's a UserCreationForm included with the django auth app which you could use.