Tastypie API return only one object for given parameters - json

I am overwriting the get_obj_list function. Following some parameters and a random function I would like to return an object related to the actual object that follows the parameters. This works fine. How can I return only this obj instead of a one-entry-list? Is there another function that better fits my purpose?
class SentenceRandomResource(ModelResource):
class Meta:
queryset = Sentence.objects.filter()
resource_name = 'sentence/random'
always_return_data = True
authorization = ReadOnlyAuthorization()
filtering = {'internal': ALL}
def obj_get_list(self, bundle, **kwargs):
if 'case' in bundle.request.GET.keys() and 'lemma' in bundle.request.GET.keys() :
if 'number' in bundle.request.GET.keys() :
words = Word.objects.filter(case = bundle.request.GET['case'], number = bundle.request.GET['number'], lemma = bundle.request.GET['lemma'])
else :
words = Word.objects.filter(case = bundle.request.GET['case'], lemma = bundle.request.GET['lemma'])
number_of_words = len(words)
if number_of_words > 0 :
random_index = int(random.random()*number_of_words)+0
random_word = words[random_index]
sentence = random_word.sentence
return [sentence]
else: ...
else: ...

Thanks to method prepend_url you may add some special functionality not included in RESTful principles.
import random
from tastypie.http import HttpBadRequest
class SentenceRandomResource(ModelResource):
class Meta:
queryset = Sentence.objects.filter()
resource_name = 'sentence/random'
always_return_data = True
authorization = ReadOnlyAuthorization()
filtering = {'internal': ALL}
def prepend_urls(self, *args, **kwargs):
name = 'get_one_random'
return [url(r"^(?P<resource_name>%s)/%s%s$" %
(self._meta.resource_name, name, trailing_slash()),
self.wrap_view(name), name="api_%s" % name)]
def get_one_random(self, request, **kwargs):
"""
Gets one random sentence of sentences with provided `case` and `lemma`
params.
"""
case = request.GET.get('case')
lemma = request.GET.get('lemma')
number = request.GET.get('number')
if case and lemma:
query_params = {'case': case, 'lemma': lemma}
if number is not None:
query_params['number'] = number
words = Word.objects.filter(**query_params)
word = random.choice(words)
return self.create_response(request, {'sentence': word.sentence.__dict__})
else:
return self.error_response(request, {'error': 'lemma and case are required.'},
response_class=HttpBadRequest)
Example use:
GET ..../sentence/random/get_one_random/?case=1&lemma=2
{'sentence': 'asdfasdf'}

Related

Filter checked checkboxes

By this I tried to make a minimal reproducible example of what I now have.
Using QSortFilterProxyModel() to find text and a custom SortFilterProxyModel to show all the selected checkboxes.
It looks like both proxy's conflicting eachother and giving the wrong row number back. (There is also an option for adding value with a dialog and also this goes to the wrong row with the custom proxy enabled, but works without the custom proxy).
class winConfigurator(QtWidgets.QMainWindow):
def __init__(self, data=None):
super(winConfigurator,self).__init__()
uic.loadUi(os.path.join(os.path.dirname(__file__), 'winConfiguratorView.ui'), self)
self.leSearch.returnPressed.connect(self.searchField)
self.chMatchSelected.toggled.connect(self.showSelected)
def readFile(self, filename):
self.model = TableModel([headers, newRows])
self.proxy_model = QSortFilterProxyModel()
self.proxy_model.setSourceModel(self.model)
"""
Proxy for 'Show Selected'
"""
self.custom_proxy_model = SortFilterProxyModel()
self.custom_proxy_model.setSourceModel(self.proxy_model)
self.tableView.setModel(self.custom_proxy_model)
def searchField(self):
self.proxy_model.setFilterFixedString(self.leSearch.text())
def showSelected(self, state = None):
self.custom_proxy_model.clearFilter()
checkstate_items = self.model.checks.items()
if state == True:
self.custom_proxy_model.setFilterByCheckbox(checkstate_items)
class SortFilterProxyModel(QSortFilterProxyModel):
def __init__(self, *args, **kwargs):
QSortFilterProxyModel.__init__(self, *args, **kwargs)
self.filters = {}
def setFilterByCheckbox(self, checkstates = {}):
self.filters = checkstates
self.invalidateFilter()
def clearFilter(self):
self.filters = {}
self.invalidateFilter()
def filterAcceptsRow(self, source_row, source_parent):
"""
Check if checkbox is checked and show this row
Slow, 7 seconds for 50k rows.
"""
try:
values = []
if self.filters:
for index, is_checked in self.filters:
if is_checked:
row = index.row()
model = self.sourceModel()
if hasattr(model, 'mapToSource'):
index = model.index(source_row, 0, source_parent)
if not index.parent().isValid():
modelIndex = model.mapToSource(index)
source_row = modelIndex.row()
if row == source_row:
values.append(index)
return any(values)
return True
except Exception as e:
# print(e)
return True

ID collision with Graphene-SQLAlchemy Interface class plus Node Interface

I've written Graphene models for polymorphic entities represented in my database w/SQLalchemy.
The problem is simple:
I want to create an interface that reflects my SQLAlchemy models for Graphene but also either a) implements Node or b) does not conflict with Node and allows me to retrieve the model's ID without needing to add ... on Node {id} to the query string.
have to exclude the ID field from my ORM-based interface or field conflicts with the Node interface, by doing so in order to get the ID then you need to add ...on Node { id }, which is ugly.
I created an SQLAlchemyInterface object that extends graphene.Interface. Many (but not all) of my models used this as well as Node as interfaces. The first problem was that this contains an ID field and it conflicted with the Node interface.
I excluded the id field to not interfere with Node, but then found I could not directly query ID on my models anymore, and had to add ... on Node {id} to the query string.
I then decided to have this SQLAlchemyInterface extend Node. I don't love this approach because I need to use another (named) Node interface for all of my models that don't necessarily need to implement SQLAlchemyInterface
class SQLAlchemyInterface(Node):
#classmethod
def __init_subclass_with_meta__(
cls,
model=None,
registry=None,
only_fields=(),
exclude_fields=(),
connection_field_factory=default_connection_field_factory,
_meta=None,
**options
):
_meta = SQLAlchemyInterfaceOptions(cls)
_meta.name = f'{cls.__name__}Node'
autoexclude_columns = exclude_autogenerated_sqla_columns(model=model)
exclude_fields += autoexclude_columns
assert is_mapped_class(model), (
"You need to pass a valid SQLAlchemy Model in " '{}.Meta, received "{}".'
).format(cls.__name__, model)
if not registry:
registry = get_global_registry()
assert isinstance(registry, Registry), (
"The attribute registry in {} needs to be an instance of "
'Registry, received "{}".'
).format(cls.__name__, registry)
sqla_fields = yank_fields_from_attrs(
construct_fields(
model=model,
registry=registry,
only_fields=only_fields,
exclude_fields=exclude_fields,
connection_field_factory=connection_field_factory
),
_as=Field
)
if not _meta:
_meta = SQLAlchemyInterfaceOptions(cls)
_meta.model = model
_meta.registry = registry
connection = Connection.create_type(
"{}Connection".format(cls.__name__), node=cls)
assert issubclass(connection, Connection), (
"The connection must be a Connection. Received {}"
).format(connection.__name__)
_meta.connection = connection
if _meta.fields:
_meta.fields.update(sqla_fields)
else:
_meta.fields = sqla_fields
super(SQLAlchemyInterface, cls).__init_subclass_with_meta__(_meta=_meta, **options)
#classmethod
def Field(cls, *args, **kwargs): # noqa: N802
return NodeField(cls, *args, **kwargs)
#classmethod
def node_resolver(cls, only_type, root, info, id):
return cls.get_node_from_global_id(info, id, only_type=only_type)
#classmethod
def get_node_from_global_id(cls, info, global_id, only_type=None):
try:
node: DeclarativeMeta = one_or_none(session=info.context.get('session'), model=cls._meta.model, id=global_id)
return node
except Exception:
return None
#staticmethod
def from_global_id(global_id):
return global_id
#staticmethod
def to_global_id(type, id):
return id
Interface impls, Models + Query code examples:
class CustomNode(Node):
class Meta:
name = 'UuidNode'
#staticmethod
def to_global_id(type, id):
return '{}:{}'.format(type, id)
#staticmethod
def get_node_from_global_id(info, global_id, only_type=None):
type, id = global_id.split(':')
if only_type:
# We assure that the node type that we want to retrieve
# is the same that was indicated in the field type
assert type == only_type._meta.name, 'Received not compatible node.'
if type == 'User':
return one_or_none(session=info.context.get('session'), model=User, id=global_id)
elif type == 'Well':
return one_or_none(session=info.context.get('session'), model=Well, id=global_id)
class ControlledVocabulary(SQLAlchemyInterface):
class Meta:
name = 'ControlledVocabularyNode'
model = BaseControlledVocabulary
class TrackedEntity(SQLAlchemyInterface):
class Meta:
name = 'TrackedEntityNode'
model = TrackedEntityModel
class Request(SQLAlchemyObjectType):
"""Request node."""
class Meta:
model = RequestModel
interfaces = (TrackedEntity,)
class User(SQLAlchemyObjectType):
"""User Node"""
class Meta:
model = UserModel
interfaces = (CustomNode,)
class CvFormFieldValueType(SQLAlchemyObjectType):
class Meta:
model = CvFormFieldValueTypeModel
interfaces = (ControlledVocabulary,)
common_field_kwargs = {'id': graphene.UUID(required=False), 'label': graphene.String(required=False)}
class Query(graphene.ObjectType):
"""Query objects for GraphQL API."""
node = CustomNode.Field()
te_node = TrackedEntity.Field()
cv_node = ControlledVocabulary.Field()
# Non-Tracked Entities:
users: List[User] = SQLAlchemyConnectionField(User)
# Generic Query for any Tracked Entity:
tracked_entities: List[TrackedEntity] = FilteredConnectionField(TrackedEntity, sort=None, filter=graphene.Argument(TrackedEntityInput))
# Generic Query for any Controlled Vocabulary:
cv: ControlledVocabulary = graphene.Field(ControlledVocabulary, controlled_vocabulary_type_id=graphene.UUID(required=False),
base_entry_key=graphene.String(required=False),
**common_field_kwargs)
cvs: List[ControlledVocabulary] = FilteredConnectionField(ControlledVocabulary, sort=None, filter=graphene.Argument(CvInput))
#staticmethod
def resolve_with_filters(info: ResolveInfo, model: Type[SQLAlchemyObjectType], **kwargs):
query = model.get_query(info)
log.debug(kwargs)
for filter_name, filter_value in kwargs.items():
model_filter_column = getattr(model._meta.model, filter_name, None)
log.debug(type(filter_value))
if not model_filter_column:
continue
if isinstance(filter_value, SQLAlchemyInputObjectType):
log.debug(True)
filter_model = filter_value.sqla_model
q = FilteredConnectionField.get_query(filter_model, info, sort=None, **kwargs)
# noinspection PyArgumentList
query = query.filter(model_filter_column == q.filter_by(**filter_value))
log.info(query)
else:
query = query.filter(model_filter_column == filter_value)
return query
def resolve_tracked_entity(self, info: ResolveInfo, **kwargs):
entity: TrackedEntity = Query.resolve_with_filters(info=info, model=BaseTrackedEntity, **kwargs).one()
return entity
def resolve_tracked_entities(self, info, **kwargs):
query = Query.resolve_with_filters(info=info, model=BaseTrackedEntity, **kwargs)
tes: List[BaseTrackedEntity] = query.all()
return tes
def resolve_cv(self, info, **kwargs):
cv: List[BaseControlledVocabulary] = Query.resolve_with_filters(info=info, model=BaseControlledVocabulary, **kwargs).one()
log.info(cv)
return cv
def resolve_cvs(self, info, **kwargs):
cv: List[BaseControlledVocabulary] = Query.resolve_with_filters(info=info, model=BaseControlledVocabulary, **kwargs).all()
return cv
schema:
schema = Schema(query=Query, types=[*tracked_members, *cv_members])
I would like to be able to not extend Node with SQLAlchemyInterface and rather add Node back to the list of interfaces for TrackedEntity and ControlledVocabulary but be able to perform a query like this:
query queryTracked {
trackedEntities{
id
(other fields)
... on Request {
(request specific fields)
}
}

Django rounds left 3 digits while displaying bigint in mysql

I am using Django to display rows in mysql.
The table in mysql has a primary key which it bigint, and one of them is 871195445245063168, 18 digits.
But on my page, I see 871195445245063200 displayed, the least 3 digits are rounded. I am wondering where I make it wrong.
1, I define a class with a function named data_query to query mysql.
class MyQuery:
self.conn = MySQLdb.connect(host = self.DBHOST, user = self.DBUSER,
passwd = self.DBPWD,port = self.DBPORT,charset = self.CHARSET,connect_timeout=3)
def data_query(self,sql):
cursor = self.conn.cursor(MySQLdb.cursors.DictCursor)
start = time.time()
cursor.execute(sql)
end = time.time()
sql_time = end - start
column_description = cursor.description
column_name = [ column[0] for column in column_description ]
res = cursor.fetchall()
cursor.close()
self.conn.close()
return res,column_name,sql_time
2, I defined a json encoder as follows
class CJsonEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime.datetime):
try:
return obj.strftime('%Y-%m-%d %H:%M:%S')
except ValueError:
return str(obj)
elif isinstance(obj, datetime.date):
try:
return obj.strftime('%Y-%m-%d')
except ValueError:
return str(obj)
elif isinstance(obj,datetime.timedelta):
return str(obj)
elif isinstance(obj, decimal.Decimal):
return float(obj)
elif isinstance(obj,ObjectId):
return str(obj)
else:
return json.JSONEncoder.default(self, obj)
3, I get my display like this, with sensitive info replaced.
db = MyQuery(host, user, pwd, port)
sql_statement = 'select * from mytable where Findex=871195445245063168 limit 10'
sql_result, table_column_name, sql_time = db.data_query(sql_statement)
query_result = {}
column_name = column_format(table_column_name)
query_result['column'] = column_name
query_result['data'] = list(sql_result)
return HttpResponse(json.dumps(query_result, cls=CJsonEncoder), content_type='application/json')
So, what I go wrong here? Thanks.
This is a JavaScript issue. Your number is bigger than the largest safe integer in JavaScript (Number.MAX_SAFE_INTEGER), so it is rounded.
You can verify this in your browser console or in node.js
$ node
> x = 871195445245063168
871195445245063200
I assume you are either using your response in some kind of JavaScript frontend or you have some Browser extension to render the JSON, which is written in JavaScript.
If you request that URL with a client like curl, you will see that it is returned correctly from the server.

Serialization through the Django Rest Framework - how to deserialize?

I have an array of the objects and then try to serialize it using the following statement:
serializer = MovieWithDescriptionSerializer(movies, many=True)
data = serializer.data
The class and the serializer are as below:
class MovieWithDescription(object):
id = 0
name = ''
description = ''
rating = ''
year = 0
def __init__(self, uid, name, description):
self.id = uid
self.name = name
self.description = description
class MovieWithDescriptionSerializer(serializers.Serializer):
class Meta:
model = MovieWithDescription
fields = ('id', 'name', 'description')
id = serializers.IntegerField()
name = serializers.StringRelatedField()
description = serializers.StringRelatedField()
The data is saved to session:
request.session['movies'] = data
And read on the other page:
movies = request.session['movies']
However when I tried to deserialize it I learned that the movies variable contains list. So it looks like I don't need to deserialize and just need to iterate through the list to process the data. What I'm doing wrong with this serialization? Is there any more simple way to serialize data than to use Django Rest Framework?
Answering your question from the comments section; also to clarify for whoever is migrating from a Java environment:
In Django Rest Framework; Serialization happens as follows:
Python Object --> serializer.data ==> Python Native Types --> Renderer Class ==> JSON
serializer.data is constructed by calling to_representation on each of the fields declared on the Serializer.
#property
def data(self):
...
if not hasattr(self, '_data'):
if self.instance is not None and not getattr(self, '_errors', None):
self._data = self.to_representation(self.instance)
elif hasattr(self, '_validated_data') and not getattr(self, '_errors', None):
self._data = self.to_representation(self.validated_data)
else:
self._data = self.get_initial()
return self._data
To representation is supposed to take an object instance and return a dict of primitive types:
def to_representation(self, instance):
"""
Object instance -> Dict of primitive datatypes.
"""
...
So if serializer.data does not render to JSON, where does this magic happen?
It happens inside the Response object. When you construct a Response object with the data attribute (data here is a dict of primitive or a list of dict of primitives), the rendered_content method then defines how the data is rendered, sets the appropriate Content-Type headers, and so on.
#property
def rendered_content(self):
renderer = getattr(self, 'accepted_renderer', None)
accepted_media_type = getattr(self, 'accepted_media_type', None)
context = getattr(self, 'renderer_context', None)
assert renderer, ".accepted_renderer not set on Response"
assert accepted_media_type, ".accepted_media_type not set on Response"
assert context is not None, ".renderer_context not set on Response"
context['response'] = self
media_type = renderer.media_type
charset = renderer.charset
content_type = self.content_type
if content_type is None and charset is not None:
content_type = "{0}; charset={1}".format(media_type, charset)
elif content_type is None:
content_type = media_type
self['Content-Type'] = content_type
ret = renderer.render(self.data, accepted_media_type, context)
if isinstance(ret, six.text_type):
assert charset, (
'renderer returned unicode, and did not specify '
'a charset value.'
)
return bytes(ret.encode(charset))
if not ret:
del self['Content-Type']
return ret
This allows you to do something neat; you can define a single Django Rest Framework View, that can render XML, JSON, and many more -> you can find them under rest_framework.renderers.
For example, you can define an APIView that supports multiple render formats based on the query parameter (?format=json, ?format=xml, ?format=csv) as follows:
class ExampleAPIView(APIView):
renderer_classes = (JSONRenderer, XMLRenderer, CSVRenderer)
...
XML and CSV require additional package installations. Read more here: http://www.django-rest-framework.org/api-guide/renderers/#xml

Easier way to create a JSON object from an SQLObject

EDIT -- took the code from below and made it so it can handle ForiegnKeys, Decimal numbers (although i'm doing a very forced float conversion). It returns a dict now so it can be recursive.
from sqlobject import SQLObject
from decimal import Decimal
def sqlobject_to_dict(obj):
json_dict = {}
cls_name = type(obj)
for attr in vars(cls_name):
if isinstance(getattr(cls_name, attr), property):
attr_value = getattr(obj, attr)
attr_class = type(attr_value)
attr_parent = attr_class.__bases__[0]
if isinstance(getattr(obj, attr), Decimal):
json_dict[attr] = float(getattr(obj, attr))
elif attr_parent == SQLObject:
json_dict[attr] = sqlobject_to_dict(getattr(obj, attr))
else:
json_dict[attr] = getattr(obj, attr)
return json_dict
EDIT -- changed to add the actual data model -- there are generated values that need to be accessed and Decimal() columns that need dealing with as well.
So I've seen this: return SQL table as JSON in python but it's not really what I'm looking for -- that's "brute force" -- you need to know the names of the attributes of the object in order to generate the JSON response.
What I'd like to do is something like this (the name of the class and it's attributes are not-important)
class BJCPStyle(SQLObject):
name = UnicodeCol(length=128, default=None)
beer_type = UnicodeCol(length=5, default=None)
category = ForeignKey('BJCPCategory')
subcategory = UnicodeCol(length=1, default=None)
aroma = UnicodeCol(default=None)
appearance = UnicodeCol(default=None)
flavor = UnicodeCol(default=None)
mouthfeel = UnicodeCol(default=None)
impression = UnicodeCol(default=None)
comments = UnicodeCol(default=None)
examples = UnicodeCol(default=None)
og_low = SGCol(default=None)
og_high = SGCol(default=None)
fg_low = SGCol(default=None)
fg_high = SGCol(default=None)
ibu_low = IBUCol(default=None)
ibu_high = IBUCol(default=None)
srm_low = SRMCol(default=None)
srm_high = SRMCol(default=None)
abv_low = DecimalCol(size=3, precision=1, default=None)
abv_high = DecimalCol(size=3, precision=1, default=None)
versions = Versioning()
def _get_combined_category_id(self):
return "%s%s" % (self.category.category_id, self.subcategory)
def _get_og_range(self):
low = self._SO_get_og_low()
high = self._SO_get_og_high()
if low == 0 and high == 0:
return "varies"
else:
return "%.3f - %.3f" % (low, high)
def _get_fg_range(self):
low = self._SO_get_fg_low()
high = self._SO_get_fg_high()
if low == 0 and high == 0:
return "varies"
else:
return "%.3f - %.3f" % (low, high)
def _get_srm_range(self):
low = self._SO_get_srm_low()
high = self._SO_get_srm_high()
if low == 0 and high == 0:
return "varies"
else:
return "%.1f - %.1f" % (low, high)
def _get_abv_range(self):
low = self._SO_get_abv_low()
high = self._SO_get_abv_high()
if low == 0 and high == 0:
return "varies"
else:
return "%.2f%% - %.2f%%" % (low, high)
def _get_ibu_range(self):
low = self._SO_get_ibu_low()
high = self._SO_get_ibu_high()
if low == 0 and high == 0:
return "varies"
else:
return "%i - %i" % (low, high)
Is there an easy way, pythonic way to write that magic to_json() function?
You can use the python json module with the SQLObject sqlmeta class. Like this:
def to_json(obj):
return json.dumps(dict((c, getattr(obj, c)) for c in obj.sqlmeta.columns))
When I run this with your class Foo I get:
>>> print to_json(f)
{"bar": "test", "lulz": "only for the", "baz": true}
Edit: if you want to include magic attributes in your json string and you don't mind using something of a hack, you could abuse the fact that the attributes of your object are python properties. For example, if I add a magic attribute foo to your original sample class:
class Foo(SQLObject):
bar = UnicodeCol(length=128)
baz = BoolCol(default=True)
lulz = UnicodeCol(length=256)
def _get_foo(self):
return "foo"
Then I can define the to_json() function like this:
def to_json(obj):
cls = type(obj)
d = dict((c, getattr(obj, c)) for c in vars(cls) if isinstance(getattr(cls, c), property))
return json.dumps(d)
Now, if I do this:
f = Foo(bar = "test", lulz = "only for the")
print to_json(f)
I get the following result:
{"baz": true, "lulz": "only for the", "bar": "test", "foo": "foo"}
import json
json.dumps(obj_instance.sqlmeta.asDict())
In my case this object contained datetimes which json doesn't serialize, so I did something like this:
json.dumps(dict((k, str(v)) for (k,v) in obj_instance.sqlmeta.asDict().items()))
Something like this ...
class MyTable( sqlobject.SQLObject ):
# ... your columns ...
json.dumps({
'MyTable': [row.sqlmeta.asDict() for row in MyTable.select()]
}, indent=4, sort_keys=True )
Suppose you have a list of sqlobject.SQLObject derived classes called
'Tables'
Tables = [MyTable, ...]
def dump():
r={}
for t in Tables:
r[t.__name__] = [row.sqlmeta.asDict() for row in t.select()]
return json.dumps(r, indent=4, sort_keys=True)