adm-ntuh-net/ntuh/dojango/data/modelstore/fields.py
2024-12-12 10:19:16 +08:00

197 lines
7 KiB
Python
Executable file

import utils
from exceptions import FieldException
import methods
__all__ = ('FieldException', 'StoreField'
'ReferenceField', 'DojoDateField')
class StoreField(object):
""" The base StoreField from which all ```StoreField```s derive
"""
def __init__(self, model_field=None, store_field=None, get_value=None, sort_field=None, can_sort=True):
""" A StoreField corresponding to a field on a model.
Arguments (all optional):
model_field
The name of the field on the model. If omitted then
it's assumed to be the attribute name given to this StoreField
in the Store definition.
Example:
>>> class MyStore(Store):
>>> field_1 = StoreField() # The model_field will be Model.field_1
>>> field_2 = StoreField('my_field') # The model_field will be Model.my_field
store_field
The name of the field in the final store. If omitted then
it will be the attribute name given to this StoreField in the
Store definition.
Example:
>>> class MyStore(Store):
>>> field_1 = StoreField() # The store_field will be 'field_1'
>>> field_2 = StoreField(store_field='my_store_field')
get_value
An instance of modelstore.methods.BaseMethod (or any callable)
used to get the final value from the field (or anywhere) that
will go in the store.
Example:
def get_custom_value():
return 'my custom value'
>>> class MyStore(Store):
# get_custom_value will be called with no arguments
>>> field_1 = StoreField(get_value=get_custom_value)
# Wrap your method in an instance of methods.BaseMethod if you want to pass
# custom arguments -- see methods.BaseMethod (and it's derivatives) for full docs.
>>> field_2 = StoreField(get_value=Method(get_custom_value, arg1, arg2, arg3))
sort_field
Denotes the string used with QuerySet.order_by() to sort the objects
by this field.
Either the value passed to 'order_by()' on Django
QuerySets or an instance of modelstore.methods.BaseMethod
(or any callable) which returns the value.
Requests to sort descending are handled automatically by prepending the sort field
with '-'
Example:
>>> class MyStore(Store):
# QuerySet.order_by() will be called like: QuerySet.order_by('my_model_field')
>>> field_1 = StoreField('my_model_field')
# Sorting by dotted fields.
>>> field_2 = StoreField('my.dotted.field', sort_field='my__dotted__field')
can_sort
Whether or not this field can be order_by()'d -- Default is True.
If this is False, then attempts to sort by this field will be ignored.
"""
self._model_field_name = model_field
self._store_field_name = store_field
self._store_attr_name = None # We don't know this yet
self.can_sort = can_sort
self._sort_field = sort_field
self._get_value = get_value
# Attach a reference to this field to the get_value method
# so it can access proxied_args
if self._get_value:
setattr(self._get_value, 'field', self)
# Proxied arguments (ie, RequestArg, ObjectArg etc.)
self.proxied_args = {}
def _get_sort_field(self):
""" Return the name of the field to be passed to
QuerySet.order_by().
Either the name of the value passed to 'order_by()' on Django
QuerySets or some method which returns the value.
"""
if (self._sort_field is None) or isinstance(self._sort_field, (str, unicode) ):
return self._sort_field
else:
return self._sort_field()
sort_field = property(_get_sort_field)
def _get_store_field_name(self):
""" Return the name of the field in the final store.
If an explicit store_field is given in the constructor then that is
used, otherwise it's the attribute name given to this field in the
Store definition.
"""
return self._store_field_name or self._store_attr_name
store_field_name = property(_get_store_field_name)
def _get_model_field_name(self):
""" Return the name of the field on the Model that this field
corresponds to.
If an explicit model_field (the first arg) is given in the constructor
then that is used, otherwise it's assumed to be the attribute name
given to this field in the Store definition.
"""
return self._model_field_name or self._store_attr_name
model_field_name = property(_get_model_field_name)
def get_value(self):
""" Returns the value for this field
"""
if not self._get_value:
self._get_value = methods.ObjectMethod(self.model_field_name)
self._get_value.field = self
return self._get_value()
class ReferenceField(StoreField):
""" A StoreField that handles '_reference' items
Corresponds to model fields that refer to other models,
ie, ForeignKey, ManyToManyField etc.
"""
def get_value(self):
""" Returns a list (if more than one) or dict
of the form:
{'_reference': '<item identifier>'}
"""
# The Store we're attached to
store = self.proxied_args['StoreArg']
items = []
if not self._get_value:
self._get_value = methods.ObjectMethod(self.model_field_name)
self._get_value.field = self
related = self._get_value()
if not bool(related):
return items
# Is this a model instance (ie from ForeignKey) ?
if hasattr(related, '_get_pk_val'):
return {'_reference': store.get_identifier(related)}
# Django Queryset or Manager
if hasattr(related, 'iterator'):
related = related.iterator()
try:
for item in related:
items.append({'_reference': store.get_identifier(item)})
except TypeError:
raise FieldException('Cannot iterate on field "%s"' % (
self.model_field_name
))
return items
###
# Pre-built custom Fields
###
class DojoDateField(StoreField):
def get_value(self):
self._get_value = methods.DojoDateMethod
self._get_value.field = self
return self._get_value()