Is it possible to do something similar to this with a list, dictionary or something else?
data_dict = {
'title' : 'awesome title',
'body' : 'great body of text',
}
Model.objects.create(data_dict)
Even better if I can extend it:
Model.objects.create(data_dict, extra='hello', extra2='world')
If title and body are fields in your model, then you can deliver the keyword arguments in your dictionary using the ** operator.
Assuming your model is called MyModel:
# create instance of model
m = MyModel(**data_dict)
# don't forget to save to database!
m.save()
As for your second question, the dictionary has to be the final argument. Again, extra and extra2 should be fields in the model.
m2 =MyModel(extra='hello', extra2='world', **data_dict)
m2.save()
For any model DummyModel you can use DummyModel.objects.create(**data_dict). Does not require save after the create.
Not directly an answer to the question, but I find this code helped me create the dicts that save nicely into the correct answer. The type conversions made are required if this data will be exported to json.
I hope this helps:
#mod is a django database model instance
def toDict( mod ):
import datetime
from decimal import Decimal
import re
#Go through the object, load in the objects we want
obj = {}
for key in mod.__dict__:
if re.search('^_', key):
continue
#Copy my data
if isinstance( mod.__dict__[key], datetime.datetime ):
obj[key] = int(calendar.timegm( ts.utctimetuple(mod.__dict__[key])))
elif isinstance( mod.__dict__[key], Decimal ):
obj[key] = float( mod.__dict__[key] )
else:
obj[key] = mod.__dict__[key]
return obj
def toCsv( mod, fields, delim=',' ):
import datetime
from decimal import Decimal
#Dump the items
raw = []
for key in fields:
if key not in mod.__dict__:
continue
#Copy my data
if isinstance( mod.__dict__[key], datetime.datetime ):
raw.append( str(calendar.timegm( ts.utctimetuple(mod.__dict__[key]))) )
elif isinstance( mod.__dict__[key], Decimal ):
raw.append( str(float( mod.__dict__[key] )))
else:
raw.append( str(mod.__dict__[key]) )
return delim.join( raw )
Related
I'm trying to figure it out a way to create a namedtuple with variable fields depending on the data you receive, in my case, I'm using the data from StatCounter and not on all the periods are the same browsers. I tried this way but it is a bit ugly and I'm sure there is a better way to achieve it.
def namedtuple_fixed(name: str, fields: List[str]) -> namedtuple:
"""Check the fields of the namedtuple and changes the invalid ones."""
fields_fixed: List[str] = []
for field in fields:
field = field.replace(" ", "_")
if field[0].isdigit():
field = f"n{field}"
fields_fixed.append(field)
return namedtuple(name, fields_fixed)
Records: namedtuple = namedtuple("empty_namedtuple", "")
def read_file(file: str) -> List["Records"]:
"""
Read the file with info about the percentage of use of various browsers
"""
global Records
with open(file, encoding="UTF-8") as browsers_file:
reader: Iterator[List[str]] = csv.reader(browsers_file)
field_names: List[str] = next(reader)
Records = namedtuple_fixed("Record", field_names)
result: List[Records] = [
Records(
*[
dt.datetime.strptime(n, "%Y-%m").date()
if record.index(n) == 0
else float(n)
for n in record
]
)
for record in reader
]
return result
The "namedtuple_fixed" function is to fix the names that have invalid identifiers.
Basically, I want to create a named tuple that receives a variable number of parameters, depending on the file you want to analyze. And if it's with type checking incorporated (I mean using NamedTuple from the typing module), much better.
Thanks in advance.
This solves my problem, but just partially
class Record(SimpleNamespace):
def __repr__(self):
items = [f"{key}={value!r}" for key, value in self.__dict__.items()]
return f"Record({', '.join(items)})"
Using the types.SimpleSpace documentation
And it can cause problems, like for example if you initiallize a Record like the following:
foo = Record(**{"a": 1, "3a": 2})
print(foo.a) # Ok
print(foo.3a) # Syntax Error
I would like to change the way the relationships are displayed in the Flask-Admin Index view of a Model. I do have two models connected through a many-to-many relationship which get displayed in the admin index view as well. Unfortunately, the relationships are just separated using a comma, which thus the user might lose the overview quickly. Ideally, I would like to convert the relationships entries into a simple list (e.g. like with li in HTML).
Is there an easy way of achieving this?
Thanks a lot!
Instead of the comma, you can use a <br> tag as the separator.
see column_type_formatters.
Define default formatter and update a list type.
def lineby_list_formatter(view, values):
html = u'<br/> '.join(str(v) for v in values)
return Markup(html)
MY_DEFAULT_FORMATTERS = dict(typefmt.BASE_FORMATTERS)
MY_DEFAULT_FORMATTERS.update({
list: lineby_list_formatter
})
class EventView(ModelView):
...
column_type_formatters = MY_DEFAULT_FORMATTERS
Ok... I figured it out myself: You can manipulate the way the data is rendered with overwriting the function _get_list_value(). see code below
def _get_list_value(self, context, model, name, column_formatters,
column_type_formatters):
"""
Returns the value to be displayed.
:param context:
:py:class:`jinja2.runtime.Context` if available
:param model:
Model instance
:param name:
Field name
:param column_formatters:
column_formatters to be used.
:param column_type_formatters:
column_type_formatters to be used.
"""
column_fmt = column_formatters.get(name)
if column_fmt is not None:
value = column_fmt(self, context, model, name)
else:
value = self._get_field_value(model, name)
choices_map = self._column_choices_map.get(name, {})
if choices_map:
return choices_map.get(value) or value
type_fmt = None
for typeobj, formatter in column_type_formatters.items():
if isinstance(value, typeobj):
type_fmt = formatter
break
if type_fmt is not None:
value = type_fmt(self, value)
### overwritten here
if name == 'items':
html_string = '<ul>'
for item in value.split(','):
html_string += '<li> {} </li>'.format(item)
html_string += '</ul>'
value = Markup(html_string)
return value
I've got a Django SessionWizardView in which I want to add extra data for the user to take advantage of during the steps. Essentially I want to build a list, and a dict which stores information about the steps once they are complete.
The first step in the wizard allows a user to add information about themselves and at the end allows the option to add another person's details. If this option is selected another, conditional, form is rendered & I'd like to provide them with the option to use the data entered previously.
So during the process_step() method I'm creating a list, and then a corresponding dictionary of data for each step in the process. Initially I had these as class attributes, but feel they would be better suited in a user's session so I've attempted to add them like so;
def process_step(self, form):
form_data = self.get_form_step_data(form)
current_step = self.storage.current_step or ''
data_dict = self.request.session.get('data_dict', dict())
data_list = self.request.session.get('data_list', list())
if current_step in data_dict:
# Always replace the existing data for a step.
data_dict.pop(current_step)
if not isinstance(form, TermsForm):
entrant_data = dict()
for k, v in form_data.iteritems():
entrant_data[k] = v
for k in entrant_data.iterkeys():
new_key = re.sub('{}-'.format(current_step), u'', k)
entrant_data[new_key] = entrant_data.pop(k)
data_dict[current_step] = entrant_data
done = False
for i, data in enumerate(data_list):
if data[0] == current_step:
data_list[i] = (
current_step, u'{} {}'.format(
entrant_data['first_name'],
entrant_data['last_name']
)
)
done = True
if not done:
data_list.append(
(
current_step, u'{} {}'.format(
entrant_data['first_name'],
entrant_data['last_name']
)
)
)
self.request.session['data_dict'] = data_dict
self.request.session['data_list'] = data_list
self.request.session.modified = True
return form_data
After this method is ran my new session keys aren't part of the session. From what I've been reading, this is a valid way of setting session data, but have I made a mistake somewhere?
Of the top of my head, your process_step function call misses the explicit request parm. It's mostly called like this:
process_step(self, request, form, step):
Playing with new Google App Engine MapReduce library filters for input_reader I would like to know how can I filter by ndb.Key.
I read this post and I've played with datetime, string, int, float, in filters tuples, but How I can filter by ndb.Key?
When I try to filter by a ndb.Key I get this error:
BadReaderParamsError: Expected Key, got u"Key('Clients', 406)"
Or this error:
TypeError: Key('Clients', 406) is not JSON serializable
I tried to pass a ndb.Key object and string representation of the ndb.Key.
Here are my two filters tuples:
Sample 1:
input_reader': {
'input_reader': 'mapreduce.input_readers.DatastoreInputReader',
'entity_kind': 'model.Sales',
'filters': [("client","=", ndb.Key('Clients', 406))]
}
Sample 2:
input_reader': {
'input_reader': 'mapreduce.input_readers.DatastoreInputReader',
'entity_kind': 'model.Sales',
'filters': [("client","=", "%s" % ndb.Key('Clients', 406))]
}
This is a bit tricky.
If you look at the code on Google Code you can see that mapreduce.model defines a JSON_DEFAULTS dict which determines the classes that get special-case handling in JSON serialization/deserialization: by default, just datetime. So, you can monkey-patch the ndb.Key class into there, and provide it with functions to do that serialization/deserialization - something like:
from mapreduce import model
def _JsonEncodeKey(o):
"""Json encode an ndb.Key object."""
return {'key_string': o.urlsafe()}
def _JsonDecodeKey(d):
"""Json decode a ndb.Key object."""
return ndb.Key(urlsafe=d['key_string'])
model.JSON_DEFAULTS[ndb.Key] = (_JsonEncodeKey, _JsonDecodeKey)
model._TYPE_IDS['Key'] = ndb.Key
You may also need to repeat those last two lines to patch mapreduce.lib.pipeline.util as well.
Also note if you do this, you'll need to ensure that this gets run on any instance that runs any part of a mapreduce: the easiest way to do this is to write a wrapper script that imports the above registration code, as well as mapreduce.main.APP, and override the mapreduce URL in your app.yaml to point to your wrapper.
Make your own input reader based on DatastoreInputReader, which knows how to decode key-based filters:
class DatastoreKeyInputReader(input_readers.DatastoreKeyInputReader):
"""Augment the base input reader to accommodate ReferenceProperty filters"""
def __init__(self, *args, **kwargs):
try:
filters = kwargs['filters']
decoded = []
for f in filters:
value = f[2]
if isinstance(value, list):
value = db.Key.from_path(*value)
decoded.append((f[0], f[1], value))
kwargs['filters'] = decoded
except KeyError:
pass
super(DatastoreKeyInputReader, self).__init__(*args, **kwargs)
Run this function on your filters before passing them in as options:
def encode_filters(filters):
if filters is not None:
encoded = []
for f in filters:
value = f[2]
if isinstance(value, db.Model):
value = value.key()
if isinstance(value, db.Key):
value = value.to_path()
entry = (f[0], f[1], value)
encoded.append(entry)
filters = encoded
return filters
Are you aware of the to_old_key() and from_old_key() methods?
I had the same problem and came up with a workaround with computed properties.
You can add to your Sales model a new ndb.ComputedProperty with the Key id. Ids are just strings, so you wont have any JSON problems.
client_id = ndb.ComputedProperty(lambda self: self.client.id())
And then add that condition to your mapreduce query filters
input_reader': {
'input_reader': 'mapreduce.input_readers.DatastoreInputReader',
'entity_kind': 'model.Sales',
'filters': [("client_id","=", '406']
}
The only drawback is that Computed properties are not indexed and stored until you call the put() parameter, so you will have to traverse all the Sales entities and save them:
for sale in Sales.query().fetch():
sale.put()
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).