我加入了一个用django 2+编写代码的项目。这是一个病人和医生的系统。当一个医生注册了一个病人,这个会收到一封带有链接的电子邮件确认。一旦他点击它,她就可以使用这个平台。
在她的用户界面设置,病人可以更新她的电子邮件,她会得到一个新的链接到她的新电子邮件,以确认这是她的电子邮件。在她单击确认链接之前,没有更改email属性。只有电子邮件候选人被更新,电子邮件被发送给这个人。(如果我将email属性改为email_candidate,那么如果她在她的email_candidate上犯了错误,她将无法再登录)
点击后,患者邮箱将成为邮件候选邮箱。
这些都可以
在Patient Support Admin界面上,代理也可以帮助患者更新他们的电子邮件。但是,当要求发送电子邮件确认的操作时,电子邮件候选人没有被选中。只选择用户的电子邮件,并向其发送电子邮件确认。
我真的不明白如何调用更新电子邮件的相同函数。
models.py 用户
class User(AbstractUser):
objects = UserManager()
is_physician = models.BooleanField(default=False)
is_patient = models.BooleanField(default=False)
title = models.CharField(choices=TITLE_CHOICES,
max_length=10,
blank=True,
null=True)
first_name = models.CharField(max_length=254, blank=True, null=True, )
last_name = models.CharField(max_length=254, blank=True, null=True, )
birthdate = models.DateField(null=True, blank=True)
home_phone = PhoneNumberField(blank=True, null=True)
mobile_phone = PhoneNumberField(blank=True, null=True)
gender = models.CharField(max_length=1,
choices=GENDER_CHOICES,
blank=True,
null=True)
language = models.CharField(max_length=4,
choices=settings.LANGUAGES,
default='en',
blank=True,
null=True)
email = models.EmailField(unique=True, db_index=True,
error_messages={
'unique': "Email already exists."
}
)
email_candidate = models.EmailField(
max_length=254,
blank=True,
null=True,
default=None)
username = models.CharField(
max_length=30,
unique=True,
blank=True,
null=True,
validators=[
validators.RegexValidator(
r'^w+$',
'Enter a valid username. This value may contain only '
'letters, numbers and _ character.',
'invalid'
),
],
error_messages={
'unique': "Username already exists."
}
)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
ordering = ('created',)
class Meta(object):
verbose_name = 'User'
verbose_name_plural = 'Users'
# abstract = False
permissions = u_perm.user_permissions
@property
def name(self):
return '{} {}'.format(self.last_name, self.first_name, )
models.pyPatient有一个OneToOne字段用户
class Patient(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
physician = models.ForeignKey(Physician,
related_name='patients',
null=True,
blank=True,
on_delete=models.SET_NULL)
address = models.TextField(blank=True, null=True)
city = models.CharField(max_length=85, null=True, blank=True)
country = CountryField(null=True, blank=True)
postal_code = models.CharField(max_length=32, null=True, blank=True)
reminder_hour_default = models.TimeField(
'Send Reminder Time',
default=settings.REMINDER_HOUR_DEFAULT)
is_eligible = models.BooleanField(default=True)
def __str__(self):
return self.user.email
@receiver(post_save, sender=Patient)
@receiver(post_save, sender=User)
def update_status(sender, **kwargs):
def update_patient(instance):
# from pending/disabled to active
current_status = instance.user.status
instance.save()
instance.user.save()
return
def update_user(instance):
pass
instance = kwargs.get('instance')
method_name = get_inner_method(sender)
if instance and method_name:
method_name(instance)
models.py病人支持
class PatientSupport(Patient):
class Meta:
proxy = True
# Add verbose name
verbose_name = 'Patient Support'
verbose_name_plural = 'Patients Support'
serializers.pyUserSerializer,调用来自患者UI的工作端点update_candidate_email
class UserSerializer(serializers.ModelSerializer):
permission_list = serializers.SerializerMethodField()
permissions = serializers.SerializerMethodField()
user_messages = serializers.SerializerMethodField()
class Meta:
model = User
fields = (
'pk',
'title',
'first_name',
'last_name',
'birthdate',
'email',
'email_candidate',
'home_phone',
'mobile_phone',
'gender',
'language',
'username',
'password',
'is_patient',
)
extra_kwargs = {
'email': {'required': False,
'validators': [],
#'read_only': True,
},
'email_candidate': {'required': False,
'validators': [],
#'read_only': True,
},
'home_phone': {'required': False,
'validators': [], },
'mobile_phone': {'required': False,
'validators': [], },
'is_patient': {'required': False},
'username': {'read_only': True, },
}
def update_candidate_email(self, instance, validated_data, email_candidate):
# if email candidate is None (and changed) -> search for tokens and delete token sent
if instance.email_candidate and email_candidate == None:
instance.email_candidate = email_candidate
instance.save()
ResetPasswordToken.objects.filter(user=instance).delete()
# if email candidate is same to current email -> error - email in use !
if instance.email_candidate == instance.email:
raise serializers.ValidationError(
{'email':
"email candidate cannot be same to current mail"}
)
# if email candidate is not None and already not in use-> save it (email candidate) and send new email replace_email_mail
if email_candidate and email_candidate != instance.email_candidate:
if User.objects.filter(email=email_candidate):
raise serializers.ValidationError(
{'email': "email in use"}
)
ResetPasswordToken.objects.filter(user=instance).delete()
instance.email_candidate = email_candidate
instance.save()
site_domain = Site.objects.filter(name__icontains='admin').first()
# create pass token send mail
token = ResetPasswordToken.objects.create(
user=instance,)
tiny_link = shortener.create(instance,
'https://{}/api/users/verifyMail/?lang={}&token={}®ion={}&patient={}'.format(
site_domain.domain,
instance.language if instance.language else "en",
token.key,
settings.REGION_PREFIX,
instance.is_patient
)
)
from messaging.notify import Notify
activate(instance.language)
msg_name = 'EMAIL_VERIFICATION'
if not instance.is_patient:
msg_name = 'EMAIL_VERIFICATION_STAFF'
notify = Notify()
notify.send_notification(
msg_name=msg_name,
users_pk=[instance.pk, ],
context={
'tiny_link': 'https://{}/s/{}/?lang={}®ion={}'.format(
site_domain.domain,
tiny_link,
instance.language if instance.language else "en",
settings.REGION_PREFIX),
'email_candidate': email_candidate
})
return
admin.pyPatientSupportAdmin从那里我需要通过发送一个电子邮件确认到email_candidate
class PatientSupportAdmin(admin.ModelAdmin):
list_display = ['pk', 'patient', 'patient_email', 'email_candidate' ]
list_display_links = ('pk', 'patient')
def get_queryset(self, request):
qs = super(PatientSupportAdmin, self).get_queryset(request)
return qs.filter(support_consent=True)
fieldsets = (
('User', {
'fields': (
'patient',
'get_gender',
'birthdate',
'home_phone', 'mobile_phone', 'language',
'email', 'username',
'date_modified',
'date_joined',
'status',
'user',
)}),
('Patient', {'fields': ('physician',
'country',
'id_number',
)}),
def patient(self, obj):
return obj.user.get_full_name()
def patient_email(self, obj):
return obj.user.email
def email_candidate(self, obj):
return obj.user.email_candidate
def birthdate(self, obj):
return obj.user.birthdate or ''
def home_phone(self, obj):
return obj.user.home_phone
def mobile_phone(self, obj):
return obj.user.mobile_phone
def email(self, obj):
return obj.user.email
def username(self, obj):
return obj.user.username or ''
actions = [resend_email_verification,
]
admin.pyresend_email_verification(),我可以使用的,但它只需要邮箱仍然注册为用户邮箱,而不是候选人邮箱如果电子邮件确认
def resend_email_verifcation(modeladmin, request, queryset):
for user in queryset:
if 'users/patient' in request.path:
user = user.user
if user.is_patient and not user.patient.is_eligible:
continue
site_domain = Site.objects.filter(name__icontains='admin').first()
token = ResetPasswordToken.objects.create(
user=user, )
reset_password_token_created.send(
sender=modeladmin.__class__, reset_password_token=token)
所以在UserSerializer中发送邮件的重要部分是
if email_candidate and email_candidate != instance.email_candidate:
你可以在admin
中用一个新动作来重复使用这个方法def send_verification_email_for_email_candidate(modeladmin, request, queryset):
for user in queryset:
email_candidate = user.email_candidate
if not email_candidate:
continue # defensive a little bit to ignore other use cases in UserSerializer
user.email_candidate = f'{email_candidate}random' # to trigger the condition above
UserSerializer().update_candidate_email(user, {}, email_candidate)
但是我建议重写函数或者复制这里的逻辑部分