چند بار شده که دوستان مختلف برای Authentication با شماره موبایل در Django سوالات و مشکلات مختلفی داشتن. امیدوارم این پست بتونه پوشش خوبی به سوالاتشون بده.
در ابتدا به موارد زیر حتما توجه می کنیم:
مراحلی که قراره طی بشه:
۱- اضافه کردن فیلد mobile_number به مدل user و تعیین فیلد اصلی برای user که الان فیلد username هست و ما میخوایم بگیم بشه فیلد mobile_number
۲- تغییر user manager تا به جای چک کردن فیلد username mobile number چک کنه
۳- پیاده سازی لاگین و رجیستر که در چند مرحله بصورت زیر پیاده سازی میکنیم:
مراحل بالا رو بصورت کد پیاده شده پیش میریم.
مرحله اول: اضافه کردن فیلد mobile_number به مدل user و تعیین USERNAME_FIELD
class User(AbstractUser): mobile_number_validator = UnicodeMobileNumberValidator( username = models.CharField( _('username'), max_length=150, unique=False, null=True, blank=True, help_text=_('Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.') ) mobile_number = models.CharField(max_length=50, unique=True, verbose_name=_('Mobile Number'), validators=[mobile_number_validator], error_messages={ 'unique': _("A user with that mobile number already exists."), }, ) USERNAME_FIELD = 'mobile_number' REQUIRED_FIELDS = [] objects = MyUserManager()
@deconstructible class UnicodeMobileNumberValidator(validators.RegexValidator): regex = r'09(\d{9})$' message = _( 'Enter a valid mobile number. This value may contain only numbers.' ) flags = 0
مرحله دوم: تغییر user manager
class MyUserManager(BaseUserManager): use_in_migrations = True def _create_user(self, mobile_number, password, **extra_fields): if not mobile_number: raise ValueError('The given username must be set') # email = self.normalize_email(email) mobile_number = self.normalize_mobile_number(mobile_number) user = self.model(mobile_number=mobile_number, **extra_fields) user.set_password(password) user.save(using=self._db) return user def create_user(self, mobile_number, password=None, **extra_fields): extra_fields.setdefault('is_staff', False) extra_fields.setdefault('is_superuser', False) return self._create_user(mobile_number, password, **extra_fields) def create_superuser(self, mobile_number, password, **extra_fields): extra_fields.setdefault('is_staff', True) extra_fields.setdefault('is_superuser', True) if extra_fields.get('is_staff') is not True: raise ValueError('Superuser must have is_staff=True.') if extra_fields.get('is_superuser') is not True: raise ValueError('Superuser must have is_superuser=True.') return self._create_user(mobile_number, password, **extra_fields) def normalize_mobile_number(self, mobile_number): return mobile_number
مرحله سوم:
نکته : برای ارسال کد از یک Adapter فرضی استفاده کردم که میشه داخلش با هر نوع sms پنلی پیاده بشه.
نکته: روش متنوعی میشه برای نگه داری کد فعال سازی پیاده سازی کرد که بسته به Domain Logic یا نیاز های پروژه میتونه متفاوت باشه. در این مقاله بنده از Redis استفاده میکنم.
۱- پیاده سازی API گرفتن mobile number و بررسی exist بودن کاربر
۲- پیاده سازی API ارسال کد فعال سازی
def request_send_verify_sms(request): mobile_number = request.data['mobile_number'] if User.objects.filter(mobile_number=mobile_number).exclude(pk=request.user.pk).exists(): raise APIException('mobile number already added', code=HTTP_406_NOT_ACCEPTABLE) verification_code = StringUtils.id_generator(4, string.digits) adapter = SMSAdapter() response = adapter.send_verify_sms( destination_number=mobile_number, verification_code=verification_code ) if response['status'] != 200: # failed send sms raise APIException('failed to send sms', HTTP_400_BAD_REQUEST) redisClient.setex(verification_code, SMS_VERIFICATION_CODE_EXPIRED_TIME, mobile_number) return Response({"message": "ok"})
۳- پیاده سازی API validate کردن کد فعال سازی
def verify_mobile_number(request): verification_code = request.data['verification_code'] if redisClient.exists(verification_code): mobile_number = redisClient.get(verification_code).decode("utf-8") redisClient.setex(mobile_number, CREATE_USER_EXPIRED_TIME, mobile_number) else: raise APIException('wrong verification code', code=HTTP_406_NOT_ACCEPTABLE) return Response({"message": "ok"})
اگر فرصت کنم یک کد سمپل رو github منتشر میکنم.