Knowledge summary of open source web framework django (12)
User center interface (I)
Define model class base class
In order to supplement the two fields of creation time and update time for the model class data in the project, we need to define the model class base class. New aerf_mall.utils/BaseModel.py file to create a model class base class.
Django model
auto_ now_ When add = true, it is the time when adding, and there will be no change when updating the object.
auto_now=True whether you add or modify an object, the time is the time you add or modify it.
from django.db import models class BaseModel(models.Model): """Supplementary fields for model classes""" create_time = models.DateTimeField(auto_now_add=True, verbose_name="Creation time") update_time = models.DateTimeField(auto_now=True, verbose_name="Update time") class Meta: abstract = True # Description is an abstract model class, which is used for inheritance and use. BaseModel tables will not be created during database migration
1. Judge whether the user logs in
Requirements:
- When a user logs in, he can access the user center.
- If the user is not logged in, it is not allowed to access the user center and guide the user to the login interface.
Implementation scheme:
- You need to determine whether the user logs in.
- Determine whether the user can access the user center according to the result of login.
2. is_authenticated determines whether the user logs in
Introduction:
- Django user authentication system provides the method request user. is_ Authenticated () to determine whether the user logs in.
- Returns True if login authentication is passed. Otherwise, False is returned.
- Disadvantages: login verification logic is required in many places, so the code needs to be repeatedly encoded many times.
3. login_ The required decorator determines whether the user logs in
3.1. Define the View subclass to encapsulate login_required decorator
- Tip: LoginRequired(object) depends on the View class View, and its reusability is very poor.
3.2. Define the extension to verify whether the user logs in
Under the utils package of the project root, create new views py
Notice why it's not in user utils. Py?
from django.http import JsonResponse # Define a decorator to verify whether it has logged in def login_required(func): # func: is a view function def wrapper(request, *args, **kwargs): # Add function code if request.user.is_authenticated: return func(request, *args, **kwargs) else: return JsonResponse({'code': 400, 'errmsg': 'You are not logged in!'}, status=401) return wrapper
4. User center page interface
from aerf_mall.utils.views import login_required # Pay attention to modifying the guide package path from django.utils.decorators import method_decorator #The main function is to solve the problem that the decorator cannot directly use the decorating class view function class UserInfoView(View): @method_decorator(login_required) def get(self, request): # 1. Get user object user = request.user # 2. Construction response data return return JsonResponse({ 'code': 0, 'errmsg': 'ok', 'info_data': { 'username': user.username, 'mobile': user.mobile, # 'email': user.email, # The email model is not written. Please comment first for the time being # 'email_active': user.email_active # The email model is not written. Please comment first for the time being } })
urls.py
# User centric sub routing re_path(r'^info/$', UserInfoView.as_view()),
User center
1. Logical analysis of basic user information
The following is the back-end logic to implement
- User model supplement email_active field
- Query and display basic user information
- Add Email
- Send mailbox verification mail
- Verify mailbox
- Installation package: pip install itsdangerous
2. User model supplement email_active field users models. py
from django.db import models from django.contrib.auth.models import AbstractUser from itsdangerous import TimedJSONWebSignatureSerializer,BadSignature from django.conf import settings # Create your models here. class User(AbstractUser): """Custom user model class""" mobile = models.CharField( unique=True, verbose_name='cell-phone number', null=True, max_length=11 ) # New email_active field # Used to record whether the mailbox is activated. The default is False: not activated email_active = models.BooleanField(default=False,verbose_name='Mailbox verification status') class Meta: db_table = 'tb_users' verbose_name = 'user' verbose_name_plural = verbose_name def __str__(self): return self.username # Encapsulate the method in the user model class def generate_verify_email_url(self): """ Generate the token of the current user; And splicing the connection confirmed by the mailbox; :return: Return to confirm connection """ serializer = TimedJSONWebSignatureSerializer(secret_key=settings.SECRET_KEY) user_info = {'user_id': self.id, 'email': self.email} token = serializer.dumps(user_info) # b'....' verify_url = settings.EMAIL_VERIFY_URL + token.decode() return verify_url # Verify the token value and return the user object @ staticmethod. Do not access instance properties or call instance methods @staticmethod def check_verify_email_token(token): """ check token value :param token: token value :return: User object or None """ serializer = TimedJSONWebSignatureSerializer(secret_key=settings.SECRET_KEY) try: user_info = serializer.loads(token) except BadSignature as e: print(e) return None user_id = user_info.get('user_id') try: user = User.objects.get(pk=user_id) except User.DoesNotExist as e: print(e) return None return user
Usage of itsdangerous:
Sometimes you want to send some data to an untrusted environment, but how to accomplish this task safely? The solution is to sign. Use a key that only you know to encrypt and sign your data, and send the encrypted data to others. When you retrieve the data, you can ensure that no one usurps and modifies the data.
Admittedly, recipients can decipher the content to see what's in your package, but they can't modify your content unless they also have your key. So as long as you keep your key and the key is complex enough, everything is OK.
itsdangerous uses HMAC and SHA1 to sign by default, based on the Django signature module. It also supports JSON Web signature (JWS). This library adopts BSD protocol and is written by Armin Ronacher. Most of the design and implementation copyright belongs to Simon Willison and other Django enthusiasts who turn this library into reality.
>>>from itsdangerous import TimedJSONWebSignatureSerializer as ts >>>serializer = ts('abc',3600) #Specify the key and expiration time in seconds >>>data = serializer.dumps({"openid":"123456"}) >>>data b'eyJhbGciOiJIUzUxMiIsImlhdCI6MTYxNjU2NDY0NCwiZXhwIjoxNjE2NTY4MjQ0fQ.eyJvcGVuaWQiOiIxMjM0NTYifQ.0nc4JtgPmohPz9yWRmslPJrBFbrgv6bC5gMv41QCNnRWIqvEe6RuDfksShPp9xEmUN4i-hhYBMEM0Hwgry0wpQ' >>>data.decode() 'eyJhbGciOiJIUzUxMiIsImlhdCI6MTYxNjU2NDY0NCwiZXhwIjoxNjE2NTY4MjQ0fQ.eyJvcGVuaWQiOiIxMjM0NTYifQ.0nc4JtgPmohPz9yWRmslPJrBFbrgv6bC5gMv41QCNnRWIqvEe6RuDfksShPp9xEmUN4i-hhYBMEM0Hwgry0wpQ' >>>serializer.loads(data) {'openid': '123456'} >>>servializer = ts('abc',1) >>>data = servializer.dumps({"openid":"123456"}) >>>servializer.loads(data) Traceback (most recent call last): File "<input>", line 1, in <module> File "/home/pyvip/.virtualenvs/aerf_test/lib/python3.6/site-packages/itsdangerous/jws.py", line 205, in loads date_signed=self.get_issue_date(header), itsdangerous.exc.SignatureExpired: Signature expired
After the fields are supplemented, they need to be migrated.
python manage.py makemigrations python manage.py migrate
3. In users views. Py to query basic user information, add:
class UserInfoView(View): @method_decorator(login_required) def get(self, request): # 1. Get user object user = request.user # 2. Construction response data return return JsonResponse({ 'code': 0, 'errmsg': 'ok', 'info_data': { 'username': user.username, 'mobile': user.mobile, 'email': user.email, # Write after adding email model 'email_active': user.email_active # Write after adding email model } })
Add and verify mailboxes (note restrictions)
1. Prepare email server https://mail.163.com/
1.1. Click to enter the setting interface
3. Open the authorization code and complete the verification SMS
5. Complete the authorization code setting
6. Configure the mail server and add it in dev.py
# Settings related to sending SMS. These settings are used by default when the user does not send related fields: # Settings for sending SMS: EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' # The smtp server address we use EMAIL_HOST = 'smtp.163.com' # Port number EMAIL_PORT = 25 # Or 465 / 587 is set with SSL encryption. 163 mailbox 465 / 587 is limited. QQ mailbox can be used. # The following contents are variable and change with different background settings: # Mailbox to send mail EMAIL_HOST_USER = 'Your email 163' # Client authorization password set in mailbox EMAIL_HOST_PASSWORD = 'Authorization code' # If you reset the new authorization code, you can directly use the latest authorization code EMAIL_USE_TLS = True # This must be True, otherwise the sending will not succeed # Sender seen by recipient EMAIL_FROM = 'Alpha mall<Your email 163>' # Mailbox validation link EMAIL_VERIFY_URL = 'http://127.0.0.1/success_verify_email.html?token='
# Settings for sending SMS: EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' # The smtp server address we use EMAIL_HOST = 'smtp.163.com' # Port number EMAIL_PORT = 25 # Or 465 / 587 has SSL encryption set # The following contents are variable and change with different background settings: # Mailbox to send mail EMAIL_HOST_USER = 'suifeng_1228@163.com' # Client authorization password set in mailbox EMAIL_HOST_PASSWORD = 'ELFMFARWZRGDIVUQ' # If you reset the new authorization code, you can directly use the latest authorization code EMAIL_USE_TLS = True # This must be True, otherwise the sending will not succeed # Sender seen by recipient EMAIL_FROM = 'Alpha mall<suifeng_1228@163.com>' # Mailbox validation link EMAIL_VERIFY_URL = 'http://127.0.0.1/success_verify_email.html?token='
Send mailbox verification mail
Important:
- Sending mailbox verification mail is a time-consuming operation and cannot block the response of alpha mall, so it is necessary to send mail asynchronously.
- We continue to use Celery to implement asynchronous tasks.
1. Define and call the asynchronous task of sending mail
tasks.py
from django.core.mail import send_mail from django.conf import settings from celery_tasks.main import app @app.task(name='send_verify_email') def send_verify_email(to_email, verify_url): subject = 'Alpha mall email verification' html_message = '<p>Dear user!</p>' \ '<p>Thank you for using alpha mall.</p>' \ '<p>Your email address is:%s . Please click this link to activate your email:</p>' \ '<p><a href="%s">%s<a></p>' % (to_email, verify_url, verify_url) send_mail( subject, '', settings.EMAIL_FROM, [to_email], html_message=html_message )
Supplement:
# bind: ensure that the task object will be automatically passed in as the first parameter. bind=True, the function plus self represents the task object # name: asynchronous task alias # retry_ Backoff: the nth time interval of abnormal automatic retry (retry_backoff*2^(n-1))s # max_retries: the maximum number of abnormal automatic retries @app.task(bind=True, name='send_verify_email', retry_backoff=3) def send_verify_email(self, to_email, verify_url): """ Send verification email :param to_email: Recipient mailbox :param verify_url: Verify link :return: None """ subject = "Alpha mall email verification" html_message = '<p>Dear user!</p>' \ '<p>Thank you for using alpha mall.</p>' \ '<p>Your email address is:%s . Please click this link to activate your email:</p>' \ '<p><a href="%s">%s<a></p>' % (to_email, verify_url, verify_url) try: send_mail(subject, "", settings.EMAIL_FROM, [to_email], html_message=html_message) except Exception as e: # Automatically retry three times if there are exceptions raise self.retry(exc=e, max_retries=3)
2. Register the task of sending e-mail: main py
- In the asynchronous task of sending mail, we use Django's configuration file.
- So we need to modify the startup file main py.
- Indicate the Django configuration file that celery can read.
- Finally, remember the task of registering the newly added email
""" This file is used as a module for asynchronous application initialization """ # Environment for loading django in asynchronous task program import os os.environ.setdefault( 'DJANGO_SETTINGS_MODULE', 'aerf_mall.settings.dev' ) from celery import Celery # Initialize an application object app = Celery("aerf") # Load configuration file - the parameter is the package guide path of the configuration file (module) # We will be in celery in the future_ The directory where the tasks package is located is the working directory to run asynchronous programs; app.config_from_object('celery_tasks.config') # Tell the app what tasks to listen for # The parameter of this function is a list, in which the guided package path of the task package is written app.autodiscover_tasks([ 'celery_tasks.sms', # Send SMS task 'celery_tasks.email', # Send mail task ])
Add mailbox backend logic
1. Add mailbox interface design and definition
1. Request method
option | programme |
---|---|
Request method | PUT |
Request address | /emails/ |
2. Request parameters
Parameter name | type | Is it necessary to pass | explain |
---|---|---|---|
string | yes | mailbox |
3. Response result: JSON
field | explain |
---|---|
code | Status code |
errmsg | error message |
2. Update mailbox
from celery_tasks.email.tasks import send_verify_email # Update mailbox class EmailView(View): @method_decorator(login_required) def put(self, request): # 1. Extract parameters data = json.loads(request.body.decode()) email = data.get('email') # 2. Calibration parameters if not email: return JsonResponse({'code': 400, 'errmsg': 'lack email'}) if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email): return JsonResponse({'code': 400, 'errmsg': 'Incorrect email format!'}) # 3. Data processing (partial update) - update mailbox user = request.user try: user.email = email user.email_active = False user.save() except Exception as e: print(e) # ======Send mailbox verification mail======= verify_url = user.generate_verify_email_url() send_verify_email.delay(email, verify_url) # Asynchronous call! # 4. Build response return JsonResponse({'code': 0, 'errmsg': 'ok'})
3. Verify the mailbox backend logic implementation
The core of email verification is to send the user's email_ The active field is set to True
# Confirm mailbox interface class VerifyEmailView(View): def put(self, request): # 1. Extract token from query string token = request.GET.get('token') # 2. Verification token user = User.check_verify_email_token(token) if not user: return JsonResponse({'code': 400, 'errmsg': 'Invalid verification message!'}) # 3. If the token is valid, set the activation status of the mailbox to True user.email_active = True user.save() return JsonResponse({'code': 0, 'errmsg': 'Mailbox activation succeeded!'})
4. Add url
users.urls.py
# Update mailbox re_path(r'^emails/$', EmailView.as_view()), # Verify and activate the mailbox interface re_path(r'^emails/verification/$', VerifyEmailView.as_view()),
5. Start Celery
celery -A celery_tasks.main worker -l info