We are trying to run code that modifies a document when it loads as part of schema maintenance. We have a document such as
from mongoengine import Document
from mongoengine.fields import IntField, StringField
class User(Document):
version = IntField(default=0)
name = StringField()
Instances of User are created with version=1 and saved.
Later on, we modify this class as follows:
class User(Document):
version = IntField(default=0)
name = StringField(max_length=20)
Some of the version 1 documents now need to be truncated:
def upgrade_1_to_2(self):
self.name = self.name[:20]
We want to automatically run that function whenever a User is retrieved from the database, and only if version == 1. (Future upgrades would be upgrade_2_to_3(), and so on.)
Where in this API can I put code that runs when a document is retrieved?
Looks like it's a good candidate for the post_init signal and you can do a check there.
See: http://docs.mongoengine.org/guide/signals.html
Related
We are having one django rest framework (DRF) project which should have multiple databases (mongoDB).Each databases should be independed. We are able to connect to one database, but when we are going to another DB for writing connection is happening but data is storing in DB which is first connected.
We changed default DB and everything but no changes.
(Note : Solution should be apt for the usage of serializer. Because we need to use DynamicDocumentSerializer in DRF-mongoengine.
Thanks in advance.
While running connect() just assign an alias for each of your databases and then for each Document specify a db_alias parameter in meta that points to a specific database alias:
settings.py:
from mongoengine import connect
connect(
alias='user-db',
db='test',
username='user',
password='12345',
host='mongodb://admin:qwerty#localhost/production'
)
connect(
alias='book-db'
db='test',
username='user',
password='12345',
host='mongodb://admin:qwerty#localhost/production'
)
models.py:
from mongoengine import Document
class User(Document):
name = StringField()
meta = {'db_alias': 'user-db'}
class Book(Document):
name = StringField()
meta = {'db_alias': 'book-db'}
I guess, I finally get what you need.
What you could do is write a really simple middleware that maps your url schema to the database:
from mongoengine import *
class DBSwitchMiddleware:
"""
This middleware is supposed to switch the database depending on request URL.
"""
def __init__(self, get_response):
# list all the mongoengine Documents in your project
import models
self.documents = [item for in dir(models) if isinstance(item, Document)]
def __call__(self, request):
# depending on the URL, switch documents to appropriate database
if request.path.startswith('/main/project1'):
for document in self.documents:
document.cls._meta['db_alias'] = 'db1'
elif request.path.startswith('/main/project2'):
for document in self.documents:
document.cls._meta['db_alias'] = 'db2'
# delegate handling the rest of response to your views
response = get_response(request)
return response
Note that this solution might be prone to race conditions. We're modifying a Documents globally here, so if one request was started and then in the middle of its execution a second request is handled by the same python interpreter, it will overwrite document.cls._meta['db_alias'] setting and first request will start writing to the same database, which will break your database horribly.
Same python interpreter is used by 2 request handlers, if you're using multithreading. So with this solution you can't start your server with multiple threads, only with multiple processes.
To address the threading issues, you can use threading.local(). If you prefer context manager approach, there's also a contextvars module.
So I created a model for storing credentials from Gmail users.
I wanted to make migrations but it says that there is no such table:
django.db.utils.OperationalError: no such table: mainApp_credentialsmodel
My models:
from django.db import models
# Create your models here.
from django.contrib.auth.models import User
from django.db import models
import json
class CredentialsModel(models.Model):
id = models.ForeignKey(User, primary_key=True,on_delete=models.CASCADE)
credential = models.CharField(max_length=1000)
Calling that model for checking authorization:
SCOPES = 'https://www.googleapis.com/auth/gmail.readonly'
store = CredentialsModel.objects.all()
creds = store.get()
if not creds or creds.invalid:
flow = client.flow_from_clientsecrets('mainApp/client_secret.json', SCOPES)
creds = tools.run_flow(flow, store)
service = build('gmail', 'v1', http=creds.authorize(Http()))
python manage.py makemigrations
If that error keep happening, check your migrations folder and check the files inside. Also check If your database is online, in case you have a database online, I've got this problem last week, but it was a problem with azure.
In last case I would create the table (model) again, changing the name to something similar, but If you have a significant amount of data in that table, then I think you can't do that.
It looks like your authorization code - including the query on CredentialsModel - is at module level. This means it runs when the module is imported, which happens before the migration has had a chance to run.
You must ensure that any database-accessing code is inside a function or method and is not invoked globally.
I search a way to request components or assets from groupId and artefactId.
Documentation provides any help about how to create this request.
This doc has been a great help.
Unfortunately, it's not enough to resolve my need and I try to create query to request components from groupId and artefactId like that:
Query.builder().where('group = ').param('###').and('name = ').param('###').and('version = ').param('###).build()
Last time I played my script it throwed java.lang.StackOverflowError. After increasing memory, I had the same result. It seems there is too much components to return, but in my nexus repository there's only one component with such group, name and version.
What's wrong with this query?
Is there someone pass this difficulty (it was so easy with nexus2 and rest api!) and retrieve components information with a groovy script?
Here is the script I successfully uploaded and use. You may easily add version to your requests - the parameter will be, for example, "snapshots-lib,com.m121.somebundle,someBundle,7.2.1-SNAPSHOT,all". As you may see, I decided to filter the sequence locally, because did not find the way to specify in query version parameter.
{
"name": "listgroup",
"type": "groovy",
"content": "import org.sonatype.nexus.repository.storage.Query;
import org.sonatype.nexus.repository.storage.StorageFacet;
import groovy.json.JsonOutput;
def repositoryId = args.split(',')[0];
def groupId = args.split(',')[1];
def artifactId = args.split(',')[2];
def baseVersion = args.split(',')[3];
def latestOnly = args.split(',')[4];
def repo = repository.repositoryManager.get(repositoryId);
StorageFacet storageFacet = repo.facet(StorageFacet);
def tx = storageFacet.txSupplier().get();
tx.begin();
def components = tx.findComponents(Query.builder().where('group = ').param(groupId).and('name = ').param(artifactId).build(), [repo]);
def found = components.findAll{it.attributes().child('maven2').get('baseVersion')==baseVersion}.collect{def version = it.attributes().child('maven2').get('version');\"${version}\"};
// found = found.unique().sort();
def latest = found.isEmpty() ? found : found.last();
tx.commit();
def result = latestOnly == 'latest' ? JsonOutput.toJson(latest) : JsonOutput.toJson(found);
return result;"
}
okee,
in fact my needs are :
i want to retrieve last release version and its date of a component by group and artefact.
I definitely leave the groovy API option.
A way i found consists in use of extdirect api. This "rest" api is used by nexus frontend to communicate with backend. No documentation exists.
I do a call to extdirect api to retrieve all versions from a component by group and artefact. I parse the results to get the last version on a release (snapshot and releases).
It's not really good because this call retrieves all the versions on all repositories and could be huge.
Another call to extdirect api to find the release date from the component id of the last release version.
I hope someday nexus publishs official documentation of an useful rest api.
In Trac is possible to set the "owner" for a new ticket to the logged in user ?
I've tried with different value for default_owner in trac.ini but no luck
From trac.edgewall.org authoritative documentation in Trac tickets (wiki):
default_owner: Name of the default owner. If set to the text "< default >" (the default value), the component owner is used.
So this is cannot help you for sure. There are a few more approaches left, depending on you Genshi template knowledge, Python skills etc. Your requirement is not hard to fulfill, but you cannot get it with stock Trac. You'll need to modify the (new) ticket template or add a relatively small plugin (tested with Trac-1.1.1):
import re
from trac.core import Component, implements
from trac.ticket.api import ITicketManipulator
class DefaultTicketOwnerManipulator(Component):
"""Set ticket owner to logged in user, if available."""
implements(ITicketManipulator)
def prepare_ticket(self, req, ticket, fields, actions):
pass
def validate_ticket(self, req, ticket):
if not ticket['owner'] and req.authname:
ticket['owner'] = req.authname
# Optionally report-back manipulation, so require a second POST.
# return [(None, "Owner set to self (%s)" % req.authname)]
return []
Hint: Use commented-out 'return' in pre-last line to not alter owner silently right on save, good for test too.
The following applies to Trac 1.1.3 or later, which is scheduled for release on 1st Jan 2015. It is implemented on the Trac trunk, which we do a pretty good job of keeping stable. The default create workflow actions are:
create = <none> -> new
create.default = 1
create_and_assign = <none> -> assigned
create_and_assign.label = assign
create_and_assign.operations = may_set_owner
create_and_assign.permissions = TICKET_MODIFY
To have the create action assign to the current user, just add:
create.operations = set_owner_to_self
Renaming the action might be appropriate, putting the ticket into either the assigned or accepted state:
create_and_accept = <none> -> accepted
create_and_accept.label = accept
create_and_accept.default = 1
create_and_accept.operations = set_owner_to_self
I need to write a "standalone" script in Python to upload sales taxes to the account_tax table in the database using ONLY the ORM module of OpenERP. What I would like to do is something like the pseudo code below.
Can someone provide me a more details on the following:
1) what sys.path's do I need to set
2) what modules do I need to import before importing the "account" module. Currently when I import the "account" module I get the following error:
AssertionError: The report "report.custom" already exists!
3) What is the proper way to get my database cursor. In the code below I am simply calling psycopg2 directly to get a cursor.
If this approach cannot work, can anyone suggest an alternative approach other than writing XML files to load the data from the OpenERP application itself. This process needs to run outside of the the standard OpenERP application.
PSEUDO CODE:
import sys
# set Python paths to access openerp modules
sys.path.append("./openerp")
sys.path.append("./openerp/addons")
# import OpenERP
import openerp
# import the account addon modules that contains the tables
# to be populated.
import account
# define connection string
conn_string2 = "dbname='test2' user='xyz' password='password'"
# get a db connection
conn = psycopg2.connect(conn_string2)
# conn.cursor() will return a cursor object
cursor = conn.cursor()
# and finally use the ORM to insert data into table.
If you wanna do it via web service then have look at the OpenERP XML-RPC Web services
Example code top work with OpenERP Web Services :
import xmlrpclib
username = 'admin' #the user
pwd = 'admin' #the password of the user
dbname = 'test' #the database
# OpenERP Common login Service proxy object
sock_common = xmlrpclib.ServerProxy ('http://localhost:8069/xmlrpc/common')
uid = sock_common.login(dbname, username, pwd)
#replace localhost with the address of the server
# OpenERP Object manipulation service
sock = xmlrpclib.ServerProxy('http://localhost:8069/xmlrpc/object')
partner = {
'name': 'Fabien Pinckaers',
'lang': 'fr_FR',
}
#calling remote ORM create method to create a record
partner_id = sock.execute(dbname, uid, pwd, 'res.partner', 'create', partner)
More clearly you can also use the OpenERP Client lib
Example Code with client lib :
import openerplib
connection = openerplib.get_connection(hostname="localhost", database="test", \
login="admin", password="admin")
user_model = connection.get_model("res.users")
ids = user_model.search([("login", "=", "admin")])
user_info = user_model.read(ids[0], ["name"])
print user_info["name"]
You see both way are good but when you use the client lib, code is less and easy to understand while using xmlrpc proxy is lower level calls that you will handle
Hope this will help you.
As per my view one must go for XMLRPC or NETSVC services provided by Open ERP for such needs.
You don't need to import accounts module of Open ERP, there are possibilities that other modules have inherited accounts.tax object and had altered its behaviour as per your business needs.
Eventually if you feed data by calling those methods manually without using Open ERP Web service its possible you'll get undesired result / unexpected failures / inconsistent database state.
You can use Erppeek to browse data, but not sure if you can really upload data to DB, personally I use/prefer XMLRPC
Why don't you use the xmlrpc call of openerp.
it will not need to import account or openerp . and even you can have all orm functionality.
You can use python library to access openerp server using xmlrpc service.
Please check https://github.com/OpenERP/openerp-client-lib
It is officially supported by OpenERP SA.
If you want to interacti directly with the DB, you could just import psycopg2 and:
conn = psycopg2.connect(dbname='dbname', user='dbuser', password='dbpassword', host='dbhost')
cur = conn.cursor()
cur.execute('select * from table where id = %d' % table_id)
cur.execute('insert into table(column1, column2) values(%d, %d)' % (value1, value2))
cur.close()
conn.close()
Why you want to fix it like that?! You should create a localization module and define data in XML files. This is the standard way to fix such a problem in OpenERP.
You want to insert sales taxes for which country? Explain more plz.
from openerp.modules.registry import RegistryManager
registry = RegistryManager.get("databasename")
with registry.cursor() as cr:
user = registry.get('res.users').browse(cr, userid, listids)
print user