"'str' object has no attribute 'strftime'" in ComplexDateTimeField conversion - string

Well, I'm implementing a testcase in service, but I'm facing some problems.
Here's a sample of the code
datetime_one = mongo.ComplexDateTimeField()._convert_from_string('2019, 12, 20, 19, 24, 10, 451923')
visitor = Visit()
visitor.user_id = '750645c7-bf66-4023-9a2d-9c942a25f6cd'
visitor.timestamp = mongo.ComplexDateTimeField().to_mongo(datetime_one)
visitor.save()
visitor is an object from the class Visit:
from datetime import datetime
import mongoengine as mongo
class Visit(mongo.Document):
user_id = mongo.UUIDField(required=True)
timestamp = mongo.ComplexDateTimeField(required=True, default=datetime.utcnow, editable=False)
When I try run this code I get error in the line of visitor.timestamp...
AttributeError: 'str' object has no attribute 'strftime'
What am I doing wrong here?

You need to use datetime instances, not strings and you don't need to use to_mongo or _convert_from_string.
As described in the doc of the ComplexDateTimeField, the only difference with a regular DateTimeField is that the date will be stored as a string behind the scene but any interaction you have with the attribute will be with a datetime.
See below:
class Visit(Document):
timestamp = ComplexDateTimeField()
visit = Visit(timestamp=datetime.utcnow())
visit.save()
assert isinstance(visit.timestamp, datetime)
# print object as it is stored in mongodb
print(Visit.objects.as_pymongo())
# Output: [{'timestamp': '2020,01,23,22,24,21,449017', '_id': ObjectId('5e2a1d15f3ede875e9c0b806')}]
Feel free to look at the corresponding test file if you want more code samples.

Related

How can I convert string of dateTime to dateTime

takeDate is (Dec. 23, 2020, 8:23 p.m.), I want to change it to DateTime.
Editor Note: Would like to define a URL with variable from which a DateTime object can be created.
urls.py
from django.urls import path
from Reservations import views
urlpatterns = [
path('rent/<takeDate>/<returnDate>/<id>', views.rent, name='rent'),
]
views.py
def rent(request, takeDate, returnDate,id):
print(takeDate)
return render(request, 'reservations/rent.html')
You're better off defining the URL more explicitly to specify the format of the DateTime URL input. Have a look at this, a similar question although using an old version of Django.
Simple
If you define your URL knowing the date format you want to receive, you can easily convert to a datetime:
url_patterns = [
path("rent/<int:take_day>-<int:take_month>-<int:take_year>/<int:return_day>-<int:return_month>-<int:return_year>/<id>/"),
]
Here we have a route that will match numerical dates in the format day-month-year.
Then in your view function, you can grab the arguments like normal and convert them into a DateTime:
from datetime import datetime
def rent(request, take_day, take_month, take_year, return_day, return_month, return_year, id):
takeDateTime = datetime(take_year, take_month, take_day)
returnDateTime = datetime(return_year, return_month, return_day)
# ...
If you want to add in a time, you can keep adding to the URL pattern eg. (format: day-month-year-hour:minute)
"<int:take_day>-<int:take_month>-<int:take_year>-<int:take_hour>:<int:take_minute>"
Advanced
An even better solution, although more advanced is to use a custom path converter, you can read more about them in the Django docs: https://docs.djangoproject.com/en/3.1/topics/http/urls/#registering-custom-path-converters
I won't explain how to implement them here, as the Django docs will do a better job than me if you are interested in this method.

How to define the same field for load_only and dump_only params at the Marshmallow scheme?

I am trying to build a marshmallow scheme to both load and dump data. And I get everything OK except one field.
Problem description
(If you understand the problem, you don't have to read this).
For load data its type is Decimal. And I used it like this before. Now I want to use this schema for dumping and for that my flask API responses with: TypeError: Object of type Decimal is not JSON serializable. OK, I understand. I changed the type to Float. Then my legacy code started to get an exception while trying to save that field to database (it takes Decimal only). I don't want to change the legacy code so I looked for any solution at the marshmallow docs and found load_only and dump_only params. It seems like those are what I wanted, but here is my problem - I want to set them to the same field. So I just wondered if I can define both fields and tried this:
class PaymentSchema(Schema):
money = fields.Decimal(load_only=True)
money = fields.Float(dump_only=True)
I have been expected for a miracle, of course. Actually I was thinking that it will skip first definition (correctly, re-define it). What I got is an absence of the field at all.
Workaround solution
So I tried another solution. I created another schema for dump and inherit it from the former schema:
class PaymentSchema(Schema):
money = fields.Decimal(load_only=True)
class PaymentDumpSchema(PaymentSchema):
money = fields.Float(dump_only=True)
It works. But I wonder if there's some another, native, "marshmallow-way" solution for this. I have been looking through the docs but I can't find anything.
You can use the marshmallow decorator #pre_load in this decorator you can do whatever you want and return with your type
from marshmallow import pre_load
import like this and in this you will get your payload and change the type as per your requirement.
UPD: I found a good solution finally.
NEW SOLUTION
The trick is to define your field in load_fields and dump_fields inside __init__ method.
from marshmallow.fields import Integer, String, Raw
from marshmallow import Schema
class ItemDumpLoadSchema(Schema):
item = Raw()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if not (self.only and 'item' not in self.only) and \
not (self.exclude and 'item' in self.exclude):
self.load_fields['item'] = Integer(missing=0)
self.dump_fields['item'] = String()
Usage:
>>> ItemDumpLoadSchema().load({})
{'item': 0}
>>> ItemDumpLoadSchema().dump({'item': 0})
{'item': '0'}
Don't forget to define field in a schema with some field (Raw in my example) - otherwise it may raise an exception in some cases (e.g. using of only and exclude keywords).
OLD SOLUTION
A little perverted one. It based on #prashant-suthar answer. I named load field with suffix _load and implemented #pre_load, #post_load and error handling.
class ArticleSchema(Schema):
id = fields.String()
title = fields.String()
text = fields.String()
class FlowSchema(Schema):
article = fields.Nested(ArticleSchema, dump_only=True)
article_load = fields.Int(load_only=True)
#pre_load
def pre_load(self, data, *args, **kwargs):
if data.get('article'):
data['article_load'] = data.pop('article')
return data
#post_load
def post_load(self, data, *args, **kwargs):
if data.get('article_load'):
data['article'] = data.pop('article_load')
return data
def handle_error(self, exc, data, **kwargs):
if 'article_load' in exc.messages:
exc.messages['article'] = exc.messages.pop('article_load')
raise exc
Why the old solution is not a good solution?
It doesn't allow to inheritate schemas with different handle_error methods defined. And you have to name pre_load and post_load methods with different names.
pass data_key argument to the field definition
Documentation mentions, data_key parameter can be used along with dump_only or load_only to be able to have same field with different functionality.
So you can write your schema as...
class PaymentSchema(Schema):
decimal_money = fields.Decimal(data_key="money", load_only=True)
money = fields.Float(dump_only=True)
This should solve your problem. I am using data_key for similar problem in marshmallow with SQLAlchemyAutoSchema and this fixed my issue.
Edit
Note: The key in ValidationError.messages (error messages) will be decimal_money by default. You may tweak the handle_error method of Schema class to replace decimal_money with money but it is not recommended as you yourself may not be able to differentiate between the error messages fields.
Thanks.

Inconsistent datetime object in Python3 and Django2

Python 3, Django 2
I am trying to get a function to consistently return a datetime object.
Here are the conditions where it does and does not work.
I have a function that generates a datetime object with an offset like so:
from django.conf import settings
from datetime import datetime
def off_time():
date = datetime.now()
offset = settings.TIME_OFFSET
offtime = date + offset
return offtime
TIME_OFFSET is generated in settings thus:
from datetime import timedelta
TIME_OFFSET = timedelta(days=370000)
If I save to a model object like this:
from django.db import models
import stellar.physics_tools
class Test(models.Model):
cdate = models.DateTimeField(default=stellar.physics_tools.off_time, help_text='When Generated')
and then work on the cdate, I can do this:
cdate = test.cdate
creation_date = cdate.strftime("%d-%m-%Y %H:%M:%S")
and it works ok.
but if I try this:
newtime = stellar.physics_tools.off_time
return newtime.strftime("%d-%m-%Y %H:%M:%S")
I get:
'function' object has no attribute 'strftime'
I need this function to return an object that I can use .strftime on, at least.
Any help appreciated. Thanks.
For this to work I need to call the function with () like this:
newtime = stellar.physics_tools.off_time()
I was not doing this because if I did this in the Django model construction it returns the value created when the class is defined, which is not what I want.
In short I still need to use stellar.physics_tools.off_time in the Django model, but when calling the function outside of this I need to use stellar.physics_tools.off_time().

How to get Id from Object in Odoo 10?

This is my structure:
class Imprint_Location(models.Model):
_name = 'imprint.location'
name = fields.Char()
product_id = fields.Many2one('product.template')
class Imprint_Charges(models.Model):
_name = 'imprint.charge'
_rec_name = 'location_id'
product_id_c = fields.Many2one('product.template', required=True)
location_id = fields.Many2one('imprint.location', required=True)
#api.multi
#api.onchange('product_id_c', 'location_id')
def product_filter(self):
res = {}
print '\n\n-------\n\n', self, self.product_id_c, '\n\n-------\n\n'
if self.product_id_c:
res['domain'] = {'location_id': [('product_id', '=', self.product_id_c.id)]}
print res
return res
class Product_Template(models.Model):
_inherit = 'product.template'
imprint_location_ids = fields.One2many('imprint.location', 'product_id')
sale_imprint_charge_ids = fields.One2many('imprint.charge', 'product_id_c')
Now i have defined a page in product.template and inside the page is sale_imprint_charge_ids which is in <tree editable="bottom"> and i am not selecting the product_id_c field[also this field doesn't show up in the tree defined].
Now my problem here is that when i select this from the form view which i defined for imprint.charge the method product_filter works fine, but when i enter from the product.template then i get a error saying
TypeError: <odoo.models.NewId object at 0x7fbb8bc21b90> is not JSON serializable
Because from product.template if passes the object <odoo.models.NewId object at 0x7fbb8bc21b90> , so if print self.product_id_c then it prints product.template(<odoo.models.NewId object at 0x7fbb8bc21b90>) so this is not serializable. i have tried doing self.product_id_c.ids which give output empty list [].
So how do get the product.template id from the object or pass the id itself overriding some method.
You should improve couple of following points.
res['domain'] = {'location_id': [('product_id', '=', self.product_id_c.id)]}
return res
study some search() method of ORM
Try with following code:
#api.multi
#api.onchange('product_id_c', 'location_id')
def product_filter(self):
res = {}
if self.product_id_c:
self.location_id = False
#search product_template in imprint.locationwith table and limit we will get only record if related record found
location_id = self.env['imprint.location'].search([('product_id', '=', self.product_id_c.id)], limit=1)
if location_id:
#location_id.ids will give you something like [2] so we need to set value as 2
self.location_id = location_id.ids[0]
EDIT:
As per your first comment, you need a list of related location then we should following trick.
Remove product_filter() method
Add domain in imprint.charge object view file
For example:
<field name="location_id" domain="[('product_id', '=', product_id_c)]"/>
Afterwards, Restart Odoo server and upgrade your custom module.
When creating a brand new record Odoo creates that wierd <odoo.models.NewId object at 0x7fbb8bc21b90> object. After you have written the record this id gets turned into the normal ids that you are used to (an integer). In this situation you have a function which (not unreasonably) expects a real id value at a point when no such value really exists. You need to provide a fallback, such as evaluating if the id is an integer and providing an alternate value in that circumstance. Although your function seems to return an object which I dont quite know what you are expecting to happen. If you wish to modify the value of one of your fields I would modify the values of the self object rather that returning an object.

Mongoengine Document.update() example

Assuming that Venue is:
from mongoengine import *
from mongoengine_extras.fields import AutoSlugField
class Venue(Document):
name = StringField(required=True)
venue_slug = AutoSlugField()
I want to update all my venue_slug fields based on the name. I try:
for v in Venue.objects():
v(venue_slug = str(v.name)).update()
But I get:
v(venue_slug = str(v.name)).update()
TypeError: Error when calling the metaclass bases
'Venue' object is not callable
Is my update function correct? If you are not familiar with AutoSlugField() could you write an example for a StringField() update?
Your code incorrect. Try:
for v in Venue.objects():
v.update(set__venue_slug=str(v.name))
See documentation: http://docs.mongoengine.org/guide/querying.html#atomic-updates.

Resources