Remove SubfieldBase metaclass
This commit is contained in:
@@ -13,6 +13,7 @@ file and check it in at the same time as your model changes. To do that,
|
||||
import hashlib
|
||||
import json
|
||||
import logging
|
||||
import six
|
||||
import uuid
|
||||
from collections import OrderedDict, defaultdict, namedtuple
|
||||
from datetime import datetime, timedelta
|
||||
@@ -1529,7 +1530,7 @@ class CourseEnrollment(models.Model):
|
||||
|
||||
if not status_hash:
|
||||
enrollments = cls.enrollments_for_user(user).values_list('course_id', 'mode')
|
||||
enrollments = [(e[0].lower(), e[1].lower()) for e in enrollments]
|
||||
enrollments = [(six.text_type(e[0]).lower(), e[1].lower()) for e in enrollments]
|
||||
enrollments = sorted(enrollments, key=lambda e: e[0])
|
||||
hash_elements = [user.username]
|
||||
hash_elements += ['{course_id}={mode}'.format(course_id=e[0], mode=e[1]) for e in enrollments]
|
||||
|
||||
@@ -7,6 +7,8 @@ from config_models.models import ConfigurationModel
|
||||
from django.db import models
|
||||
from django.utils.text import compress_string
|
||||
|
||||
from openedx.core.djangoapps.util.model_utils import CreatorMixin
|
||||
|
||||
logger = logging.getLogger(__name__) # pylint: disable=invalid-name
|
||||
|
||||
|
||||
@@ -40,12 +42,10 @@ def decompress_string(value):
|
||||
return ret
|
||||
|
||||
|
||||
class CompressedTextField(models.TextField):
|
||||
class CompressedTextField(CreatorMixin, models.TextField):
|
||||
""" TextField that transparently compresses data when saving to the database, and decompresses the data
|
||||
when retrieving it from the database. """
|
||||
|
||||
__metaclass__ = models.SubfieldBase
|
||||
|
||||
def get_prep_value(self, value):
|
||||
""" Compress the text data. """
|
||||
if value is not None:
|
||||
|
||||
@@ -45,7 +45,7 @@ def get_problem_grade_distribution(course_id):
|
||||
|
||||
# Loop through resultset building data for each problem
|
||||
for row in db_query:
|
||||
curr_problem = UsageKey.from_string(row['module_state_key']).map_into_course(course_id)
|
||||
curr_problem = row['module_state_key'].map_into_course(course_id)
|
||||
|
||||
# Build set of grade distributions for each problem that has student responses
|
||||
if curr_problem in prob_grade_distrib:
|
||||
@@ -85,7 +85,7 @@ def get_sequential_open_distrib(course_id):
|
||||
# Build set of "opened" data for each subsection that has "opened" data
|
||||
sequential_open_distrib = {}
|
||||
for row in db_query:
|
||||
row_loc = UsageKey.from_string(row['module_state_key']).map_into_course(course_id)
|
||||
row_loc = row['module_state_key'].map_into_course(course_id)
|
||||
sequential_open_distrib[row_loc] = row['count_sequential']
|
||||
|
||||
return sequential_open_distrib
|
||||
@@ -122,7 +122,7 @@ def get_problem_set_grade_distrib(course_id, problem_set):
|
||||
|
||||
# Loop through resultset building data for each problem
|
||||
for row in db_query:
|
||||
row_loc = UsageKey.from_string(row['module_state_key']).map_into_course(course_id)
|
||||
row_loc = row['module_state_key'].map_into_course(course_id)
|
||||
if row_loc not in prob_grade_distrib:
|
||||
prob_grade_distrib[row_loc] = {
|
||||
'max_grade': 0,
|
||||
|
||||
@@ -960,7 +960,7 @@ class ScoresClient(object):
|
||||
# attached to them (since old mongo identifiers don't include runs).
|
||||
# So we have to add that info back in before we put it into our lookup.
|
||||
self._locations_to_scores.update({
|
||||
UsageKey.from_string(location).map_into_course(self.course_key): self.Score(correct, total, created)
|
||||
location.map_into_course(self.course_key): self.Score(correct, total, created)
|
||||
for location, correct, total, created
|
||||
in scores_qset.values_list('module_state_key', 'grade', 'max_grade', 'created')
|
||||
})
|
||||
|
||||
@@ -199,6 +199,7 @@ def issued_certificates(course_key, features):
|
||||
# Report run date
|
||||
for data in generated_certificates:
|
||||
data['report_run_date'] = report_run_date
|
||||
data['course_id'] = str(data['course_id'])
|
||||
|
||||
return generated_certificates
|
||||
|
||||
|
||||
@@ -1055,7 +1055,7 @@ class MembershipListView(ExpandableFieldViewMixin, GenericAPIView):
|
||||
CourseAccessRole.objects.filter(user=request.user, role='staff').values_list('course_id', flat=True)
|
||||
)
|
||||
accessible_course_ids = [item for sublist in (enrolled_courses, staff_courses) for item in sublist]
|
||||
if requested_course_id is not None and requested_course_id not in accessible_course_ids:
|
||||
if requested_course_id is not None and requested_course_key not in accessible_course_ids:
|
||||
return Response(status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
if not specified_username_or_team:
|
||||
@@ -1068,7 +1068,7 @@ class MembershipListView(ExpandableFieldViewMixin, GenericAPIView):
|
||||
if requested_course_key is not None:
|
||||
course_keys = [requested_course_key]
|
||||
elif accessible_course_ids is not None:
|
||||
course_keys = [CourseKey.from_string(course_string) for course_string in accessible_course_ids]
|
||||
course_keys = accessible_course_ids
|
||||
|
||||
queryset = CourseTeamMembership.get_memberships(username, course_keys, team_id)
|
||||
page = self.paginate_queryset(queryset)
|
||||
|
||||
@@ -551,10 +551,7 @@ class CourseOverview(TimeStampedModel):
|
||||
"""
|
||||
Returns all course keys from course overviews.
|
||||
"""
|
||||
return [
|
||||
CourseKey.from_string(course_overview['id'])
|
||||
for course_overview in CourseOverview.objects.values('id')
|
||||
]
|
||||
return CourseOverview.objects.values_list('id', flat=True)
|
||||
|
||||
def is_discussion_tab_enabled(self):
|
||||
"""
|
||||
|
||||
27
openedx/core/djangoapps/util/model_utils.py
Normal file
27
openedx/core/djangoapps/util/model_utils.py
Normal file
@@ -0,0 +1,27 @@
|
||||
class Creator(object):
|
||||
"""
|
||||
A placeholder class that provides a way to set the attribute on the model.
|
||||
"""
|
||||
def __init__(self, field):
|
||||
self.field = field
|
||||
|
||||
def __get__(self, obj, type=None):
|
||||
if obj is None:
|
||||
return self
|
||||
return obj.__dict__[self.field.name]
|
||||
|
||||
def __set__(self, obj, value):
|
||||
obj.__dict__[self.field.name] = self.field.to_python(value)
|
||||
|
||||
|
||||
class CreatorMixin(object):
|
||||
"""
|
||||
Mixin class to provide SubfieldBase functionality to django fields.
|
||||
See: https://docs.djangoproject.com/en/1.11/releases/1.8/#subfieldbase
|
||||
"""
|
||||
def contribute_to_class(self, cls, name, *args, **kwargs):
|
||||
super(CreatorMixin, self).contribute_to_class(cls, name, *args, **kwargs)
|
||||
setattr(cls, name, Creator(self))
|
||||
|
||||
def from_db_value(self, value, expression, connection, context):
|
||||
return self.to_python(value)
|
||||
@@ -7,7 +7,7 @@ import warnings
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import models
|
||||
from opaque_keys.edx.keys import BlockTypeKey, CourseKey, UsageKey
|
||||
|
||||
from openedx.core.djangoapps.util.model_utils import CreatorMixin
|
||||
from xmodule.modulestore.django import modulestore
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -68,7 +68,7 @@ def _strip_value(value, lookup='exact'):
|
||||
return stripped_value
|
||||
|
||||
|
||||
class OpaqueKeyField(models.CharField):
|
||||
class OpaqueKeyField(CreatorMixin, models.CharField):
|
||||
"""
|
||||
A django field for storing OpaqueKeys.
|
||||
|
||||
@@ -81,8 +81,6 @@ class OpaqueKeyField(models.CharField):
|
||||
"""
|
||||
description = "An OpaqueKey object, saved to the DB in the form of a string."
|
||||
|
||||
__metaclass__ = models.SubfieldBase
|
||||
|
||||
Empty = object()
|
||||
KEY_CLASS = None
|
||||
|
||||
|
||||
Reference in New Issue
Block a user