How to save data as BlobProperty instead of multiple ListProperties? - python

Currently I have the following code:
class User(db.Model):
field_names = db.StringListProperty(indexed=False)
field_values = db.StringListProperty(indexed=False)
field_scores = db.ListProperty(int, indexed=False)
def fields_add(user_key_name, field_name, field_value, field_score):
user = User.get(user_key_name)
if user:
try:
field_index = user.field_names.index(field_name) # (1)
user.field_values[field_index] = field_value
user.field_scores[field_index] = field_score
except ValueError:
# field wasn't added to the list before
user.field_names.append(field_name)
user.field_values.append(field_value)
user.field_scores.append(field_score)
user.put()
It works well, but I would like to optimize that - serialize field_name, field_value and field_score and store in one BlobProperty:
class User(db.Model):
fields = db.ListProperty(indexed=False)
f = {
'f': field_name,
'v': field_value,
's': field_score,
}
user.fields = simplejson.dumps(f)
But how should code (1) look like with such approach? How to find record for update?

If user.fields is a list of dicts where 'f' is the field name, this is one possible answer to your immediate question:
field_index = [field['f'] for field in user.fields].index(field_name)
It's not immediately clear why your revision is more optimal in your case, but I'll take your word for it. :)

You can serialize objects with json or pickle.
So for instance. If your model holds a property : udata = db.BlobProperty()
The serialize an object like : ..udata = pickle.dumps(object).

Related

Odoo: How to create many records in Transient.Model?

This code only creates one record. What is wrong?
class PartnerTagCreate(models.TransientModel):
""" Choose tags to be added to partner."""
_name = 'partner.tags.create'
_description = __doc__
market_id = fields.Many2one('partner.tags', string='Market Tag')
application_id = fields.Many2one('partner.tags', string='Application Tag')
partner_id = fields.Integer()
#api.multi
def create_contact_tag(self):
for record in self.env['sale.order.line'].browse(self._context.get('active_ids', [])):
vals = {}
vals['partner_id'] = record.order_partner_id
self.write(vals)
return True
I need this function to create one record for each order_partner_id I selected before opening the wizard...
How to achieve that?
Here my new code (function) ...
def create_contact_tag(self):
sale_order_line_ids = self.env['sale.order.line'].browse(self._context.get('active_ids', []))
for partner in sale_order_line_ids:
values = {}
values['partner_id'] = partner.order_partner_id
self.create(values)
return {}
This creates one record for marketing_id and/or application_id and dedicated records for each partner_id in the record.
You use the 'create' method to create new records; this is the same for TransientModel as for the persistent Model.
So, replace
self.write(vals)
by
self.create(vals)
and you should be fine.

Can I lookup a related field using a Q object in the Django ORM?

In Django, can I re-use an existing Q object on multiple models, without writing the same filters twice?
I was thinking about something along the lines of the pseudo-Django code below, but did not find anything relevant in the documentation :
class Author(Model):
name = TextField()
company_name = TextField()
class Book(Model):
author = ForeignKey(Author)
# Create a Q object for the Author model
q_author = Q(company_name="Books & co.")
# Use it to retrieve Book objects
qs = Book.objects.filter(author__matches=q_author)
If that is not possible, can I extend an existing Q object to work on a related field? Pseudo-example :
# q_book == Q(author__company_name="Books & co.")
q_book = q_author.extend("author")
# Use it to retrieve Book objects
qs = Book.objects.filter(q_book)
The only thing I've found that comes close is using a subquery, which is a bit unwieldy :
qs = Book.objects.filter(author__in=Author.objects.filter(q_author))
From what I can tell by your comment, it just looks like you're trying to pass a set of common arguments to multiple filters, to do that you can just unpack a dictionary
The values in the dictionary can still be q objects if required as if it were a value you would pass in to the filter argument normally
args = { 'author__company_name': "Books & co" }
qs = Book.objects.filter(**args)
args['author_name'] = 'Foo'
qs = Book.objects.filter(**args)
To share this between different models, you'd have to do some dictionary mangling
author_args = { k.lstrip('author__'): v for k, v in args.items }
You can do this
books = Book.objects.filter(author__company_name="Books & co")

Query different collection by different variable in mongoengine and Django

Is it possible to use variable as the part of collection name and query different collection based on the name in mongoengine?
For example:
There are 3 collections in my mongoDB
collection_first
collection_second
collection_third
and execute a simple for-loop like:
collection_names = ['first', 'second', 'third']
for name in collection_names:
## Query the collection_+`name` here
By the way, I am using mongoengin in Django, how to set the model.py of this kind of scenario?
class Testing(DynamicDocument):
# The collection_name should be dynamic, isn't it?
meta = {'collection' : 'collection_name'}
user_name = StringField(db_field='user_name')
Thank you very much.
Update the solution.
Define the Model in models.py without meta:
class Testing(DynamicDocument):
## Do NOT use the meta to point to a specific collection.
user_name = StringField(db_field='user_name')
When you call the function, use switch_collection to switch to the real collection:
def search_in_testing(self, name, **kwargs):
with switch_collection(Testing, 'colection_%s' % (name)):
search_results = Testing.objects(**kwargs)
return search_results
In your code, just call the function in for loop:
collection_names = ['first', 'second', 'third']
for name in collection_names:
search_results = search_in_testing(name, name=name)
Reference: switch_collection in mongoengine
Perhaps the following test in this commit would be of help in some way:
def test_dynamic_collection_naming(self)
def create_collection_name(cls):
return "PERSON"
class DynamicPerson(Document):
name = StringField()
age = IntField()
meta = {'collection': create_collection_name}
collection = DynamicPerson._get_collection_name()
self.assertEquals(collection, 'PERSON')
DynamicPerson(name='Test User', age=30).save()
self.assertTrue(collection in self.db.collection_names())
Yes you can do it like this. As an example,
for name in collection_names:
for doc in db[collection_+'name'].find():
print doc
Here db is Database object.

Returning verbose names from query set when using json serializer

Is it possible to call
tasks = models.Conference.objects.filter(location_id=key)
data = serializers.serialize("json", tasks)
and have it return the verbose field names rather than the variable names?
One way to accomplish this, is by monkey patching the methods within the django.core.serializers.python.Serializer class to return each fields verbose_name opposed to the standard name attribute.
Take for example the following code...
models.py
from django.db import models
class RelatedNode(models.Model):
name = models.CharField(max_length=100, verbose_name="related node")
class Node(models.Model):
name = models.CharField(max_length=100, verbose_name="verbose name")
related_node = models.ForeignKey(RelatedNode, verbose_name="verbose fk related node", related_name="related_node")
related_nodes = models.ManyToManyField(RelatedNode, verbose_name="verbose related m2m nodes", related_name="related_nodes")
I create these model objects within the database...
RelatedNode.objects.create(name='related_node_1')
RelatedNode.objects.create(name='related_node_2')
RelatedNode.objects.create(name='related_node_fk')
Node.objects.create(name='node_1', related_node=RelatedNode.objects.get(name='related_node_fk'))
Node.objects.all()[0].related_nodes.add(RelatedNode.objects.get(name='related_node_1'))
Node.objects.all()[0].related_nodes.add(RelatedNode.objects.get(name='related_node_2'))
views.py
from testing.models import Node
from django.utils.encoding import smart_text, is_protected_type
from django.core.serializers.python import Serializer
from django.core import serializers
def monkey_patch_handle_field(self, obj, field):
value = field._get_val_from_obj(obj)
# Protected types (i.e., primitives like None, numbers, dates,
# and Decimals) are passed through as is. All other values are
# converted to string first.
if is_protected_type(value):
self._current[field.verbose_name] = value
else:
self._current[field.verbose_name] = field.value_to_string(obj)
def monkey_patch_handle_fk_field(self, obj, field):
if self.use_natural_foreign_keys and hasattr(field.rel.to, 'natural_key'):
related = getattr(obj, field.name)
if related:
value = related.natural_key()
else:
value = None
else:
value = getattr(obj, field.get_attname())
self._current[field.verbose_name] = value
def monkey_patch_handle_m2m_field(self, obj, field):
if field.rel.through._meta.auto_created:
if self.use_natural_foreign_keys and hasattr(field.rel.to, 'natural_key'):
m2m_value = lambda value: value.natural_key()
else:
m2m_value = lambda value: smart_text(value._get_pk_val(), strings_only=True)
self._current[field.verbose_name] = [m2m_value(related)
for related in getattr(obj, field.name).iterator()]
Serializer.handle_field = monkey_patch_handle_field
Serializer.handle_fk_field = monkey_patch_handle_fk_field
Serializer.handle_m2m_field = monkey_patch_handle_m2m_field
serializers.serialize('json', Node.objects.all())
This outputs for me...
u'[{"fields": {"verbose fk related node": 3, "verbose related m2m nodes": [1, 2], "verbose name": "node_1"}, "model": "testing.node", "pk": 1}]'
As we could see, this actually gives us back the verbose_name of each field as keys in the returned dictionaries.

Serializing ReferenceProperty in Appengine Datastore to JSON

I am using the following code to serialize my appengine datastore to JSON
class DictModel(db.Model):
def to_dict(self):
return dict([(p, unicode(getattr(self, p))) for p in self.properties()])
class commonWordTweets(DictModel):
commonWords = db.StringListProperty(required=True)
venue = db.ReferenceProperty(Venue, required=True, collection_name='commonWords')
class Venue(db.Model):
id = db.StringProperty(required=True)
fourSqid = db.StringProperty(required=False)
name = db.StringProperty(required=True)
twitter_ID = db.StringProperty(required=True)
This returns the following JSON response
[
{
"commonWords": "[u'storehouse', u'guinness', u'badge', u'2011"', u'"new', u'mayor', u'dublin)']",
"venue": "<__main__.Venue object at 0x1028ad190>"
}
]
How can I return the actual venue name to appear?
Firstly, although it's not exactly your question, it's strongly recommended to use simplejson to produce json, rather than trying to turn structures into json strings yourself.
To answer your question, the ReferenceProperty just acts as a reference to your Venue object. So you just use its attributes as per normal.
Try something like:
cwt = commonWordTweets() # Replace with code to get the item from your datastore
d = {"commonWords":cwt.commonWords, "venue": cwt.venue.name}
jsonout = simplejson.dumps(d)

Categories