Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
8d9297ea
Commit
8d9297ea
authored
Aug 10, 2012
by
David Ormsbee
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Some refactoring of how user info is copied over when enrollments are created.
parent
731e04e0
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
119 additions
and
33 deletions
+119
-33
common/djangoapps/student/models.py
+119
-33
No files found.
common/djangoapps/student/models.py
View file @
8d9297ea
...
@@ -17,6 +17,13 @@ We do a partial replication of:
...
@@ -17,6 +17,13 @@ We do a partial replication of:
* User -- Askbot extends this and uses the extra fields, so we replicate only
* User -- Askbot extends this and uses the extra fields, so we replicate only
the stuff that comes with basic django_auth and ignore the rest.)
the stuff that comes with basic django_auth and ignore the rest.)
There are a couple different scenarios:
1. There's an update of User or UserProfile -- replicate it to all Course DBs
that the user is enrolled in (found via CourseEnrollment).
2. There's a change in CourseEnrollment. We need to push copies of UserProfile,
CourseEnrollment, and the base fields in User
Migration Notes
Migration Notes
If you make changes to this model, be sure to create an appropriate migration
If you make changes to this model, be sure to create an appropriate migration
...
@@ -247,53 +254,132 @@ def add_user_to_default_group(user, group):
...
@@ -247,53 +254,132 @@ def add_user_to_default_group(user, group):
utg
.
users
.
add
(
User
.
objects
.
get
(
username
=
user
))
utg
.
users
.
add
(
User
.
objects
.
get
(
username
=
user
))
utg
.
save
()
utg
.
save
()
################################# SIGNALS ######################################
########################## REPLICATION SIGNALS #################################
@receiver
(
post_save
,
sender
=
CourseEnrollment
)
def
replicate_enrollment_save
(
sender
,
**
kwargs
):
"""This is called when a Student enrolls in a course. It has to do the
following:
1. Make sure the User is copied into the Course DB. It may already exist
(someone deleting and re-adding a course). This has to happen first or
the foreign key constraint breaks.
2. Replicate the CourseEnrollment.
3. Replicate the UserProfile.
"""
enrollment_obj
=
kwargs
[
'instance'
]
replicate_user
(
enrollment_obj
.
user
,
enrollment_obj
.
course_id
)
replicate_model
(
CourseEnrollment
.
save
,
enrollment_obj
.
user_id
,
**
kwargs
)
replicate_model
(
UserProfile
.
save
,
enrollment_obj
.
user_id
,
**
kwargs
)
@receiver
(
post_delete
,
sender
=
CourseEnrollment
)
def
replicate_enrollment_delete
(
sender
,
**
kwargs
):
enrollment_obj
=
kwargs
[
'instance'
]
return
replicate_model
(
CourseEnrollment
.
delete
,
enrollment_obj
.
user_id
,
**
kwargs
)
@receiver
(
post_save
,
sender
=
UserProfile
)
def
replicate_userprofile_save
(
sender
,
**
kwargs
):
"""We just updated the UserProfile (say an update to the name), so push that
change to all Course DBs that we're enrolled in."""
user_profile_obj
=
kwargs
[
'instance'
]
return
replicate_model
(
UserProfile
.
save
,
enrollment_obj
.
user_id
,
**
kwargs
)
######### Replication functions #########
def
replicate_user
(
portal_user
,
course_db_name
):
"""Replicate a User to the correct Course DB. This is more complicated than
it should be because Askbot extends the auth_user table and adds its own
fields. So we need to only push changes to the standard fields and leave
the rest alone so that Askbot can
"""
try
:
# If the user exists in the Course DB, update the appropriate fields and
# save it back out to the Course DB.
course_user
=
User
.
objects
.
using
(
course_db_name
)
.
get
(
portal_user
.
id
)
fields_to_copy
=
[
"username"
,
"first_name"
,
"last_name"
,
"email"
,
"password"
,
"is_staff"
,
"is_active"
,
"is_superuser"
,
"last_login"
,
"date_joined"
]
for
field
in
fields_to_copy
:
setattr
(
course_user
,
field
,
getattr
(
portal_user
,
field
))
mark_handled
(
course_user
)
course_user
.
save
(
using
=
course_db_name
)
# Just being explicit.
except
User
.
DoesNotExist
:
# Otherwise, just make a straight copy to the Course DB.
mark_handled
(
portal_user
)
portal_user
.
save
(
using
=
course_db_name
)
def
replicate_model
(
model_method
,
user_id
,
**
kwargs
):
"""
model_method is the model action that we want replicated. For instance,
UserProfile.save
"""
instance
=
kwargs
[
'instance'
]
if
not
should_replicate
(
instance
):
return
mark_handled
(
instance
)
course_db_names
=
db_names_to_replicate_to
(
user_id
)
log
.
debug
(
"Replicating {0} for user {1} to DBs: {2}"
.
format
(
model_method
,
user_id
,
course_db_names
))
for
db_name
in
course_db_names
:
model_method
(
instance
,
using
=
db_name
)
######### Replication Helpers #########
def
is_valid_course_id
(
course_id
):
def
is_valid_course_id
(
course_id
):
"""We check to both make sure that it's a valid course_id (and not
"""We check to both make sure that it's a valid course_id (and not
'default', or some other non-course DB name) and that we have a mapping
'default', or some other non-course DB name) and that we have a mapping
for what database it belongs to."""
for what database it belongs to."""
course_ids
=
set
(
course
.
id
for
course
in
modulestore
()
.
get_courses
())
course_ids
=
set
(
course
.
id
for
course
in
modulestore
()
.
get_courses
())
return
(
course_id
in
course_ids
)
and
(
course_id
in
settings
.
DATABASES
)
is_valid
=
(
course_id
in
course_ids
)
and
(
course_id
in
settings
.
DATABASES
)
if
not
is_valid
:
log
.
error
(
"{0} is not a valid DB to replicate to."
.
format
(
course_id
))
return
is_valid
def
is_portal
():
def
is_portal
():
"""Are we in the portal pool? (in which case we'll have to replicate user
"""Are we in the portal pool? Only Portal servers are allowed to replicate
updates). Right now, that means we have more than one database defined."""
their changes. For now, only Portal servers see multiple DBs, so we use
that to decide."""
return
len
(
settings
.
DATABASES
)
>
1
return
len
(
settings
.
DATABASES
)
>
1
def
replicate_enrollment
(
instance_method
,
**
kwargs
):
def
db_names_to_replicate_to
(
user_id
):
log
.
debug
(
"########## Enrollment replication called ############"
)
"""Return a list of DB names that this user_id is enrolled in."""
instance
=
kwargs
[
'instance'
]
return
[
c
.
course_id
for
c
in
CourseEnrollment
.
objects
.
filter
(
user_id
=
user_id
)
if
is_valid_course_id
(
c
.
course_id
)]
def
marked_handled
(
instance
):
"""Have we marked this instance as being handled to avoid infinite loops
caused by saving models in post_save hooks for the same models?"""
return
hasattr
(
instance
,
'_do_not_copy_to_course_db'
)
def
mark_handled
(
instance
):
"""You have to mark your instance with this function or else we'll go into
an infinite loop since we're putting listeners on Model saves/deletes and
the act of replication requires us to call the same model method.
We create a _replicated attribute to differentiate the first save of this
model vs. the duplicate save we force on to the course database. Kind of
a hack -- suggestions welcome.
"""
instance
.
_do_not_copy_to_course_db
=
True
def
should_replicate
(
instance
):
"""Should this instance be replicated? We need to be a Portal server and
the instance has to not have been marked_handled."""
if
marked_handled
(
instance
):
# Basically, avoid an infinite loop. You should
log
.
debug
(
"{0} should not be replicated because it's been marked"
)
return
False
if
not
is_portal
():
if
not
is_portal
():
log
.
debug
(
"replicate_enrollment triggered, but we're not a portal so "
+
log
.
debug
(
"{0} should not be replicated because we're not a portal."
"we're not propogating"
)
.
format
(
instance
))
return
return
False
return
True
if
not
is_valid_course_id
(
instance
.
course_id
):
log
.
error
(
"Don't know where to replicate to for course_id: {0}"
.
format
(
instance
.
course_id
))
return
# We create a _replicated attribute to differentiate the first save of this
# model vs. the duplicate save we force on to the course database.
if
hasattr
(
instance
,
'_replicated'
):
log
.
debug
(
"We've already replicated this -- stopping so we don't go "
+
"into an infinite loop."
)
return
instance
.
_replicated
=
True
# instance_method is either CourseEnrollment.save or CourseEnrollment.delete
# using is the entry in DATABASES we push to (we use course_ids for names)
instance_method
(
instance
,
using
=
instance
.
course_id
)
@receiver
(
post_save
,
sender
=
CourseEnrollment
)
def
replicate_enrollment_save
(
sender
,
**
kwargs
):
return
replicate_enrollment
(
CourseEnrollment
.
save
,
**
kwargs
)
@receiver
(
post_delete
,
sender
=
CourseEnrollment
)
def
replicate_enrollment_delete
(
sender
,
**
kwargs
):
return
replicate_enrollment
(
CourseEnrollment
.
delete
,
**
kwargs
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment