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
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.
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.
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().
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.
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.