[PYTHON] The story of seeing painful eyes when looking sweet around authentication (try introducing authentication using django-allauth)

Introduction

This time, in 6 weeks, I participated in a project from team building to product release. Please see the link below for the entire project. I would like you to read the articles and past articles about me, but in short, I am inexperienced and looking for a job as an engineer.

Click here for the project

Quiet talk, this article talks about the authentication part of the features I was in charge of for the project. There are many opportunities to implement functions related to so-called authentication such as sign-up, login, logout, password reset, email address reset, etc. For beginners, it feels like a must-have skill and makes you want to take the initiative. This time, it's a package that is an option when introducing social login with Django, along with a story that I had a bitter feeling when I tried it while developing as a team. I'd like to dig a little deeper into the introduction of django-allauth.

What you can do with django-allauth

--Sign up for both local and social accounts --Connect multiple social accounts to local accounts --Social account delinking (password must be set if you want to keep the linked local account) --You can optionally set instant sign-up for your social account --Email address management (multiple email addresses, primary settings)

As you can see, if you introduce it, you will be able to do all the necessary things around authentication. However, like me, these are used in a way that replaces the Django standard authentication feature.

** Maybe it will be possible if you do it while looking at the commentary site **

If you have a sweet idea like It doesn't work as expected and you will have a hard time.

Things to keep in mind when implementing authentication in the first place

This time, I participated in a project to create one Web application as a team (hereinafter referred to as a project), and I was in charge of about two functions of the application there, and I was also in charge of authentication separately. This is the premise of this story.

So, here are some things to keep in mind when implementing the authentication that you felt when you participated in the project and actually developed it as a team. (I'm ashamed to say that I'm still looking for a job and I don't have any actual work experience, so I hope you'll take it as a feeling.)

――At the first MTG, the PL and the person in charge of design, the user model and the authentication method are thoroughly rubbed and the specifications are decided

→ The user model is inseparable from authentication, and at the same time, for Web applications, the user model tends to be a reference relationship with the model used for each function and the first foreign key. In other words, once the user model is migrated, it cannot be changed, and I think it is better not to do that in the first place. Therefore, I thought that it would be easier to avoid troubles if the design and certification specifications were firmly shared at the first MTG, the package to be adopted was selected, and then the design was done by the designer. It was. As a result of neglecting this when I was in charge of this time, I ended up rewriting the model arbitrarily and deciding the specifications arbitrarily, resulting in a non-better implementation. This was a very bad thing for team development ...

--It is better to implement the authentication function first

As mentioned earlier, authentication is inseparable from the user model. For example, if you replace the Django standard authentication with authentication using a package like this time, you will not understand the package and get used to it. There are many places where there are inconveniences or things that go wrong due to factors such as lack. In that case, it will be necessary to redefine the user model and redo the migration, but if you do that, a dependency error will appear in the DB, and in this case, initially django-allauth I hadn't decided to introduce , so I had to modify the directory and the number of unexpected tasks increased. In addition, there are some functions that cannot be developed unless authentication is implemented, and this function was also available in this project, but as a result of postponing authentication this time, the development of that function will be delayed. Trouble has occurred. (This was also mentioned as a point that PL should reflect on)

About django-allauth

What you can do when you introduce it is as I wrote at the beginning. To install it, hit the install command with pip etc.

#Example
pip install django-allauth

Once installed, add the following items to your Django settigs.py file:


AUTHENTICATION_BACKENDS = [
    ...
    # Needed to login by username in Django admin, regardless of `allauth`
    'django.contrib.auth.backends.ModelBackend',

    # `allauth` specific authentication methods, such as login by e-mail
    'allauth.account.auth_backends.AuthenticationBackend',
    ...
]

INSTALLED_APPS = [
    ...
    'django.contrib.sites',
    'allauth',
    'allauth.account',
    'allauth.socialaccount',

    #Specify the social provider you want to use for social login. You can specify more than one
    'allauth.socialaccount.providers.google',
]

SITE_ID = 1


That is all for the minimum items to be specified. If you're dealing with Django, you probably know about the TEMPLATE part and the static folder settings, so I'll omit the details. Most of the templates are specified by templates, but if you want to create a separate folder and manage all the templates used by allauth there, please set them individually.

After that, add the following items to urls.py (the file in the same directory as settings.py, not prepared for each application).


urlpatterns = [
    ...
    path('accounts/', include('allauth.urls')),
    ...
]

After that, it is OK if you route as follows in urls.py (application with user model) in the application of the authentication function.

from django.contrib import admin
from django.urls import include, path
from django.views.generic import TemplateView

app_name = 'accounts'

urlpatterns = [
    path('accounts/login/',
         TemplateView.as_view(template_name='login.html'), name='login'),
    path('accounts/logout/',
         TemplateView.as_view(template_name='logout.html'), name='logout'),
    path('accounts/signup/',
         TemplateView.as_view(template_name='signup.html'), name='signup'),
]


After that, please migrate. See also the troubleshooting section below for migration. After migrating, use the createsuperuser command to create an admin user and access the admin site. Please refer to the document of the reference article such as the setting related to authentication with django-allauth.

By the way, if you install django-allauth and migrate, in addition to the default and each application model

--Site --Account --Social account

 allauthモデル.png

Three models are automatically added. Among them, it is necessary to register the key etc. of each social provider as shown in the image below in the social account model. You can also specify it with settings.py, but this time I took the form of entering the management site and registering.

socialモデル.png

All you have to do is create a template and you're done. Of course, you can also apply CSS. However, it is difficult for people who are not familiar with the mechanism of templates, and customizing it of django-allauth is a little complicated reference, so the author's I think it's a good idea to borrow the template part from one of the repositories, apply it, and edit it while checking how it is actually displayed on the browser using the Developer Tool. Especially if you want to apply CSS individually, it is honestly troublesome to just use the template, so we recommend that you do it in this process.

django-allauth repository

As a procedure

  1. Create an account folder under templates. If you want to introduce social login, also create a social account folder
  2. Manage authentication screen templates other than social login in account, and manage templates related to social login in socialaccount folder.

It's just that. Regarding the application of CSS, it is the same as that of ordinary Django, so I will omit it.

Items adopted in this project

The purpose of this project is the following three points.

—— Creating a place for participants to engineer

—— Experience team development procedures and methods

――After giving priority to the above two points, we will improve the degree of perfection as a web application to the extent that participants can.

This time, as mentioned above, what I implemented when using django-allauth

--Normal authentication function (login, logout, membership registration) --Social login

There were only two points. This is a result of my unfamiliarity with django-allauth and the fact that the progress of other participants was slowing down as a result of not prioritizing the authentication process. Originally at least in addition to the above

I think it's a big reflection that I couldn't do that because I should have included the 5 points.

Processing related to session

In additional tasks

** Link the survey results to registered users from the session when the account creation button is pressed **

Since I was given the addition of processing, it corresponds.


#Specific processing code
def update_question_result(user_info, question_result_info):
    question_result_obj = QuestionResult.objects.get(
        id=question_result_info)
    user_obj = CustomUser.objects.get(id=user_info)
    question_result_obj.customuser_obj = user_obj
    data_to_update = question_result_obj.save()

    return data_to_update


#Code to call and use the above process(views.The part that hits py)
class Top(LoginRequiredMixin, View):
    def get(self, request, *args, **kwargs):
        if 'unsaved_answer' in self.request.session and self.request.session['unsaved_answer'] == True:
            user_info = self.request.user.id
            question_result_info = self.request.session["primary_key"]
            update_question_result(user_info, question_result_info)
            self.request.session['unsaved_answer'] = False
        return render(request, 'top/top_page.html', context=context)


The code of the processing part is extracted from the actual code. Actually, the first code is defined in another .py file, and it is called and used by the second code defined in the file corresponding to views.py.

As a process

  1. The contents of the questionnaire are saved in the DB when answering the questionnaire.
  2. At that time, if the user is not logged in, the session will be flagged as ['unsaved_answer'] == True.
  3. After that, create an account and login will be completed at the same time when you redirect to the top page, so if you check the session at that time and check the ['unsaved_answer'] == True flag, from the user information It is a process to acquire the user id, acquire the user information in the form of an object from the CustomUser table using it as a key, and update the question_result_obj.customuser_obj of the record registered in 1 with the contents. .. Since question_result_obj.customuser_obj is registered as an object because of the primary key and relation, an error will occur if the object is not updated.

trouble shooting

--Migration order

→ This is not limited to django-allauth, but when migrating a user model, there is a high possibility that an error will occur in the dependency if the migration is done for the convenience of making it a reference relationship with a foreign key or the like. So before you get used to it

  1. Perform make migraton and migrate individually for each model used in each application (specify the application name after each command)
  2. Do make migraton and migrate for django.contrib.admin and django.contrib.auth
  3. After setting the installation of django-allauth, perform make migraton and migrate again.

I think you should take steps such as.

--I want to migrate again

→ Delete the database files and the Migrations folder of each app, comment out or delete all the parts related to django-allauth from settings.py and ʻurls.py, and then make migrationsin each app. And then execute themigratecommand. By the way, if an error occurs arounddjango.contrib.admin or django.contrib.authand you can't migrate properly, you can comment out each item fromsettings.py` in the same way. May be possible.

I can't help but feel that most of the troubles around authentication are this migration part, so I'm also troubled this time, so I hope it helps if there is a similar person.

At the end

Authentication is provided as standard by using a framework, so if you want to implement it somehow, you can do it, but after all it is difficult if you try to do it properly or do something a little different. After all, I was reminded that I should not lick around the authentication.

Another thing that I noticed in this project was that the authentication method must be decided appropriately according to the purpose depending on the deliverables. Of course, it's not about security, but at first I thought I would log in with my email address and password to do this, I actually wrote the code for the process of changing from temporary registration to full registration by email authentication. But for PL

This time, it is a product (portfolio) rather than a service, and considering that, isn't it better to make it easier for users to try it out?

It's a hassle to log in with an email address, and some people may find it bothersome and unpleasant during email authentication. From the perspective of There is a history of changing to the login method with a user name and password. Certainly, there is a reason for that. If you think of it as a deliverable, it is important to insist that you have this much skill or that you have created it, but I realized that you have to think about the people who actually use it. I did.

Postscript

Regarding the social login part, it seems that an error has occurred in the production environment, so it has been deleted crying. It would have been nice if I could handle it, but it seems that the measures were taken because it was a function that was not originally expected and that it would not cause any problems in the near future. After all, it seems that the certification relationship is not straightforward ... I will do my best next time.

reference

django-allauth documentation django-allauth settings [Django] Procedure for implementing social authentication using django-allauth Django-Django django-allauth implements social login function quickly Procedures for customizing django-allauth template files Creating a login screen in Django allauth

Recommended Posts

The story of seeing painful eyes when looking sweet around authentication (try introducing authentication using django-allauth)
The story of introducing a multi-factor authentication function using a one-time password into a Java application
Try using the collections module (ChainMap) of python3