Commit 10480c3a by Oleg Marshev

Use keys as strings.

parent aced6994
import json import json
from django.db import models from django.db import models
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from opaque_keys.edx.keys import CourseKey, UsageKey
from opaque_keys.edx.locations import BlockUsageLocator, CourseLocator
def _strip_object(key):
"""
Strips branch and version info if the given key supports those attributes.
"""
if hasattr(key, 'version_agnostic') and hasattr(key, 'for_branch'):
return key.for_branch(None).version_agnostic()
else:
return key
def _strip_value(value, lookup='exact'):
"""
Helper function to remove the branch and version information from the given value,
which could be a single object or a list.
"""
if lookup == 'in':
stripped_value = [_strip_object(el) for el in value]
else:
stripped_value = _strip_object(value)
return stripped_value
class OpaqueKeyField(models.CharField):
"""
A django field for storing OpaqueKeys.
The baseclass will return the value from the database as a string, rather than an instance
of an OpaqueKey, leaving the application to determine which key subtype to parse the string
as.
Subclasses must specify a KEY_CLASS attribute, in which case the field will use :meth:`from_string`
to parse the key string, and will return an instance of KEY_CLASS.
"""
description = "An OpaqueKey object, saved to the DB in the form of a string."
__metaclass__ = models.SubfieldBase
Empty = object()
KEY_CLASS = None
def __init__(self, *args, **kwargs):
if self.KEY_CLASS is None:
raise ValueError('Must specify KEY_CLASS in OpaqueKeyField subclasses')
super(OpaqueKeyField, self).__init__(*args, **kwargs)
def to_python(self, value):
if value is self.Empty or value is None:
return None
assert isinstance(value, (basestring, self.KEY_CLASS))
if value == '':
# handle empty string for models being created w/o fields populated
return None
if isinstance(value, basestring):
return self.KEY_CLASS.from_string(value)
else:
return value
def get_prep_lookup(self, lookup, value):
if lookup == 'isnull':
raise TypeError('Use {0}.Empty rather than None to query for a missing {0}'.format(self.__class__.__name__))
return super(OpaqueKeyField, self).get_prep_lookup(
lookup,
# strip key before comparing
_strip_value(value, lookup)
)
def get_prep_value(self, value):
if value is self.Empty or value is None:
return '' # CharFields should use '' as their empty value, rather than None
assert isinstance(value, self.KEY_CLASS)
return unicode(_strip_value(value))
def validate(self, value, model_instance):
"""Validate Empty values, otherwise defer to the parent"""
# raise validation error if the use of this field says it can't be blank but it is
if not self.blank and value is self.Empty:
raise ValidationError(self.error_messages['blank'])
else:
return super(OpaqueKeyField, self).validate(value, model_instance)
def run_validators(self, value):
"""Validate Empty values, otherwise defer to the parent"""
if value is self.Empty:
return
return super(OpaqueKeyField, self).run_validators(value)
class CourseKeyField(OpaqueKeyField):
"""
A django Field that stores a CourseKey object as a string.
"""
description = "A CourseKey object, saved to the DB in the form of a string"
KEY_CLASS = CourseKey
class UsageKeyField(OpaqueKeyField):
"""
A django Field that stores a UsageKey object as a string.
"""
description = "A Location object, saved to the DB in the form of a string"
KEY_CLASS = UsageKey
class Note(models.Model): class Note(models.Model):
user_id = models.CharField(max_length=255) user_id = models.CharField(max_length=255)
course_id = CourseKeyField(max_length=255) course_id = models.CharField(max_length=255)
usage_id = UsageKeyField(max_length=255) usage_id = models.CharField(max_length=255)
text = models.TextField(default="") text = models.TextField(default="")
quote = models.TextField(default="") quote = models.TextField(default="")
range_start = models.CharField(max_length=2048) range_start = models.CharField(max_length=2048)
...@@ -142,8 +31,8 @@ class Note(models.Model): ...@@ -142,8 +31,8 @@ class Note(models.Model):
self.quote = note.get('quote', '') self.quote = note.get('quote', '')
try: try:
self.course_id = CourseLocator.from_string(note['course_id']) self.course_id = note['course_id']
self.usage_id = BlockUsageLocator.from_string(note['usage_id']) self.usage_id = note['usage_id']
self.user_id = note['user'] self.user_id = note['user']
except KeyError as error: except KeyError as error:
raise ValidationError('Note must have a course_id and usage_id and user_id.') raise ValidationError('Note must have a course_id and usage_id and user_id.')
...@@ -167,8 +56,8 @@ class Note(models.Model): ...@@ -167,8 +56,8 @@ class Note(models.Model):
return { return {
'id': self.pk, 'id': self.pk,
'user': self.user_id, 'user': self.user_id,
'course_id': self.course_id.html_id(), 'course_id': self.course_id,
'usage_id': self.usage_id.to_deprecated_string(), 'usage_id': self.usage_id,
'text': self.text, 'text': self.text,
'quote': self.quote, 'quote': self.quote,
'ranges': [{ 'ranges': [{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment